-
背景介紹
pybind11是一個基於C++11標准的模版庫. 與Boost.Python類似, pybind11主要着眼於創建C++代碼的Python封裝, 並為其提供了一套輕量級的解決方案. -
安裝與代碼示例
終端運行: pip3 install pybind11
⑤. 獲取pybind11庫相關目錄
解釋器內運行:import pybind11 pybind11.get_cmake_dir() # 獲取cmake目錄 pybind11.get_include() # 獲取include目錄
⑥. 待封裝之C++源碼
本文以一個main.cpp源文件為例, 簡要給出一個函數與一個類的封裝示例, 代碼如下,#include <string> #include <iostream> #include <pybind11/pybind11.h> #include <pybind11/eigen.h> int MyFunc(int i, int j) { return i + j; } class MyClass { public: MyClass(const std::string& msg) : msg_(msg) {} void printMsg() { std::cout << this->msg_ << std::endl; } Eigen::VectorXd add(const Eigen::VectorXd& lhs, const Eigen::VectorXd& rhs) { Eigen::VectorXd ret = lhs + rhs; return ret; } std::string msg_; }; PYBIND11_MODULE(testlib, m) // 此處設置模塊名為testlib { m.doc() = "This is a test library"; m.def("MyFunc", &MyFunc, "my first function", pybind11::arg("i") = 1, pybind11::arg("j") = 2); pybind11::class_<MyClass>(m, "MyClass") .def(pybind11::init<const std::string&>()) .def("printMsg", &MyClass::printMsg) .def("add", &MyClass::add, pybind11::arg("lhs"), pybind11::arg("rhs")) .def_readwrite("msg_", &MyClass::msg_); }
-
cmake工程示例
配合上述main.cpp源文件, CMakeLists.txt文件內容如下,cmake_minimum_required(VERSION 3.15) set(CMAKE_BUILD_TYPE "Release") set(CMAKE_CXX_STANDARD 11) project(test_lib) set(test_srcs main.cpp ) set(pybind11_DIR "/opt/homebrew/lib/python3.9/site-packages/pybind11/share/cmake/pybind11") # 此處設置pybind11之cmake目錄, 即: pybind11.get_cmake_dir() find_package(pybind11 REQUIRED) pybind11_add_module(testlib ${test_srcs}) # 此處設置模塊名為testlib target_include_directories(testlib PUBLIC "/Users/xxhbdk/MyLibs/eigen-3.4.0") # 此處附加包含eigen庫目錄
當前工程結構如下,
-
編譯及效果展示
終端運行如下命令編譯Python動態庫:mkdir build # 創建編譯目錄 cd build cmake .. make
運行完成后, 筆者build目錄下生成了Python動態庫文件"testlib.cpython-39-darwin.so". 隨后即可在Python環境中使用之, 測試效果如下,
可以看到, 接口導出整體符合預期. -
注意事項
-
參考文檔
def MyFunc(i, j): return i + j
②. 待編譯之C++源碼
本文以一個main.cpp源文件為例, 簡要給出一個C++調用Python函數之示例, 代碼如下,#include <iostream> #include <pybind11/embed.h> int main() { pybind11::scoped_interpreter guard; // 初始化python解釋器 pybind11::module my_func = pybind11::module::import("my_func"); int i = 11; int j = 22; pybind11::object ret = my_func.attr("MyFunc")(i, j); int n = ret.cast<int>(); std::cout << i << " + " << j << " = " << n << std::endl; }
③. cmake工程示例
配合上述main.cpp源文件, CMakeLists.txt文件內容如下,cmake_minimum_required(VERSION 3.15) set(CMAKE_BUILD_TYPE "Release") set(CMAKE_CXX_STANDARD 11) project(test_cppInvokePy) set(test_srcs main.cpp ) add_executable(main ${test_srcs}) set(pybind11_DIR "/opt/homebrew/lib/python3.9/site-packages/pybind11/share/cmake/pybind11") # 此處設置pybind11之cmake目錄, 即: pybind11.get_cmake_dir() find_package(pybind11 REQUIRED) target_link_libraries(main PUBLIC pybind11::embed)
④. 編譯及效果展示
當前工程結構如下,終端運行如下命令編譯C++可執行文件,
mkdir build # 創建編譯目錄 cd build cmake .. make
運行完成后, 筆者build目錄下生成了可執行文件"main". 隨后將Python文件my_func.py拷貝至此build目錄下.終端運行可執行文件main, 測試效果如下,
可以看到, 執行結果符合預期, Python模塊調用成功.
- 補充1 - 參考文檔
①. https://www.yuque.com/u461675/pcadi1/hf4fha#e82c4d4d - 補充2(Global Interpreter Lock)
當執行流從Python側進入C++側時, GIL總是持有的. 因此, 如果C++側代碼長時間運行, 且不釋放GIL, 則Python側多線程可能無法達到預期的運行效果(如: UI運行阻塞等).
因此, 通過Python調用C++時, 若C++側代碼執行時間較長且具備Python側多線程需求, 建議在C++代碼入口處釋放GIL.
釋放GIL之方法①(功能代碼執行處): pybind11::gil_scoped_release release;
釋放GIL之方法②(模塊接口定義處): pybind11::call_guard<pybind11::gil_scoped_release>(); - 補充2 - 注意事項
①. C++側多線程不受GIL影響. - 補充2 - 參考文檔