How to call C++ function from C?

匿名 (未验证) 提交于 2019-12-03 01:28:01

问题:

I know this.

Calling C function from C++:

If my application was in C++ and I had to call functions from a library written in C. Then I would have used

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

This wouldn't mangle the name C_library_function and linker would find the same name in its input *.lib files and problem is solved.

Calling C++ function from C???

But here I'm extending a large application which is written in C and I need to use a library which is written in C++. Name mangling of C++ is causing trouble here. Linker is complaining about the unresolved symbols. Well I cannot use C++ compiler over my C project because thats breaking lot of other stuff. What is the way out?

By the way I'm using MSVC

回答1:

You need to create a C API for exposing the functionality of your C++ code. Basically, you will need to write C++ code that is declared extern "C" and that has a pure C API (not using classes, for example) that wraps the C++ library. Then you use the pure C wrapper library that you've created.

Your C API can optionally follow an object-oriented style, even though C is not object-oriented. 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);  } 


回答2:

I would do it in the following way:

(If working with MSVC, ignore the GCC compilation commands)

Suppose that I have a C++ class named AAA, defined in files aaa.h, aaa.cpp, and that the class AAA has a method named sayHi(const char *name), that I want to enable for C code.

The C++ code of class AAA - Pure C++, I don't modify it:

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

Compiling this class as regularly done for C++. This code "does not know" that it is going to be used by C code. Using the command:

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

Now, also in C++, creating a C connector. Defining it in files aaa_c_connector.h, aaa_c_connector.cpp. This connector is going to define a C function, named AAA_sayHi(cosnt char *name), that will use an instance of AAA and will call its method:

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

Compiling it, again, using a regular C++ compilation command:

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

Now I have a shared library (libaaa_c_connector.so), that implements the C function AAA_sayHi(const char *name). I can now create a C main file and compile it all together:

// main.c

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

Compiling it using a C compilation command:

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

I will need to set LD_LIBRARY_PATH to contain $PWD, and if I run the executable ./c_aaa, I will get the output I expect:

Hi David Hi James 

EDIT:

On some linux distributions, -laaa and -lstdc++ may also be required for the last compilation command. Thanks to @AlaaM. for the attention



回答3:

Assuming the C++ API is C-compatible (no classes, templates, etc.), you can wrap it in extern "C" { ... }, just as you did when going the other way.

If you want to expose objects and other cute C++ stuff, you'll have to write a wrapper API.



回答4:

You will have to write a wrapper for C in C++ if you want to do this. C++ is backwards compatible, but C is not forwards compatible.



回答5:

export your C++ functions as extern "C" (aka C style symbols), or use the .def file format to define undecorated export symbols for the C++ linker when it creates the C++ library, then the C linker should have no troubles reading it



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!