將C++程序打包成SO庫并調(diào)用的詳細流程
一、SO 庫的基本概念
- 動態(tài)鏈接庫(SO):在程序運行時被加載,多個程序可共享同一 SO 庫,節(jié)省內(nèi)存
- 優(yōu)點:減小可執(zhí)行文件體積、便于模塊更新(無需重新編譯主程序)
- 核心:通過
extern "C"解決 C++ 名稱修飾問題,確保庫函數(shù)能被正確識別
二、創(chuàng)建 SO 庫的步驟
1. 準備源文件
假設(shè)有一個簡單的數(shù)學(xué)運算模塊,包含頭文件和實現(xiàn)文件:
math_utils.h(頭文件)
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
// 用extern "C"包裹,避免C++名稱修飾
#ifdef __cplusplus
extern "C" {
#endif
// 加法
int add(int a, int b);
// 乘法
int multiply(int a, int b);
#ifdef __cplusplus
}
#endif
#endif // MATH_UTILS_Hmath_utils.cpp(實現(xiàn)文件)
#include "math_utils.h"
// 加法實現(xiàn)
int add(int a, int b) {
return a + b;
}
// 乘法實現(xiàn)
int multiply(int a, int b) {
return a * b;
}2. 編譯生成 SO 庫
使用g++編譯,關(guān)鍵參數(shù):
-fPIC:生成位置無關(guān)代碼(必選,確保庫可被多個程序共享)-shared:指定生成動態(tài)鏈接庫-o:指定輸出文件名(慣例以lib開頭,.so結(jié)尾)
編譯命令:
g++ -fPIC -shared -o libmath_utils.so math_utils.cpp
執(zhí)行后會生成libmath_utils.so文件
三、調(diào)用 SO 庫的兩種方式
方式 1:編譯時鏈接(靜態(tài)加載)
1. 編寫調(diào)用程序main.cpp
#include <iostream>
#include "math_utils.h"
int main() {
int a = 10, b = 20;
std::cout << "a + b = " << add(a, b) << std::endl;
std::cout << "a * b = " << multiply(a, b) << std::endl;
return 0;
}2. 編譯調(diào)用程序
編譯時需要指定:
- 庫所在路徑(
-L.表示當前目錄) - 庫名稱(
-lmath_utils,省略lib前綴和.so后綴)
編譯命令:
g++ main.cpp -o main -L. -lmath_utils
3. 運行程序
直接運行生成的可執(zhí)行文件:
./main
可能的錯誤:
若提示error while loading shared libraries: libmath_utils.so: cannot open shared object file: No such file or directory,解決方法:
- 臨時方案:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH - 永久方案:將 SO 庫復(fù)制到
/usr/lib或/usr/local/lib目錄
方式 2:運行時動態(tài)加載(使用dlfcn.h)
這種方式無需在編譯時鏈接庫,可在程序運行中動態(tài)加載,適合插件化設(shè)計。
1. 編寫動態(tài)加載程序dynamic_main.cpp
#include <iostream>
#include <dlfcn.h> // 動態(tài)加載頭文件
int main() {
// 加載SO庫
void* handle = dlopen("./libmath_utils.so", RTLD_LAZY);
if (!handle) {
std::cerr << "加載庫失敗: " << dlerror() << std::endl;
return 1;
}
// 獲取函數(shù)指針(注意:函數(shù)指針類型必須與實際函數(shù)匹配)
typedef int (*AddFunc)(int, int);
AddFunc add = (AddFunc)dlsym(handle, "add");
typedef int (*MultiplyFunc)(int, int);
MultiplyFunc multiply = (MultiplyFunc)dlsym(handle, "multiply");
// 檢查函數(shù)是否獲取成功
const char* error;
if ((error = dlerror()) != NULL) {
std::cerr << "獲取函數(shù)失敗: " << error << std::endl;
dlclose(handle);
return 1;
}
// 調(diào)用函數(shù)
int a = 10, b = 20;
std::cout << "a + b = " << add(a, b) << std::endl;
std::cout << "a * b = " << multiply(a, b) << std::endl;
// 關(guān)閉庫
dlclose(handle);
return 0;
}2. 編譯動態(tài)加載程序
需要鏈接動態(tài)加載庫-ldl:
g++ dynamic_main.cpp -o dynamic_main -ldl
3. 運行程序
./dynamic_main
四、高級用法:帶類的 SO 庫
C++ 的類也可以封裝到 SO 庫中,但需要特殊處理(因為extern "C"不支持類)。
1. 定義帶純虛函數(shù)的接口類
calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
class Calculator {
public:
// 純虛函數(shù)(接口)
virtual int add(int a, int b) = 0;
virtual int multiply(int a, int b) = 0;
virtual ~Calculator() {} // 虛析構(gòu)函數(shù)
};
// 提供C風(fēng)格的創(chuàng)建和銷毀函數(shù)
extern "C" {
Calculator* create_calculator();
void destroy_calculator(Calculator* calc);
}
#endif // CALCULATOR_H2. 實現(xiàn)接口類
calculator_impl.cpp
#include "calculator.h"
class CalculatorImpl : public Calculator {
public:
int add(int a, int b) override {
return a + b;
}
int multiply(int a, int b) override {
return a * b;
}
};
// 實現(xiàn)C風(fēng)格的創(chuàng)建和銷毀函數(shù)
extern "C" {
Calculator* create_calculator() {
return new CalculatorImpl();
}
void destroy_calculator(Calculator* calc) {
delete calc;
}
}3. 編譯 SO 庫
g++ -fPIC -shared -o libcalculator.so calculator_impl.cpp
4. 調(diào)用帶類的 SO 庫
use_calculator.cpp
#include <iostream>
#include "calculator.h"
int main() {
// 創(chuàng)建對象
Calculator* calc = create_calculator();
// 調(diào)用方法
int a = 10, b = 20;
std::cout << "a + b = " << calc->add(a, b) << std::endl;
std::cout << "a * b = " << calc->multiply(a, b) << std::endl;
// 銷毀對象
destroy_calculator(calc);
return 0;
}編譯調(diào)用程序
g++ use_calculator.cpp -o use_calc -L. -lcalculator
五、注意事項
- 名稱修飾問題:C++ 會對函數(shù)名進行修飾(添加參數(shù)類型信息),必須用
extern "C"包裹導(dǎo)出函數(shù),確保 C 風(fēng)格的函數(shù)名 - 版本兼容性:修改 SO 庫后,若函數(shù)簽名不變,調(diào)用程序無需重新編譯
- 內(nèi)存管理:在 SO 庫中分配的內(nèi)存,應(yīng)在同一庫中釋放,避免跨庫內(nèi)存管理問題
- 依賴問題:若 SO 庫依賴其他庫,需要確保這些庫在運行時可被找到
- 調(diào)試:可使用
nm -D libxxx.so查看 SO 庫導(dǎo)出的函數(shù)列表
通過以上步驟,你可以將 C++ 代碼封裝為 SO 庫,并靈活地在其他程序中調(diào)用,這在大型項目模塊化開發(fā)中非常實用。
以上就是將C++程序打包成SO庫并調(diào)用的詳細流程的詳細內(nèi)容,更多關(guān)于C++程序打包成SO庫并調(diào)用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c++獲取sqlite3數(shù)據(jù)庫表中所有字段的方法小結(jié)
本文給大家分享c++獲取sqlite3數(shù)據(jù)庫表中所有字段的三種常用方法,本文針對每一種方法給大家詳細介紹,需要的的朋友通過本文一起學(xué)習(xí)吧2016-11-11
C++?容器中map和unordered?map區(qū)別詳解
這篇文章主要為大家介紹了C++?容器中map和unordered?map區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

