將 C++ 類別封裝於 extern "C" 函數中,編譯為 .so(Linux)或 .dll(Windows),C 程式透過 dlopen/dlsym(或 LoadLibrary/GetProcAddress)動態載入並呼叫。
// vector.cpp (C++)
extern "C" {
void vector_add_string(char* str);
void vector_list_strings(char* buff);
std::vector<std::string> _stringList;
void vector_add_string(char* str) { _stringList.push_back(str); }
void vector_list_strings(char* buff) { /* 複製字串到 buff */ }
}
// 編譯:g++ -shared -o vector.so vector.cpp
// test.c (C)
void (*add)(char*); void (*list)(char*);
void* dp = dlopen("./vector.so", RTLD_LAZY);
add = dlsym(dp, "vector_add_string");
list = dlsym(dp, "vector_list_strings");
直接在 C++ 原始碼中以 extern "C" 宣告非成員函數,提供 C 可呼叫的介面。若需呼叫成員函數(含虛擬函數),需撰寫包裝函數:
// C++ 程式碼
class C { virtual double f(int); };
extern "C" double call_C_f(C* p, int i) { return p->f(i); }
// C 程式碼
double call_C_f(struct C* p, int i); // 前向宣告
void ccc(struct C* p, int i) { double d = call_C_f(p, i); }
多載函數需提供不同名稱的包裝函數(如 f_i, f_d)。
在共用標頭檔中定義對應的 C 結構體,確保記憶體配置相容:
// c.h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct { int i; int j; } struct1; // 對應 class1 的資料成員
#ifdef __cplusplus
}
#endif
C++ 端以 reinterpret_cast 或 C 風格轉型將 struct1* 轉為 class1* 呼叫成員函數。
g++ 編譯,C 原始檔用 gcc 編譯。-lstdc++)。Bjarne Stroustrup FAQ:Calling C++ from C