轉載來自:https://zhuanlan.zhihu.com/p/361485807
假設我們有一個C++類 Robot,在文件 robot.h 和 robot.cpp 中定義。Robot 類中有個成員函數 sayHi() 我們想在C程序中調用這個函數。
robot.h
#pragma once #include <string> class Robot { public: Robot(std::string name) : name_(name) {} void sayHi(); private: std::string name_; };
robot.cpp
#include <iostream> #include "robot.h" void Robot::sayHi() { std::cout << "Hi, I am " << name_ << "!\n"; }
我們用編譯C++代碼的方式,使用 g++ 編譯器對這個類進行編譯,此時類 Robot 並不知道自己會被C程序調用。
g++ -fpic -shared robot.cpp -o librobot.so
接下來用C++創建一個C的接口,定義在 robot_c_api.h 和 robot_c_api.cpp 中,這個接口會定義一個C函數 Robot_sayHi(const char *name), 這個函數會創建一個類 Robot 的實例,並調用 Robot 的成員函數 sayHi()。
robot_c_api.h
#pragma once #ifdef __cplusplus extern "C" { #endif void Robot_sayHi(const char *name); #ifdef __cplusplus } #endif
robot_c_api.cpp
#include "robot_c_api.h" #include "robot.h" #ifdef __cplusplus extern "C" { #endif // 因爲我們將使用C++的編譯方式,用g++編譯器來編譯 robot_c_api.cpp 這個文件, // 所以在這個文件中我們可以用C++代碼去定義函數 void Robot_sayHi(const char *name)(在函數中使用C++的類 Robot), // 最後我們用 extern "C" 來告訴g++編譯器,不要對 Robot_sayHi(const char *name) 函數進行name mangling // 這樣最終生成的動態鏈接庫中,函數 Robot_sayHi(const char *name) 將生成 C 編譯器的符號表示。 void Robot_sayHi(const char *name) { Robot robot(name); robot.sayHi(); } #ifdef __cplusplus } #endif
同樣用編譯C++代碼的方式進行編譯
g++ -fpic -shared robot_c_api.cpp -L. -lrobot -o librobot_c_api.so
現在我們有了一個動態鏈接庫 librobot_c_api.so, 這個動態鏈接庫提供了一個C函數 Robot_sayHi(const char *name),我們可以在C程序中調用它了。
main.c
#include "robot_c_api.h" int main() { Robot_sayHi("Alice"); Robot_sayHi("Bob"); return 0; }
使用C程序的編譯方式,用 gcc 對 main.c 進行編譯
gcc main.c -L. -lrobot_c_api
可以看到 gcc 編譯出的函數符號和 librobot_capi.so中 g++ 編譯器編譯出的函數符號一致。這樣最終在我們的C程序中可以正確的鏈接到動態庫中的Robot_sayHi(const char *name) 函數。