www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - foreach range construction bug?!!?!?!?

reply "Phil Lavoie" <maidenphil hotmail.com> writes:
Hi all,

I am very close to posting a bug report, however I would like 
some insights first. See the code below:

module newstructerror;

alias int GLint;
alias uint GLenum;

enum {
   GL_EXTENSIONS
}

/* Errors */
enum {
  GL_NO_ERROR                             = 0x0,
  GL_INVALID_VALUE                        = 0x0501,
  GL_INVALID_ENUM                         = 0x0500,
  GL_INVALID_OPERATION                    = 0x0502,
  GL_STACK_OVERFLOW                       = 0x0503,
  GL_STACK_UNDERFLOW                      = 0x0504,
  GL_OUT_OF_MEMORY                        = 0x0505,
}

version( Defined ) {
   extern( C ) nothrow {
     void glGetIntegerv( GLenum p, GLint * v ) {
       //Do nothing;
     }

     GLenum glGetError() {
       return GL_INVALID_OPERATION;
     }
   }
} else {
   pragma( lib, "opengl32.lib" );
   extern( C ) nothrow {
     void glGetIntegerv( GLenum p, GLint * v );
     GLenum glGetError();
   }
}

import std.stdio;

void main( string[] args ) {
   GLint noExt = -1;
   glGetIntegerv( GL_EXTENSIONS, &noExt );
   writeln( "Number of extensions: ", noExt );

   version( Working ) {
     //Works with both.
     auto errors = GLErrors();
     foreach( GLenum err; errors ) {
       writeln( glError( err ) );
     }
   } else {
     //Crashes in non defined version.
     foreach( GLenum err; GLErrors() ) {
       writeln( glError( err ) );
     }
   }

}

string glError( GLenum errCode ) {
   switch( errCode ) {
   case GL_NO_ERROR:
     return "no error";
   case GL_INVALID_VALUE:
     return "invalid value";
   case GL_INVALID_ENUM:
     return "invalid enum";
   case GL_INVALID_OPERATION:
     return "invalid operation";
   case GL_STACK_OVERFLOW:
     return "stack overflow";
   case GL_STACK_UNDERFLOW:
     return "stack underflow";
   case GL_OUT_OF_MEMORY:
     return "out of memory";
   default:
     return "unknown error";
   }
}

struct GLErrors {
   private GLenum _current;
public:
    property bool empty() {
     _current = glGetError();
     return ( _current == GL_NO_ERROR );
   }
    property GLenum front() {
     return _current;
   }
   void popFront() { ; }
}

Now here is what is really troubling me, the non defined version 
(using the import library) crashes when the range constructor is 
called inside the foreach statement, which is weird by itself. In 
addition, in DOES NOT crash when the functions are artifically 
defined. Anyone seen that before? Or maybe I am doing something 
wrong?

This is the program output when you don't define any versions:
Number of extensions: -1
object.Error: Access Violation
----------------
0x0040DAD8
0x0040D963
0x7799B459 in LdrRemoveLoadAsDataTable
0x7799B42B in LdrRemoveLoadAsDataTable
0x77950133 in KiUserExceptionDispatcher
0x00403A20
0x00403A56
0x00403659
0x00402830
0x751A33AA in BaseThreadInitThunk
0x77979EF2 in RtlInitializeExceptionChain
0x77979EC5 in RtlInitializeExceptionChain
----------------

Defining Working or Defined will produce an infinite loop (or 
maybe not).

Thanks for your time!
Phil
Jan 06 2013
next sibling parent "Phil Lavoie" <maidenphil hotmail.com> writes:
Oh, BTW, I am aware that GL_EXTENSIONS (whose value is missing 
but it's actually this: 0x1F03) is not a valid enum for the 
request, I am EXPECTING an error from opengl.
Jan 06 2013
prev sibling next sibling parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Sunday, 6 January 2013 at 18:59:36 UTC, Phil Lavoie wrote:
 Hi all,

 I am very close to posting a bug report, however I would like 
 some insights first. See the code below:
   version( Working ) {
     //Works with both.
     auto errors = GLErrors();
     foreach( GLenum err; errors ) {
       writeln( glError( err ) );
     }
   } else {
     //Crashes in non defined version.
     foreach( GLenum err; GLErrors() ) {
       writeln( glError( err ) );
     }
   }
 Now here is what is really troubling me, the non defined 
 version (using the import library) crashes when the range 
 constructor is called inside the foreach statement, which is 
 weird by itself. In addition, in DOES NOT crash when the 
 functions are artificially defined. Anyone seen that before? Or 
 maybe I am doing something wrong?

 This is the program output when you don't define any versions:
 Number of extensions: -1
 object.Error: Access Violation

 Defining Working or Defined will produce an infinite loop (or 
 maybe not).
 Oh, BTW, I am aware that GL_EXTENSIONS (whose value is missing
but it's actually this: 0x1F03) is not a valid enum for the request, I am EXPECTING an error from opengl. I doubt it's the constructor, as it just copies it's .init (or zeroizes depending on what's better). The segfault would be from the first call of .empty . Now this is a few shots in the dark and going from memory, but as I recall compiled code (but not linked) won't have any calls actually go where they are suppose to; The linker resolves that during the linking step. So either those addresses aren't getting linked and resolved when it gets compiled, or your opengl32.lib is trying to call something that doesn't exist thereby throwing SegFault. Is the lib compiled with the same compiler (set) as your current source (gdc/gcc for example)? Since it's not a dll (or .so file), perhaps something incompatible is going on in the background. Another thought as I write is are you using gnu/linux? If you are, are you using Xwindows (probably...)? Xwindows as I recall is server/daemon based so if the service(s) aren't running perhaps the openGL driver/service may not even be active, or requires root access to connect to it.
Jan 06 2013
parent reply "Phil Lavoie" <maidenphil hotmail.com> writes:
I am currently working on Windows. The opengl32.lib is an import 
library created using implib /noi /system opengl32.lib 
C:\Windows\System32\opengl32.dll
This all links correctly, otherwise I would get en error during 
compile + link. I run the command rdmd --force thismodule.d. The 
library is located on a dev folder which is located in sc.ini. 
Also, optlink.cfg contains the /scanlib directive. I don't think 
this is strictly an issue about compilation/link, since moving 
the constructor away seems to work correctly (-version=Working).
Jan 06 2013
parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Sunday, 6 January 2013 at 23:16:13 UTC, Phil Lavoie wrote:
 since moving the constructor away seems to work correctly 
 (-version=Working).
Mmmm... But no construction happens, empty parameters equals default .init. Perhaps it's a bug related to confusing lvalue/rvalue and it's trying to access a temporary variable outside it's scope (so it has garbage data); but even with garbage data... property bool empty() { writeln("Empty!"); _current = glGetError(); return ( _current == GL_NO_ERROR ); } property GLenum front() { writeln("Front!"); return _current; } void popFront() { writeln("PopFront!"); } } If it dies before printing 'Empty!' or 'Front!' then it could be a temporary reference variable bug (I really doubt ctor, although a garbage pointer would cause the problem too). If it dies instead at glGetError it likely is a hidden global variable is getting messed up/not initialized (though seems unlikely). Decompile the assembly code around the foreach loop and the working version, that will give us a definitive explanation of what's really going on. Mmmm I wonder... __traits(compiles) allowed me to see a hidden added 'void* this;' pointer in a union; Perhaps... else { //Crashes in non defined version. assert(__traits(compiles, { foreach( GLenum err; GLErrors() ) { const int a; a++; // breaking on purpose, may show full unrolling // of compiler re-written code writeln( glError( err ) ); } })); }
Jan 06 2013
prev sibling parent reply Tavi Cacina <octavian.cacina outlook.com> writes:
Am 06.01.2013 19:59, schrieb Phil Lavoie:
 Now here is what is really troubling me, the non defined version (using
 the import library) crashes when the range constructor is called inside
 the foreach statement, which is weird by itself. In addition, in DOES
 NOT crash when the functions are artifically defined. Anyone seen that
 before? Or maybe I am doing something wrong?
I had once some crashes when calling code in an external dll (on Windows). The problem went away after I've changed the declaration from extern(C) to extern(System). It may be worth trying. Tavi.
Jan 06 2013
parent reply "Mike Parker" <aldacron gmail.com> writes:
On Monday, 7 January 2013 at 07:57:39 UTC, Tavi Cacina wrote:
 Am 06.01.2013 19:59, schrieb Phil Lavoie:
 Now here is what is really troubling me, the non defined 
 version (using
 the import library) crashes when the range constructor is 
 called inside
 the foreach statement, which is weird by itself. In addition, 
 in DOES
 NOT crash when the functions are artifically defined. Anyone 
 seen that
 before? Or maybe I am doing something wrong?
I had once some crashes when calling code in an external dll (on Windows). The problem went away after I've changed the declaration from extern(C) to extern(System). It may be worth trying. Tavi.
As a general solution, that's not good advice, but in this case it's probably so. The OpenGL functions on Windows are declared with the stdcall calling convention, and as cdecl on other platforms. In D code, that means they need to be declared as extern(Windows) on Windows and extern(C) everywhere else. extern(System) is the convenient way to do that, as the compiler will do the right thing on each platform. Always look at the C headers to understand how the functions are declared before declaring them in D to make sure you've got the correct calling convention.
Jan 07 2013
parent reply "Phil Lavoie" <maidenphil hotmail.com> writes:
On Monday, 7 January 2013 at 09:16:00 UTC, Mike Parker wrote:
 On Monday, 7 January 2013 at 07:57:39 UTC, Tavi Cacina wrote:
 Am 06.01.2013 19:59, schrieb Phil Lavoie:
 Now here is what is really troubling me, the non defined 
 version (using
 the import library) crashes when the range constructor is 
 called inside
 the foreach statement, which is weird by itself. In addition, 
 in DOES
 NOT crash when the functions are artifically defined. Anyone 
 seen that
 before? Or maybe I am doing something wrong?
I had once some crashes when calling code in an external dll (on Windows). The problem went away after I've changed the declaration from extern(C) to extern(System). It may be worth trying. Tavi.
As a general solution, that's not good advice, but in this case it's probably so. The OpenGL functions on Windows are declared with the stdcall calling convention, and as cdecl on other platforms. In D code, that means they need to be declared as extern(Windows) on Windows and extern(C) everywhere else. extern(System) is the convenient way to do that, as the compiler will do the right thing on each platform. Always look at the C headers to understand how the functions are declared before declaring them in D to make sure you've got the correct calling convention.
Yeah you're right, though I'm having difficulty making the import library. implib /noi /system will generate "_name" function names, but extern windows expect them to be "_name someInt". Removing system only seems to remove the prepended "_". Any ideas?
Jan 07 2013
parent reply "Phil Lavoie" <maidenphil hotmail.com> writes:
I have looked around the a while and I can say that the invalid 
calling convention is the most probable cause, though I am still 
wondering what is the best way to create en import library from a 
.dll that will export symbols matching to extern( Windows ) 
"_... int". Implib from digital mars does not seem to have that 
option... so I am looking around, will let you know what happens!
Jan 07 2013
parent reply "Mike Parker" <aldacron gmail.com> writes:
On Monday, 7 January 2013 at 18:11:14 UTC, Phil Lavoie wrote:
 I have looked around the a while and I can say that the invalid 
 calling convention is the most probable cause, though I am 
 still wondering what is the best way to create en import 
 library from a .dll that will export symbols matching to 
 extern( Windows ) "_... int". Implib from digital mars does not 
 seem to have that option... so I am looking around, will let 
 you know what happens!
If you aren't doing this as a learning exercise, you could save yourself a headache and just use Derelict's OpenGL binding[1]. Then you don't have to worry about import libraries or object file formats. Just DerelictGL3.load() and away you go. [1] https://github.com/aldacron/Derelict3/
Jan 07 2013
parent "Phil Lavoie" <maidenphil hotmail.com> writes:
On Tuesday, 8 January 2013 at 04:49:42 UTC, Mike Parker wrote:
 On Monday, 7 January 2013 at 18:11:14 UTC, Phil Lavoie wrote:
 I have looked around the a while and I can say that the 
 invalid calling convention is the most probable cause, though 
 I am still wondering what is the best way to create en import 
 library from a .dll that will export symbols matching to 
 extern( Windows ) "_... int". Implib from digital mars does 
 not seem to have that option... so I am looking around, will 
 let you know what happens!
If you aren't doing this as a learning exercise, you could save yourself a headache and just use Derelict's OpenGL binding[1]. Then you don't have to worry about import libraries or object file formats. Just DerelictGL3.load() and away you go. [1] https://github.com/aldacron/Derelict3/
Yeah I kinda am :). Though I am interested in the solution you propose so I might have a look. Thanks! Phil
Jan 09 2013