Python中調(diào)用C++代碼的方法總結(jié)
1. extern "C" {} 包裹導(dǎo)出函數(shù)
// C++ 中存在名稱修飾,通過 extern "C" {} 將C++函數(shù)導(dǎo)出為C函數(shù)
// 1. 為類的每個(gè)函數(shù)創(chuàng)建C風(fēng)格接口,第一個(gè)參數(shù)為對(duì)象指針
// 2. 提供 create(構(gòu)造) destroy(析構(gòu)) 函數(shù)管理對(duì)象的生命周期
#include <string>
// C++類定義
class Calculator {
private:
int base;
public:
Calculator(int b) : base(b) {}
int add(int a) { return base + a; }
std::string greet(const std::string& name) { return "Hello, " + name; }
};
// C風(fēng)格函數(shù)
extern "C" {
// 創(chuàng)建對(duì)象(對(duì)應(yīng)構(gòu)造函數(shù))
Calculator* Calculator_create(int base) {
return new Calculator(base);
}
// 銷毀對(duì)象(對(duì)應(yīng)析構(gòu)函數(shù))
void Calculator_destroy(Calculator* obj) {
delete obj;
}
// 封裝成員函數(shù)
int Calculator_add(Calculator* obj, int a) {
return obj->add(a);
}
// 字符串處理需要特殊轉(zhuǎn)換(C++ string -> char*)
const char* Calculator_greet(Calculator* obj, const char* name) {
static std::string result; // 注意:靜態(tài)變量在多線程中不安全
result = obj->greet(std::string(name));
return result.c_str();
}
}
2. 將C++代碼編譯為動(dòng)態(tài)庫
# 使用C++編譯器,保留 -f PIC -shared 參數(shù)生成位置無關(guān)的動(dòng)態(tài)庫 g++ -shared -fPIC -o libcalc.so example.cpp
3.python ctypes調(diào)用動(dòng)態(tài)庫
使用python的ctypes庫調(diào)用動(dòng)態(tài)庫(加載動(dòng)態(tài)庫 -> 定義C函數(shù)的輸入?yún)?shù)與返回參數(shù) -> 調(diào)用動(dòng)態(tài)庫函數(shù))
- 用 c_void_p 類型存儲(chǔ)C++對(duì)象的指針
- 字符串通過bytes類型轉(zhuǎn)換(encode/decode)
- 實(shí)現(xiàn) del 方法自動(dòng)釋放C++對(duì)象,避免內(nèi)存泄漏
import ctypes
import os
class CalculatorWrapper:
"""C++ Calculator類的Python封裝器"""
def __init__(self):
# 加載動(dòng)態(tài)庫
self.lib = self._load_library()
# 配置C接口函數(shù)原型
self._setup_functions()
# 存儲(chǔ)C++對(duì)象指針
self.obj_ptr = None
def _load_library(self):
"""根據(jù)操作系統(tǒng)加載對(duì)應(yīng)的庫文件"""
try:
if os.name == "nt":
return ctypes.CDLL("./calc.dll")
elif os.name == "posix":
return ctypes.CDLL("./libcalc.so")
else:
raise OSError("不支持的操作系統(tǒng)")
except OSError as e:
raise RuntimeError(f"加載庫失敗: {e}")
def _setup_functions(self):
"""定義C接口函數(shù)的參數(shù)類型和返回值類型"""
# 創(chuàng)建對(duì)象
self.lib.Calculator_create.argtypes = [ctypes.c_int]
self.lib.Calculator_create.restype = ctypes.c_void_p # 返回對(duì)象指針
# 銷毀對(duì)象
self.lib.Calculator_destroy.argtypes = [ctypes.c_void_p]
self.lib.Calculator_destroy.restype = None
# 加法函數(shù)
self.lib.Calculator_add.argtypes = [ctypes.c_void_p, ctypes.c_int]
self.lib.Calculator_add.restype = ctypes.c_int
# 字符串問候函數(shù)
self.lib.Calculator_greet.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
self.lib.Calculator_greet.restype = ctypes.c_char_p
def create(self, base_value):
"""創(chuàng)建C++ Calculator實(shí)例"""
self.obj_ptr = self.lib.Calculator_create(base_value)
if not self.obj_ptr:
raise RuntimeError("創(chuàng)建C++對(duì)象失敗")
def add(self, a):
"""調(diào)用加法方法"""
self._check_object()
return self.lib.Calculator_add(self.obj_ptr, a)
def greet(self, name):
"""調(diào)用問候方法(處理字符串轉(zhuǎn)換)"""
self._check_object()
# Python字符串轉(zhuǎn)C字符串(bytes類型)
c_name = name.encode('utf-8')
result_ptr = self.lib.Calculator_greet(self.obj_ptr, c_name)
# C字符串轉(zhuǎn)Python字符串
return result_ptr.decode('utf-8')
def _check_object(self):
"""檢查對(duì)象是否已創(chuàng)建"""
if not self.obj_ptr:
raise RuntimeError("請(qǐng)先調(diào)用create()創(chuàng)建對(duì)象")
def __del__(self):
"""對(duì)象銷毀時(shí)自動(dòng)釋放C++資源"""
if self.obj_ptr:
self.lib.Calculator_destroy(self.obj_ptr)
if __name__ == "__main__":
try:
calc = CalculatorWrapper()
calc.create(100) # 初始化base值為100
print(f"100 + 50 = {calc.add(50)}") # 輸出150
print(calc.greet("C++")) # 輸出"Hello, Python"
except Exception as e:
print(f"錯(cuò)誤: {e}")
4.方法補(bǔ)充
以下是小編整理的Python調(diào)用C++代碼的其他方法,大家可以參考一下
使用ctypes調(diào)用C/C++代碼
編寫C/C++代碼
首先,我們編寫一個(gè)簡(jiǎn)單的C++函數(shù),并將其編譯為共享庫(.so文件)。
// mylib.cpp
#include <iostream>
extern "C" {
int add(int a, int b) {
std::cout << "add called" << std::endl;
return a + b;
}
}
編譯C/C++代碼
使用以下命令將C++代碼編譯為共享庫:
g++ -shared -o libmylib.so -fPIC mylib.cpp
-shared:生成共享庫。-o libmylib.so:指定輸出文件名為libmylib.so。-fPIC:生成位置無關(guān)代碼(Position Independent Code),這是共享庫所必需的。
在Python中調(diào)用C/C++函數(shù)
使用ctypes庫加載共享庫并調(diào)用C++函數(shù):
import ctypes
import os
# 加載共享庫
lib = ctypes.CDLL(os.path.abspath("libmylib.so"))
# 調(diào)用 C++ 函數(shù)
result = lib.add(3, 4) # add called
print(f"Result: {result}") # 輸出: Result: 7
使用pybind11調(diào)用C/C++代碼
需要 pip install pybind11
pybind11 的名字中的 11 表示它需要 C++11 或更高版本 的支持
編寫C/C++代碼
pybind11是一個(gè)輕量級(jí)的C++庫,用于將C++代碼暴露給Python。我們編寫一個(gè)簡(jiǎn)單的C++函數(shù),并使用pybind11將其綁定到Python。
// mylib_pybind.cpp
#include <pybind11/pybind11.h>
#include <iostream>
int add(int a, int b) {
std::cout << "add called from pybind" << std::endl;
return a + b;
}
PYBIND11_MODULE(mylib_pybind, m) {
m.def("add", &add, "A function that adds two numbers");
}
編譯C/C++代碼
使用以下命令將C++代碼編譯為Python模塊:
c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) mylib_pybind.cpp -o mylib_pybind$(python3-config --extension-suffix)
生成了 mylib_pybind.cpython-311-x86_64-linux-gnu.so 文件
-O3:優(yōu)化級(jí)別為3,生成高度優(yōu)化的代碼。-Wall:?jiǎn)⒂盟芯妗?/li>-shared:生成共享庫。-std=c++11:使用C++11標(biāo)準(zhǔn)。-fPIC:生成位置無關(guān)代碼。$(python3 -m pybind11 --includes):獲取pybind11的頭文件路徑。-o mylib_pybind$(python3-config --extension-suffix):指定輸出文件名,并使用Python的擴(kuò)展名后綴。
在Python中調(diào)用C/C++函數(shù)
import mylib_pybind as mylib
result = mylib.add(3, 4) # add called from pybind
print(f"Result: {result}") # 輸出: Result: 7
c++調(diào)用python代碼
# example.py
def add(a, b):
return a + b
#include <pybind11/embed.h> // 嵌入 Python 解釋器
#include <iostream>
namespace py = pybind11;
int main() {
// 啟動(dòng) Python 解釋器
py::scoped_interpreter guard{};
// 導(dǎo)入 Python 模塊
py::module_ example = py::module_::import("example");
// 調(diào)用 Python 函數(shù)
py::object result = example.attr("add")(3, 4);
std::cout << "Result: " << result.cast<int>() << std::endl; // 輸出: Result: 7
return 0;
}
g++ -o call_py call_py.cpp -I/opt/conda/envs/ai/include/python3.10/ $(python3 -m pybind11 --includes) -L/opt/conda/envs/ai/lib -lpython3.10 echo 'export LD_LIBRARY_PATH=/opt/conda/envs/ai/lib:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc
執(zhí)行 ./call_py
到此這篇關(guān)于Python中調(diào)用C++代碼的方法總結(jié)的文章就介紹到這了,更多相關(guān)Python調(diào)用C++代碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python matplotlib 注釋文本箭頭簡(jiǎn)單代碼示例
這篇文章主要介紹了python matplotlib 注釋文本箭頭簡(jiǎn)單代碼示例,具有一定借鑒價(jià)值。2018-01-01
python 多種日期時(shí)間處理函數(shù)實(shí)例詳解
Python提供了豐富的日期和時(shí)間處理函數(shù),可以幫助你輕松地解析、格式化、計(jì)算和操作日期和時(shí)間,在實(shí)際應(yīng)用中,根據(jù)具體需求選擇合適的函數(shù),可以提高工作效率并簡(jiǎn)化代碼,本文給大家介紹python多種日期時(shí)間處理函數(shù)介紹,感興趣的朋友一起看看吧2024-03-03
python調(diào)用百度API實(shí)現(xiàn)人臉識(shí)別
這篇文章主要介紹了python調(diào)用百度API實(shí)現(xiàn)人臉識(shí)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
實(shí)現(xiàn)Windows下設(shè)置定時(shí)任務(wù)來運(yùn)行python腳本
這篇文章主要介紹了實(shí)現(xiàn)Windows下設(shè)置定時(shí)任務(wù)來運(yùn)行python腳本的完整過程,有需要的朋友可以借鑒參考下,希望對(duì)廣大讀者朋友能夠有所幫助2021-09-09
Python圖像閾值化處理及算法比對(duì)實(shí)例解析
這篇文章主要介紹了Python圖像閾值化處理及算法比對(duì)實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
python高手之路python處理excel文件(方法匯總)
用python來自動(dòng)生成excel數(shù)據(jù)文件。python處理excel文件主要是第三方模塊庫xlrd、xlwt、xluntils和pyExcelerator,除此之外,python處理excel還可以用win32com和openpyxl模塊2016-01-01
PyQt5多線程防卡死和多窗口用法的實(shí)現(xiàn)
這篇文章主要介紹了PyQt5多線程防卡死和多窗口用法的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

