Como chamar a function C ++ de C?

Eu sei isso.

Chamando a function C do C ++:

Se meu aplicativo estivesse em C ++ e eu tivesse que chamar funções de uma biblioteca escrita em C. Então eu teria usado

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

Isso não iria mangle o nome C_library_function e o linker encontraria o mesmo nome em seus arquivos * .lib de input e o problema estaria resolvido.

Chamando a function C ++ de C ???

Mas aqui estou estendendo um aplicativo grande que está escrito em C e preciso usar uma biblioteca que esteja escrita em C ++. O mangling de nome do C ++ está causando problemas aqui. O vinculador está reclamando dos símbolos não resolvidos. Bem, eu não posso usar o compilador C ++ sobre o meu projeto C porque isso está quebrando muitas outras coisas. Qual é a saída?

Pela maneira que eu estou usando o MSVC

Você precisa criar uma API C para expor a funcionalidade do seu código C ++. Basicamente, você precisará escrever código C ++ que seja declarado extern “C” e que tenha uma API C pura (não usando classs, por exemplo) que envolva a biblioteca C ++. Então você usa a biblioteca pura de wrapper C que você criou.

Sua API C pode, opcionalmente, seguir um estilo orientado a objects, mesmo que C não seja orientado a objects. 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); } 

Eu faria da seguinte maneira:

(Se estiver trabalhando com o MSVC, ignore os comandos de compilation do GCC)

Suponha que eu tenha uma class C ++ chamada AAA , definida nos arquivos aaa.h, aaa.cpp , e que a class AAA tenha um método chamado sayHi (const char * name) , que eu quero habilitar para o código C.

O código C ++ da class AAA – Pure C ++, não 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 esta class como regularmente feito para C ++. Este código "não sabe" que vai ser usado pelo código C. Usando o comando:

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

Agora, também em C ++, criando um conector C. Definindo nos arquivos aaa_c_connector.h, aaa_c_connector.cpp . Este conector irá definir uma function C, denominada AAA_sayHi (cosnt char * name) , que usará uma instância de AAA e chamará seu método:

// 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 

Compilando-o, novamente, usando um comando normal de compilation C ++:

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

Agora eu tenho uma biblioteca compartilhada (libaaa_c_connector.so), que implementa a function C AAA_sayHi (const char * name) . Agora posso criar um arquivo principal C e compilá-lo todos juntos:

// main.c

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

Compilando-o usando um comando de compilation C:

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

Eu precisarei definir LD_LIBRARY_PATH para conter $ PWD e, se eu executar o executável ./c_aaa , obterei a saída esperada:

 Hi David Hi James 

EDITAR:

Em algumas distribuições linux, -laaa e -lstdc++ também podem ser necessários para o último comando de compilation. Graças a @AlaaM. para a sua atenção

Supondo que a API do C ++ seja compatível com C (sem classs, modelos, etc.), você pode envolvê-la no extern "C" { ... } , da mesma forma que você fez quando foi para o outro lado.

Se você quiser expor objects e outras coisas fofas de C ++, você terá que escrever uma API de wrapper.

Você terá que escrever um wrapper para C em C ++ se você quiser fazer isso. C ++ é compatível com versões anteriores, mas C não é compatível com versões futuras.

exporte suas funções C ++ como extern “C” (também conhecido como C style symbols), ou use o formato de arquivo .def para definir símbolos não exportados de exportação para o C ++ linker ao criar a biblioteca C ++, então o C linker não deve ter problemas ao lê-lo

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