將C++程序打包成SO庫(kù)并調(diào)用的詳細(xì)流程
一、SO 庫(kù)的基本概念
- 動(dòng)態(tài)鏈接庫(kù)(SO):在程序運(yùn)行時(shí)被加載,多個(gè)程序可共享同一 SO 庫(kù),節(jié)省內(nèi)存
- 優(yōu)點(diǎn):減小可執(zhí)行文件體積、便于模塊更新(無(wú)需重新編譯主程序)
- 核心:通過(guò)
extern "C"解決 C++ 名稱(chēng)修飾問(wèn)題,確保庫(kù)函數(shù)能被正確識(shí)別
二、創(chuàng)建 SO 庫(kù)的步驟
1. 準(zhǔn)備源文件
假設(shè)有一個(gè)簡(jiǎn)單的數(shù)學(xué)運(yùn)算模塊,包含頭文件和實(shí)現(xiàn)文件:
math_utils.h(頭文件)
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
// 用extern "C"包裹,避免C++名稱(chēng)修飾
#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(實(shí)現(xiàn)文件)
#include "math_utils.h"
// 加法實(shí)現(xiàn)
int add(int a, int b) {
return a + b;
}
// 乘法實(shí)現(xiàn)
int multiply(int a, int b) {
return a * b;
}2. 編譯生成 SO 庫(kù)
使用g++編譯,關(guān)鍵參數(shù):
-fPIC:生成位置無(wú)關(guān)代碼(必選,確保庫(kù)可被多個(gè)程序共享)-shared:指定生成動(dòng)態(tài)鏈接庫(kù)-o:指定輸出文件名(慣例以lib開(kāi)頭,.so結(jié)尾)
編譯命令:
g++ -fPIC -shared -o libmath_utils.so math_utils.cpp
執(zhí)行后會(huì)生成libmath_utils.so文件
三、調(diào)用 SO 庫(kù)的兩種方式
方式 1:編譯時(shí)鏈接(靜態(tài)加載)
1. 編寫(xiě)調(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)用程序
編譯時(shí)需要指定:
- 庫(kù)所在路徑(
-L.表示當(dāng)前目錄) - 庫(kù)名稱(chēng)(
-lmath_utils,省略lib前綴和.so后綴)
編譯命令:
g++ main.cpp -o main -L. -lmath_utils
3. 運(yùn)行程序
直接運(yùn)行生成的可執(zhí)行文件:
./main
可能的錯(cuò)誤:
若提示error while loading shared libraries: libmath_utils.so: cannot open shared object file: No such file or directory,解決方法:
- 臨時(shí)方案:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH - 永久方案:將 SO 庫(kù)復(fù)制到
/usr/lib或/usr/local/lib目錄
方式 2:運(yùn)行時(shí)動(dòng)態(tài)加載(使用dlfcn.h)
這種方式無(wú)需在編譯時(shí)鏈接庫(kù),可在程序運(yùn)行中動(dòng)態(tài)加載,適合插件化設(shè)計(jì)。
1. 編寫(xiě)動(dòng)態(tài)加載程序dynamic_main.cpp
#include <iostream>
#include <dlfcn.h> // 動(dòng)態(tài)加載頭文件
int main() {
// 加載SO庫(kù)
void* handle = dlopen("./libmath_utils.so", RTLD_LAZY);
if (!handle) {
std::cerr << "加載庫(kù)失敗: " << dlerror() << std::endl;
return 1;
}
// 獲取函數(shù)指針(注意:函數(shù)指針類(lèi)型必須與實(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)閉庫(kù)
dlclose(handle);
return 0;
}2. 編譯動(dòng)態(tài)加載程序
需要鏈接動(dòng)態(tài)加載庫(kù)-ldl:
g++ dynamic_main.cpp -o dynamic_main -ldl
3. 運(yùn)行程序
./dynamic_main
四、高級(jí)用法:帶類(lèi)的 SO 庫(kù)
C++ 的類(lèi)也可以封裝到 SO 庫(kù)中,但需要特殊處理(因?yàn)?code>extern "C"不支持類(lèi))。
1. 定義帶純虛函數(shù)的接口類(lèi)
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)建和銷(xiāo)毀函數(shù)
extern "C" {
Calculator* create_calculator();
void destroy_calculator(Calculator* calc);
}
#endif // CALCULATOR_H2. 實(shí)現(xiàn)接口類(lèi)
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;
}
};
// 實(shí)現(xiàn)C風(fēng)格的創(chuàng)建和銷(xiāo)毀函數(shù)
extern "C" {
Calculator* create_calculator() {
return new CalculatorImpl();
}
void destroy_calculator(Calculator* calc) {
delete calc;
}
}3. 編譯 SO 庫(kù)
g++ -fPIC -shared -o libcalculator.so calculator_impl.cpp
4. 調(diào)用帶類(lèi)的 SO 庫(kù)
use_calculator.cpp
#include <iostream>
#include "calculator.h"
int main() {
// 創(chuàng)建對(duì)象
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;
// 銷(xiāo)毀對(duì)象
destroy_calculator(calc);
return 0;
}編譯調(diào)用程序
g++ use_calculator.cpp -o use_calc -L. -lcalculator
五、注意事項(xiàng)
- 名稱(chēng)修飾問(wèn)題:C++ 會(huì)對(duì)函數(shù)名進(jìn)行修飾(添加參數(shù)類(lèi)型信息),必須用
extern "C"包裹導(dǎo)出函數(shù),確保 C 風(fēng)格的函數(shù)名 - 版本兼容性:修改 SO 庫(kù)后,若函數(shù)簽名不變,調(diào)用程序無(wú)需重新編譯
- 內(nèi)存管理:在 SO 庫(kù)中分配的內(nèi)存,應(yīng)在同一庫(kù)中釋放,避免跨庫(kù)內(nèi)存管理問(wèn)題
- 依賴(lài)問(wèn)題:若 SO 庫(kù)依賴(lài)其他庫(kù),需要確保這些庫(kù)在運(yùn)行時(shí)可被找到
- 調(diào)試:可使用
nm -D libxxx.so查看 SO 庫(kù)導(dǎo)出的函數(shù)列表
通過(guò)以上步驟,你可以將 C++ 代碼封裝為 SO 庫(kù),并靈活地在其他程序中調(diào)用,這在大型項(xiàng)目模塊化開(kāi)發(fā)中非常實(shí)用。
以上就是將C++程序打包成SO庫(kù)并調(diào)用的詳細(xì)流程的詳細(xì)內(nèi)容,更多關(guān)于C++程序打包成SO庫(kù)并調(diào)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Qt實(shí)現(xiàn)可以計(jì)算大數(shù)的簡(jiǎn)單計(jì)算器
計(jì)算器是我們生活中很常見(jiàn)的東西,它可以由多種語(yǔ)言多種方式來(lái)實(shí)現(xiàn)。本文主要介紹的是基于C++語(yǔ)言,由QT實(shí)現(xiàn)的可以計(jì)算大數(shù)的簡(jiǎn)單計(jì)算器,需要的可以參考一下2022-12-12
windows下在vim中搭建c語(yǔ)言開(kāi)發(fā)環(huán)境的詳細(xì)過(guò)程
這篇文章主要介紹了windows下在vim中搭建c語(yǔ)言開(kāi)發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05
C語(yǔ)言輸出旋轉(zhuǎn)后數(shù)組中的最小數(shù)元素的算法原理與實(shí)例
這篇文章主要介紹了C語(yǔ)言輸出旋轉(zhuǎn)后數(shù)組中的最小數(shù)元素的算法原理與實(shí)例,數(shù)組旋轉(zhuǎn)就是把開(kāi)頭的幾個(gè)指定的元素放到數(shù)組的末尾,需要的朋友可以參考下2016-03-03
C語(yǔ)言 文件的打開(kāi)與關(guān)閉詳解及示例代碼
本文主要介紹C語(yǔ)言 文件的基礎(chǔ)知識(shí),這里整理了相關(guān)資料及示例代碼,有興趣的小伙伴可以參考下2016-08-08
OpenCV提取圖像中圓線上的數(shù)據(jù)具體流程
在對(duì)圖像進(jìn)行處理時(shí),經(jīng)常會(huì)要提取出圖像中某條直線、圓線或者ROI區(qū)域內(nèi)的感興趣數(shù)據(jù),進(jìn)行重點(diǎn)關(guān)注。本文主要介紹了利用OpenCV獲取圖像中圓線上的數(shù)據(jù),需要的可以參考一下2021-11-11
C++實(shí)現(xiàn)四則運(yùn)算器(無(wú)括號(hào))
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)四則運(yùn)算器,無(wú)括號(hào)的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
詳解C++中的對(duì)象指針與對(duì)象數(shù)組
這篇文章主要介紹了詳解C++中的對(duì)象指針與對(duì)象數(shù)組,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09

