55

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 have also tried reinterpret_cast but no luck, although the C cast operator seems to work..

1
  • I think that conversion pointer to function into void* was originally a bad idea. Is that problem to return pointer to function from dlsym? Commented Jul 8, 2009 at 7:09

9 Answers 9

66

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 do. 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...

// get the address which is an object pointer
void (**object_ptr)() = &my_ptr;  

// convert it to void** which is also an object pointer
void ** ppv = reinterpret_cast<void**>(object_ptr);

// 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'
*ppv = gptr;  

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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.
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).
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 - thanks for the background :) - here's the link to the DR that might have started it all: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
If you want a more portable way, you can always write a wrapper around that specific dlsym in C which does return a function pointer and call that wrapper from C++.
|
20

Note that C++11 allows such a conversion and from gcc 4.9 and above this conversion does not generate a warning: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869.

See SO discussions:

Comments

2

Well, if you know what the argument list is, it's extremely simple to just c-cast it. As stated above, it has undefined behavior, but I've been using this in my own events handler for a pet project and it seems to work just fine on MSVC.

I can cast the same void* to _beginthread_proc_type to start a thread with _beginthread(), and that also doesn't seem to cause any issues (though I don't really know what the consequences of sending arguments to a function that doesn't require any, or not sending arguments to functions that do require arguments will do, that does seem to at least call the function/start the thread in my limited testing):

void somefunction(){
    std::cout <<"hi"<<std::endl;
}

void* function = (void*)&somefunction;
((void(__cdecl*)(void))(function)) ();

_beginthread((_beginthread_proc_type)function, 0, NULL);

I know the community has developed a hatred for macros, but I use a macro for that function call in my events handler:

#define call_voidstar_function(fc)     ((void(__cdecl*)(void))(fc)) ()

1 Comment

For me, the macro disdain comes from the fact that you can't put them in a namespace.
1

This compiles in Visual Studio without using reinterpret cast:

void *ptr;
int (*func)(void) = (int(*)(void))ptr;
int num = func();

3 Comments

Might compile, but will result in undefined behavior (as described by the C specs.)
Is that really without reinterpret_cast? Which cast will the compiler choose?
You're doing a c-style cast, which is effectively a reinterpret cast in this case.
1

I found this (a bit ugly) solution. gcc with maximum warning level does not complain. This example calls dlsym() (that returns a void*) and returns the result in a function pointer.

typedef void (*FUNPTR)();

FUNPTR fun_dlsym(void* handle, const char* name) {
    union {
        void* ptr;
        FUNPTR fptr;
    } u;
    u.ptr = dlsym(handle, name);
    return u.fptr;
}

1 Comment

That works if the compilation unit is C but not C++11 & later as it's undefined behaviour: stackoverflow.com/questions/11373203/…
1

You can cast dlsym to a function that returns the required pointer and then call it like this:

typedef void (*fptr)();
fptr my_fptr = reinterpret_cast<fptr (*)(void*, const char*)>(dlsym)(RTLD_DEFAULT, name);

PS. Casting a function pointer to a different function pointer and then calling it is undefined behavior (see point 7 in https://en.cppreference.com/w/cpp/language/reinterpret_cast) so it is better to cast the result of dlsym to uintptr_t and then to the required type:

fptr my_fptr = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(dlsym(RTLD_DEFAULT, name)));

Comments

0

One might use the following technique:

int (*fn)(int);
*(void **)(&fn) = dlsym(lib1, "function");
int result = (*fn)(3);

Or

fn = (int (*)(int))dlsym(lib1, "function");

Compiled with:

g++ -Wall -pedantic -std=c++11

Comments

0

typeof works for me

//fn_ptr = void_ptr; // works in C, fails in C++

fn_ptr = (typeof(fn_ptr))(void_ptr);

im surprised that C++ does not do this automatically...

#include <stdio.h>
//#include <cstdint> // uintptr_t

int fn(int x) {
  return x + 1;
}

int main() {

  void *void_ptr = (void*)&fn;

  typedef int (*fn_ptr_t)(int);

#ifndef __cplusplus
  // C
  fn_ptr_t fn_ptr = NULL;
#else
  // C++
  fn_ptr_t fn_ptr = nullptr;
#endif

#ifndef __cplusplus
  // C
  // this just works in C
  fn_ptr = void_ptr;
#else
  // C++
  // error: invalid conversion from ‘void*’ to ‘fn_ptr_t’ {aka ‘int (*)(int)’}
  //fn_ptr = void_ptr;

  // this works, but its verbose
  //fn_ptr = reinterpret_cast<fn_ptr_t>(reinterpret_cast<uintptr_t>(void_ptr));

  // cast with typeof. im surprised that C++ does not do this automatically
  fn_ptr = (typeof(fn_ptr))(void_ptr);
#endif

  // should return 2
  return fn_ptr(1);
}

Comments

-3

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.

3 Comments

I'm gonna be amazed if that's helpful!
The reason that's working is because you're not going via a void * pointer.
After the edit he is, right? And the code seems to work. (Though I'm no expert, so maybe this happens to work, but is actually undefined?)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.