2013-05-31 5 views
6

Ho bisogno di collegarmi dinamicamente ad una funzione di libreria in fase di esecuzione in Mac OS X. Seguendo Apple's example, dichiaro un puntatore a funzione e lo assegno con il risultato di dlsym(). Il seguente esempio si compila correttamente come un semplice file C (.c). Ma ho bisogno di questo in un file C++, e se compilo questo esempio come C++ di file (cpp), il compilatore clang mi diceL'assegnazione del puntatore funzione funziona in C ma non in C++

Impossibile inizializzare una variabile di tipo 'void () (char *)' con un valore di tipo 'void '

Perché funziona in semplice 'C' e come posso risolvere questo problema?

#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) ; 
} 

risposta

7

dlsym rendimenti void*. In POSIX (ma non C standard, come fa notare James) c'è una conversione implicita da void* a un tipo puntatore-a-funzione, quindi l'assegnazione a myFunc funziona. In C++ non v'è alcuna conversione implicita (perché non è il tipo di sicurezza), quindi è necessario dire al compilatore che realmente significa che con l'aggiunta di un cast:

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

(o si può ottenere fantasia con un reinterpret_cast).

+0

... o forse 'static_cast': http://stackoverflow.com/questions/310451/should-i-use-static-cast-or-reinterpret-cast-when-casting-a-void-to- qualunque sia la –

+2

La versione dello standard C ho detto che un puntatore a 'void' può essere convertito in o da un puntatore a qualsiasi tipo _object_' (enfasi aggiunta). Una funzione non è un tipo di oggetto e C non ha mai consentito conversioni (esplicite o implicite) tra 'void *' e puntatori a funzioni. Non sono sicuro, ma penso che sia necessaria una diagnosi. (Non penso che sia un comportamento indefinito.) E il cast esplicito (anche 'reinterpret_cast') non dovrebbe funzionare neanche. (La maggior parte dei compilatori Unix non sono conformi a questo proposito, ma poiché è un'estensione, ogni compilatore è libero di fare ciò che gli piace.) –

+0

@JamesKanze ha ragione, C non ha alcuna conversione implicita da 'void *' a nessun puntatore-a- tipo di funzione. Un compilatore C conforme deve emettere una diagnostica (che può essere solo un avvertimento); un compilatore C non conforme, come la maggior parte di default, può fare qualsiasi cosa voglia. –

0

Perché il compilatore C è danneggiato. Nessuna conversione (esplicita o implicita) tra void* e un puntatore alla funzione , né in C né in C++.

Posix aggiunge una restrizione a C, e richiede che void* e puntatori a funzioni hanno la stessa dimensione e la rappresentazione, in modo che :

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

funzionerà.

In C++, si potrebbe desiderare di usare qualcosa come:

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())); 
} 

(con un controllo degli errori po 'di più, naturalmente).

+0

Una conversione esplicita (cioè un cast) da 'void *' a un tipo pointer-to-function non, per quanto posso dire, viola alcun vincolo. Ma poiché lo standard non definisce il comportamento di tale conversione, il suo comportamento non è definito. POSIX probabilmente definisce il comportamento, come è libero di fare. –

+0

Grazie, @James. Non sono abbastanza intelligente da sottolineare la mancanza del compilatore. Inoltre, la tua seconda riga non viene compilata. Oltre al typo 'u' vs 'y', anche apparentemente manca una parentesi destra, ma non riesco a capire dove dovrebbe andare. –

+0

@KeithThompson Non sono sicuro di C; in C++, è richiesta una diagnostica. Fino a poco tempo fa, Posix sostanzialmente diceva di usare il mio primo esempio, sopra; stavolta, tuttavia, hanno detto di usare un'estensione del compilatore. Quindi non lo so (per quanto riguarda C). In pratica, ho sempre usato qualcosa come la mia versione C++, quindi il problema non è mai sorto (inoltre, non ho mai visto un compilatore Unix che abbia applicato questa regola). –