views:

949

answers:

2

Hi all,
I have a void pointer returned by dlsym(), I want to call the function pointed by the void pointer. So I do a type conversion by casting:

void *gptr = dlsym(some symbol..) ;
typedef void (*fptr)();
fptr my_fptr = static_cast<fptr>(gptr) ;

I hav also tried reinterpret_cast but no luck, although the C cast operator seems to work..

Thanx,

+6  A: 

Converting a void* to a function pointer directly is not allowed (should not compile using any of the casts) in C++98/03. It is conditionally supported in C++0x (an implementation may choose to define the behavior and if it does define it then it must do what the standard says it should d). A void*, as defined by the C++98/03 standard, was meant to point to objects and not to contain function pointers or member pointers.

Knowing that what you are doing is heavily implementation dependent, here is one option that should compile and work (assuming 32 bit pointers, use long long for 64 bit) on most platforms, even though it is clearly undefined behavior according to the standard:

void *gptr = dlsym(some symbol..) ;
typedef void (*fptr)();
fptr my_fptr = reinterpret_cast<fptr>(reinterpret_cast<long>(gptr)) ;

And here is another option that should compile and work, but carries the same caveats with it as the above:

fptr my_ptr = 0;
*reinterpret_cast<void**>(&my_ptr) = gptr;
// Or in Slow motion ... 
void (*(*object_ptr))() = &my_ptr;  // get the address which is an object pointer

// convert it to void** which is also an object pointer
void ** ppv = reinterpret_cast<void**>(object_ptr);  
*ppv = gptr;  // assign the address in the memory cell named by 'gptr' 
              // to the memory cell that is named by 'my_ptr' which is
              // the same memory cell that is pointed to 
              // by the memory cell that is named by 'ppv'

Since the above code is a little cryptic, the following explanation might help: It essentially exploits the fact that the address of the function pointer is an object pointer [void (*(*object_ptr))()] - so we can use reinterpret_cast to convert it to any other object pointer: such as void**. We can then follow the address back (by dereferencing the void**) to the actual function pointer and store the value of the gptr there. yuk - by no means well-defined code - but it should do what you expect it to do on most implementations.

Faisal Vali
I expect this is it - the C++ casting is being standard-compliant, the C casting is being backward compatible with the requirements of the POSIX shared library calls.
Daniel Earwicker
Even the C style explicit cast is not required to do the conversion or even compile - the C style cast is defined in terms of the other casts (with one minor additional feature regarding base class accessibility).
Faisal Vali
As a side note, a better choice of a type to use in an intermediate cast might be `size_t` - it is usually large enough to fit a pointer on any platform, even though that isn't guaranteed either. Better yet, use `<stdint.h>`/`<cstdint>` header and `intptr_t` typedef in it where it's available (C99, C++TR1, C++0x).
Pavel Minaev
@Pavel - thanks for the intptr_t info :)
Faisal Vali
The "conditionally supported" wording was in fact invented with dlsym() behavior in mind - around 2001 it was noticed that real C++ compilers for POSIXy systems all accepted this cast.
MSalters
@MSalters - thanks for the background :) - here's the link to the DR that might have started it all: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
Faisal Vali
A: 

This may help you. It prints "Hello".

#include <iostream>

void hello()
{
  std::cout << "Hello" << std::endl;
}

int main() {
  typedef void (*fptr)();
  fptr gptr = (fptr) (void *) &hello;
  gptr();
}

OR you can do:

fptr gptr = reinterpret_cast<fptr>( (void *) &hello);

where &hello is replaced by the dlsym command.

Ryan Oberoi
I'm gonna be *amazed* if that's helpful!
Daniel Earwicker
The reason that's working is because you're not going via a `void *` pointer.
Daniel Earwicker