Come chiamare la funzione C ++ da C?

Lo so.

Chiamando la funzione C da C ++:

Se la mia applicazione era in C ++ e dovevo chiamare le funzioni da una libreria scritta in C. Allora avrei usato

//main.cpp extern "C" void C_library_function(int x, int y);//prototype C_library_function(2,4);// directly using it. 

Ciò non mancherebbe il nome C_library_function e il linker troverebbe lo stesso nome nei suoi file * .lib di input e il problema è risolto.

Chiamando la funzione C ++ da C ???

Ma qui sto estendendo una grande applicazione che è scritta in C e ho bisogno di usare una libreria che è scritta in C ++. Il nome mangling di C ++ sta causando problemi qui. Linker si lamenta dei simboli non risolti. Beh, non posso usare il compilatore C ++ sul mio progetto C perché questo è quello che rompe molte altre cose. Qual è la via d’uscita?

Dal modo in cui sto usando MSVC

Devi creare un’API C per esporre la funzionalità del tuo codice C ++. Fondamentalmente, dovrai scrivere codice C ++ dichiarato extern “C” e che ha una pura API C (non usando classi, per esempio) che avvolge la libreria C ++. Quindi utilizzi la pura libreria wrapper C che hai creato.

L’API C può opzionalmente seguire uno stile orientato agli oggetti, anche se C non è orientato agli oggetti. Ex:

  // *.h file // ... #ifdef __cplusplus #define EXTERNC extern "C" #else #define EXTERNC #endif typedef void* mylibrary_mytype_t; EXTERNC mylibrary_mytype_t mylibrary_mytype_init(); EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype); EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param); #undef EXTERNC // ... // *.cpp file mylibrary_mytype_t mylibrary_mytype_init() { return new MyType; } void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) { MyType* typed_ptr = static_cast(untyped_ptr); delete typed_ptr; } void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) { MyType* typed_self = static_cast(untyped_self); typed_self->doIt(param); } 

Lo farei nel modo seguente:

(Se si lavora con MSVC, ignorare i comandi di compilazione GCC)

Supponiamo che io abbia una class C ++ denominata AAA , definita nei file aaa.h, aaa.cpp , e che la class AAA abbia un metodo chiamato sayHi (const char * name) , che voglio abilitare per il codice C.

Il codice C ++ della class AAA – Pure C ++, non lo modifico:

// aaa.h

 #ifndef AAA_H #define AAA_H class AAA { public: AAA(); void sayHi(const char *name); }; #endif 

// aaa.cpp

 #include  #include "aaa.h" AAA::AAA() { } void AAA::sayHi(const char *name) { std::cout < < "Hi " << name << std::endl; } 

Compilando questa class come regolarmente eseguita per C ++. Questo codice "non sa" che verrà utilizzato dal codice C. Utilizzando il comando:

 g++ -fpic -shared aaa.cpp -o libaaa.so 

Ora, anche in C ++, creando un connettore C. Definendolo nei file aaa_c_connector.h, aaa_c_connector.cpp . Questo connettore definirà una funzione C, denominata AAA_sayHi ( nome cosnt char *) , che utilizzerà un'istanza di AAA e chiamerà il suo metodo:

// aaa_c_connector.h

 #ifndef AAA_C_CONNECTOR_H #define AAA_C_CONNECTOR_H #ifdef __cplusplus extern "C" { #endif void AAA_sayHi(const char *name); #ifdef __cplusplus } #endif #endif 

// aaa_c_connector.cpp

 #include  #include "aaa_c_connector.h" #include "aaa.h" #ifdef __cplusplus extern "C" { #endif // Inside this "extern C" block, I can define C functions that are able to call C++ code static AAA *AAA_instance = NULL; void lazyAAA() { if (AAA_instance == NULL) { AAA_instance = new AAA(); } } void AAA_sayHi(const char *name) { lazyAAA(); AAA_instance->sayHi(name); } #ifdef __cplusplus } #endif 

Compilandolo, di nuovo, usando un normale comando di compilazione C ++:

 g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so 

Ora ho una libreria condivisa (libaaa_c_connector.so), che implementa la funzione C AAA_sayHi (const char * name) . Ora posso creare un file principale C e compilarlo tutto insieme:

// main.c

 #include "aaa_c_connector.h" int main() { AAA_sayHi("David"); AAA_sayHi("James"); return 0; } 

Compilandolo usando un comando di compilazione C:

 gcc main.c -L. -laaa_c_connector -o c_aaa 

Dovrò impostare LD_LIBRARY_PATH per contenere $ PWD, e se eseguo l'eseguibile ./c_aaa , otterrò l'output che mi aspetto:

 Hi David Hi James 

MODIFICARE:

Su alcune distribuzioni Linux, -laaa e -lstdc++ possono anche essere richieste per l'ultimo comando di compilazione. Grazie a @AlaaM. per l'attenzione

Supponendo che l’API C ++ sia compatibile con C (senza classi, modelli, ecc.), Puoi racchiuderla in extern "C" { ... } , proprio come hai fatto quando vai nell’altro modo.

Se vuoi esporre oggetti e altri contenuti C ++ carini, dovrai scrivere un’API wrapper.

Dovrai scrivere un wrapper per C in C ++ se vuoi farlo. C ++ è retrocompatibile, ma C non è compatibile con le versioni precedenti.

esporta le tue funzioni C ++ come extern “C” (ovvero i simboli di stile C), o usa il formato .def per definire simboli di esportazione non decorati per il linker C ++ quando crea la libreria C ++, quindi il linker C non dovrebbe avere problemi a leggerlo

 #include  ////////////// // C++ code // ////////////// struct A { int i; int j; A() {i=1; j=2; std::cout < < "class A created\n";} void dump() {std::cout << "class A dumped: " << i << ":" << j << std::endl;} ~A() {std::cout << "class A destroyed\n";} }; extern "C" { // this is the C code interface to the class A static void *createA (void) { // create a handle to the A class return (void *)(new A); } static void dumpA (void *thisPtr) { // call A->dump () if (thisPtr != NULL) // I'm an anal retentive programmer { A *classPtr = static_cast(thisPtr); classPtr->dump (); } } static void *deleteA (void *thisPtr) { // destroy the A class if (thisPtr != NULL) { delete (static_cast(thisPtr)); } } } //////////////////////////////////// // this can be compiled as C code // //////////////////////////////////// int main (int argc, char **argv) { void *handle = createA(); dumpA (handle); deleteA (handle); return 0; }