dlopen 實戰C++ 編譯器對函數名進行 **Name Mangling**(如 _ZN1A2gtEv),導致 C 代碼無法直接鏈接調用 C++ 庫的類成員函數。解決方案:**C++ 內部封裝 C 接口(extern "C"),導出純 C 風格函數,C 端通過 dlopen/dlsym 動態加載調用。**
class A {
public:
A();
virtual ~A();
int gt(); // 設置內部值
int pt(); // 打印內部值
private:
int s;
};
#include#include "head.h" A::A() {} A::~A() {} int A::gt() { s = 10; return 0; } int A::pt() { std::cout << s << std::endl; return 0; }
g++ -shared -o libmy.so firstso.cpp cp libmy.so /usr/lib/
#include#include "head.h" extern "C" { // 關鍵:禁用 Name Mangling,導出純 C 符號 int f(); // 導出函數聲明 int f() { // 實現:內部實例化 C++ 類並調用 A a; a.gt(); a.pt(); return 0; } }
gcc -shared -o sec.so secso.cpp -L. -lmy cp sec.so /usr/lib/
-L.:當前目錄查找庫。-lmy:鏈接 libmy.so。sec.so 導出未修飾的 f 符號。#include#include #define SOFILE "sec.so" int main() { void *handle = dlopen(SOFILE, RTLD_LAZY); if (!handle) { fprintf(stderr, "dlopen failed: %s\n", dlerror()); return 1; } int (*f)() = dlsym(handle, "f"); if (!f) { fprintf(stderr, "dlsym failed: %s\n", dlerror()); dlclose(handle); return 1; } f(); // 調用封裝函數 dlclose(handle); return 0; }
gcc -rdynamic -o myapp test.c -ldl
-rdynamic:將所有符號加入動態符號表(供 dlsym 查找)。-ldl:鏈接 libdl(提供 dlopen/dlsym)。$ ./myapp 10
| 步驟 | 關鍵動作 | 目的 |
|---|---|---|
| C++ 庫 | g++ -shared | 生成 .so,符號經過 Name Mangling |
| 二次封裝 | extern "C" + 內部調用 C++ 類 | 導出無修飾 C 符號,隱藏 C++ 實現細節 |
| 封裝庫編譯 | gcc -shared -lmy | 鏈接 C++ 庫,生成純 C 接口 .so |
| C 端加載 | dlopen + dlsym + 函數指針調用 | 運行時動態綁定,無需編譯期鏈接 C++ 符號 |
若不需運行時動態加載,C 主程序可直接鏈接 sec.so:
gcc -o myapp test.c ./sec.so -L. -lmy # test.c 中直接声明 extern int f(); 並調用 f()
extern "C" 邊界,封裝層需 try-catch 轉為錯誤碼返回。-fvisibility=hidden 並显式標記 __attribute__((visibility("default")))。「C++ 實現 + extern "C" 封裝 C 接口 + C 端 dlopen 調用」是跨語言動態庫互操作的標準模式。掌握 Name Mangling 原理、extern "C" 作用、dlopen/dlsym 流程,即可將 C++ 庫無縫集成到 C 項目中。