3

I've been working on creating an OpenGL function loading library which will help me call OpenGL functions when I need them.

I have a getProcAddress function which uses glX.

void* getProcAddress(const char *name)
{
    auto pr = reinterpret_cast<void*>(glXGetProcAddress(
            reinterpret_cast<const unsigned char*>(name)));

    return pr;
}

This returns the address of an OpenGL function. I get weird compiler errors if I don't use reinterpret_casts so that's why they're there.

I then define a gl* function prototype in a header file:

typedef void _GLACTIVETEXTURE(GLenum texture);

Where GLenum is defined in another header file as an enum. I then declare the function pointer in a class:

_GLACTIVETEXTURE glActiveTexture;

And then in a function called init I do:

void GLFunctions::init()
{
    glActiveTexture = (_GLACTIVETEXTURE)getProcAddress("glActiveTexture");
}

The getProcAddress function compiles by itself fine, but the above line of code won't compile. GCC throws this compiler error:

error: invalid cast to function type ‘_GLACTIVETEXTURE {aka void(GLenum)}’

And I don't know how to deal with this kind of compiler error. It makes no sense because this is a function pointer, not a function itself unless I use (). I'm not really sure what the problem here is; whether it's from my side or GCC. It's not clear. I've tried fiddling around with the pointers and voids, but all in vain and the same error message comes up. Does anyone know what's going on here and how I can properly call OpenGL functions?

Poriferous
  • 1,566
  • 4
  • 20
  • 33

2 Answers2

5

@nshct already gave you an explanation why compilers complain about your code. What that answer didn't address is, why your reinterpret_cast was necessary in the first place. The reason for that is, that function pointers are something different than regular pointers and it's perfectly possible that sizeof(void*) != sizeof(void(*)(void)), i.e. function pointers may have an entirely different range of values and alignment rules than regular pointers. The man page for dlsym addresses this in detail:

http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html

Rationale

The ISO C standard does not require that pointers to functions can be cast back and forth to pointers to data. Indeed, the ISO C standard does not require that an object of type void * can hold a pointer to a function. Implementations supporting the XSI extension, however, do require that an object of type void * can hold a pointer to a function. The result of converting a pointer to a function into a pointer to another data type (except void *) is still undefined, however. Note that compilers conforming to the ISO C standard are required to generate a warning if a conversion from a void * pointer to a function pointer is attempted as in:

fptr = (int (*)(int))dlsym(handle, "my_function");

Due to the problem noted here, a future version may either add a new function to return function pointers, or the current interface may be deprecated in favor of two new functions: one that returns data pointers and the other that returns function pointers.

Because of that when using dlsym a special way of casting must be used, namely some lvalue casting trickery.

void    *handle;
int     (*fptr)(int);

/* open the needed object */
handle = dlopen("/usr/home/me/libfoo.so", RTLD_LOCAL | RTLD_LAZY);

/* find the address of function and data objects */
*(void **)(&fptr) = dlsym(handle, "my_function");

The definition of glXGetProcAddress is aware of this and has explicitly been written so that it returns a function pointer. But because function pointers are different from regular pointers you must not cast a function pointer into a regular pointer. Instead you must cast either to the target function pointer type, or cast the function pointer variable lvalue in the assignment (as with dlsym) to match the rvalue of glXGetProcAddress.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Are there any real systems where function pointers and regular pointers actually are different? I don't doubt the theory, just wondering if it's a real life concern. – Reto Koradi Jul 30 '16 at 17:00
  • @RetoKoradi: Just about every Harvard Architecture processor. Most DSPs are Harvard Architectures, as are many µCs. – datenwolf Jul 30 '16 at 20:47
  • @RetoKoradi: For example have a look at the Analog Devices ADSP-219x DSPs: These use a 16 bit addressable data memory bus (i.e. 16 bit address space for data) but the program memory bus has 24 addressable bits (24 bit address space). So if you'd cast a function pointer into a regular data pointer it would be truncated. – datenwolf Jul 30 '16 at 21:02
  • 1
    Systems that have either dlsym [posix/unix] or GetProcAddress [Windows] are bound to have uniform data and code pointers. No such thing can be said about the other systems. – Zsigmond Lőrinczy Jul 31 '16 at 17:41
  • 1
    Also I think this 'lvalue casting trickery' would cause problems if data-pointers and code-pointers were of different size (eg: if data pointers are 32 bit long and code pointers are 16 bit long, you over-write 16 bits after 'fptr') – Lorinczy Zsigmond Aug 02 '16 at 14:49
3

Your typedef is wrong. You are creating an alias to a function type, not to a function pointer. This is how it is done correctly:

typedef void (*_GLACTIVETEXTURE)(GLenum texture);
nshct
  • 1,197
  • 9
  • 29