Function Pointer assignment works in C but not C++
Asked Answered
V

2

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) ;
}
Vitiate answered 31/5, 2013 at 18:25 Comment(0)
C
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).

Cellist answered 31/5, 2013 at 18:31 Comment(4)
...or maybe static_cast: #310951Rollback
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.)Lody
@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.Andromada
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.Vitiate
L
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).

Lody answered 31/5, 2013 at 18:48 Comment(4)
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.Andromada
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.Vitiate
@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).Lody
@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.Lody

© 2022 - 2024 — McMap. All rights reserved.