2013-05-31 19 views
6

Muszę dynamicznie łączyć funkcję biblioteki w środowisku wykonawczym w systemie Mac OS X. Po Apple's example deklaruję wskaźnik funkcji i przypisuję go z wynikiem dlsym(). Poniższy przykład kompiluje się pomyślnie jako zwykły plik C (.c). Ale muszę to w ++ pliku C, a jeśli mogę skompilować ten przykład w C++ plików (.cpp), kompilator dzyń mówi miFunkcja Przypisanie wskaźnika działa w języku C, ale nie w C++

Nie można zainicjować zmienną typu „void () (char *)” z wartością typu "void "

Dlaczego działa w prostym "C" i jak mogę to naprawić?

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

Odpowiedz

7

dlsym powraca void*. W POSIX (ale nie w standardowym C, jak wskazuje James) istnieje niejawna konwersja z void* na typ wskaźnika na funkcję, więc zadanie do myFunc po prostu działa. W C++ nie istnieje niejawna konwersja (bo to nie jest typ bezpieczny), więc trzeba poinformować kompilator, że naprawdę oznacza, że ​​dodając Obsada:

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

(lub można uzyskać fantazyjny z reinterpret_cast).

+0

... a może "static_cast": http://stackoverflow.com/questions/310451/----------------------- -internet- niezależnie od tego, –

+2

Wersja standardu C powiedziałem, że wskaźnik do 'void' może zostać przekonwertowany na lub ze wskaźnika na dowolny _object_ type' (podkreślenie dodane). Funkcja nie jest typem obiektu, a C nigdy nie zezwala na konwersje (jawne lub niejawne) między 'void *' a wskaźnikami do funkcji. Nie jestem pewien, ale myślę, że diagnostyka jest wymagana. (Nie sądzę, że jest to niezdefiniowane zachowanie.) A wyraźny rzut (nawet 'reinterpret_cast') również nie powinien działać. (Większość kompilatorów Uniksa nie jest zgodna pod tym względem, ale ponieważ jest to rozszerzenie, każdy kompilator może robić to, co mu się podoba.) –

+0

@JamesKanze ma rację, C nie ma żadnej niejawnej konwersji z 'void *' do żadnego wskaźnika na ... typ funkcji. Odpowiedni kompilator języka C musi wystawić diagnostykę (co może być tylko ostrzeżeniem); niezgodny kompilator C, jak większość jest domyślnie, może zrobić wszystko, co mu się podoba. –

0

Ponieważ kompilator języka C jest uszkodzony. Nie ma konwersji (jawna lub niejawna) między void* a wskaźnikiem na funkcję , ani w C ani w C++.

POSIX dodaje ograniczenie do C i wymaga void* i wskaźniki do funkcji tego samego rozmiaru i reprezentacji, więc że:

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

zadziała.

W C++, możesz użyć czegoś takiego:

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

(o nieco większej kontroli błędów, oczywiście).

+0

Konwersja jawna (tj. Rzutowanie) z 'void *' na typ wskaźnika na funkcję nie pozwala, o ile wiem, naruszyć żadnego ograniczenia. Ale ponieważ standard nie definiuje zachowania takiej konwersji, jej zachowanie jest niezdefiniowane. POSIX prawdopodobnie definiuje zachowanie, ponieważ jest ono darmowe. –

+0

Dziękuję, @ ​​James. Nie jestem wystarczająco inteligentny, aby zwrócić uwagę na niedobór kompilatora. Również twoja druga linia nie kompiluje się. Poza literówką "u" kontra "y", brakuje też nawiasów w prawo, ale nie wiem, gdzie powinien się udać. –

+0

@KeithThompson Nie jestem pewien co do C; w C++ wymagana jest diagnostyka. Do niedawna Posix w zasadzie mówił, że użył mojego pierwszego przykładu powyżej; kiedy tym razem jednak podniosłem tę kwestię, powiedzieli, że używają rozszerzenia kompilatora. Więc nie wiem (w odniesieniu do C). W praktyce zawsze używałem czegoś w rodzaju mojej wersji C++, więc problem nigdy się nie pojawił (również, nigdy nie widziałem kompilatora uniksowego, który wymuszał tę regułę). –

Powiązane problemy