6

I need to dynamically link to a library function at runtime in Mac OS X. Following Apple's example, I declare a function pointer and assign it with the result of dlsym(). The following example compiles successfully as a plain C (.c) file. But I need this in a C++ file, and if I compile this example as a C++ file (.cpp), the clang compiler tells me

Cannot initialize a variable of type 'void ()(char *)' with an rvalue of type 'void '

Why does it work in plain 'C', and how can I fix this?

#include <dlfcn.h>

void Test() {
    // Load the library which defines myFunc
    void* lib_handle = dlopen("myLib.dylib", RTLD_LOCAL|RTLD_LAZY);

    // The following line is an error if compiled as C++
    void (*myFunc)(char*) = dlsym(lib_handle, "myFunc");

    myFunc("Hello");

    dlclose(lib_handle) ;
}
Jerry Krinock
  • 4,860
  • 33
  • 39

2 Answers2

7

dlsym returns void*. In POSIX (but not standard C, as James points out) there's an implicit conversion from void* to a pointer-to-function type, so the assignment to myFunc just works. In C++ there is no implicit conversion (because it's not type safe), so you need to tell the compiler that you really mean it by adding a cast:

void (*myFunc)(char*) = (void(*)(char*))dlsym(lib_handle, "myFunc");

(or you can get fancy with a reinterpret_cast).

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • ...or maybe `static_cast`: http://stackoverflow.com/questions/310451/should-i-use-static-cast-or-reinterpret-cast-when-casting-a-void-to-whatever – Nate Kohl May 31 '13 at 18:34
  • 2
    The version of the C standard I have says that a pointer to `void` may be converted to or from a pointer to any _object_ type` (emphasis added). A function is not an object type, and C has never allowed conversions (explicit or implicit) between `void*` and pointers to functions. I'm not sure, but I think a diagnostic is required. (I don't think it's undefined behavior.) And the explicit cast (even `reinterpret_cast`) shouldn't work either. (Most Unix compilers are not conform in this regard, but since it's an extension, each compiler is free to do what it likes.) – James Kanze May 31 '13 at 18:53
  • @JamesKanze is right, C has no implicit conversion from `void*` to any pointer-to-function type. A conforming C compiler must issue a diagnostic (which can be just a warning); a non-conforming C compiler, as most are by default, can do anything it likes. – Keith Thompson May 31 '13 at 19:21
  • Thank you, Pete! I had tried an explicit typecast, but because I wasn't aware of the implicit conversion C-vs-C++ issue, I didn't try hard enough - didn't have enough parentheses! Your line of code works. – Jerry Krinock May 31 '13 at 19:29
0

Because the C compiler is broken. There is no conversion (explicit or implicit) between void* and a pointer to function, neither in C nor in C++.

Posix adds a restriction to C, and requires that void* and pointers to functions have the same size and representation, so that:

void (*myFunc)( char * );
*(void (**myFunc)( char* ))( &myFunc ) = dlsym(...);

will work.

In C++, you might want to use something like:

class GetFunctionHelper;
GetFunctionHelper getFunction( void* dlHandle, std::string const& functionName );

class GetFunctionHelper
{
    void* fromSystem;
    freind GetFunctionHelper getFunction( void* , std::string const& );
    GetFunctionHelper( void* fromSystem ) : fromSystem( fromSystem ) {}
public:
    template <typename Ptr> operator Ptr() const
    {
        return *reinterpret_cast<Ptr const*>( &fromSystem );
    }
};

GetFunctionHelper
getFunction( void* dlHandle, std::string const& functionName )
{
    return GetFunctionHelper( dlsym( dlHandle, functionName.c_str() ) );
}

(with a bit more error checking, of course).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • An explicit conversion (i.e., a cast) from `void*` to a pointer-to-function type does not, as far as I can tell, violate any constraint. But since the standard doesn't define the behavior of such a conversion, its behavior is undefined. POSIX probably does define the behavior, as it's free to do. – Keith Thompson May 31 '13 at 19:22
  • Thank you, @James. I'm not smart enough to remark on the compiler deficiency. Also, your second line doesn't compile. Besides the typo 'u' vs. 'y', there is also apparently a right parentheses missing, but I can't figure out where should go. – Jerry Krinock May 31 '13 at 19:30
  • @KeithThompson I'm not sure about C; in C++, a diagnostic is required. Until very recently, Posix basically said to use my first example, above; when I looked it up this time, however, they said to use a compiler extension. So I don't know (with regards to C). In practice, I've always used something like my C++ version, so the issue never arose (also, I've never actually seen a Unix compiler that enforced this rule). – James Kanze May 31 '13 at 19:34
  • @JerryKrinock I've fixed the C declarations. If there are any problems in the C++, I'll have to get out my actual code (which is at work, and I'm at home recovering from an operation) to verify it. – James Kanze May 31 '13 at 19:36