Jump to page: 1 2
Thread overview
foreach range construction bug?!!?!?!?
Jan 06, 2013
Phil Lavoie
Jan 06, 2013
Phil Lavoie
Jan 06, 2013
Era Scarecrow
Jan 06, 2013
Phil Lavoie
Jan 06, 2013
Era Scarecrow
Jan 07, 2013
Tavi Cacina
Jan 07, 2013
Mike Parker
Jan 07, 2013
Phil Lavoie
Jan 07, 2013
Phil Lavoie
Jan 08, 2013
Mike Parker
Jan 09, 2013
Phil Lavoie
January 06, 2013
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

January 06, 2013
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.
January 06, 2013
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.
January 06, 2013
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).
January 06, 2013
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 ) );
      }
    }));
 }
January 07, 2013
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.


January 07, 2013
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.
January 07, 2013
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?
January 07, 2013
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!
January 08, 2013
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/
« First   ‹ Prev
1 2