使用Pybind11将Python代码封装为可在C++环境调用的.pyd
一、环境准备1.1 Python环境安装Python官网速度慢可从镜像站下载CNPM Binaries Mirror找到需要版本安装即可。1.2 C环境Visual Studio官网下载即可。速度慢软件管家-安全、稳定、纯净的软件下载平台-首页1.3 相关库安装使用Pybind11库可以使Python与C中暴露的接口被另一方调用。可以使用pip安装pip install pybind11 (-i https://pypi.tuna.tsinghua.edu.cn/simple)使用cmake生成项目pip install cmake二、绑定Py与C2.1 项目结构推荐结构如下项目名纯英/├─CMakeLists.txt├─ src/│ ├─ xxx1.cpp│ ├─ xxx2.cpp│ └─ bind.cpp└─ python/├─ xxx1.py└─ xxx2.py其中xxx1.py与xxx2.py是将要向C暴露接口的程序xxx1、xxx2同名的cpp文件为对应的绑定声明文件bind.cpp为主绑定文件CMakeLists.txt为配置信息文件我们下一小节详细描述。2.2 CMakeLists.txt我们cmake用来生成项目CMakeLists.txt用于描述配置信息。cmake_minimum_required(VERSION 3.12) #最低cmake版本 project(项目名) # 查找python find_package(Python COMPONENTS Interpreter Development REQUIRED) # 查找pybind11 find_package(pybind11 REQUIRED) # 设置输出目录 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # 添加模块 pybind11_add_module(项目名 src/xxx1.cpp src/xxx2.cpp src/bind.cpp ) # 设置包含目录 target_include_directories(项目名 PRIVATE ${PYTHON_INCLUDE_DIRS} ) # 设置链接库 target_link_libraries(项目名 PRIVATE ${PYTHON_LIBRARIES} ) # 复制python文件到输出目录 file(GLOB PYTHON_FILES python/*.py) file(COPY ${PYTHON_FILES} DESTINATION ${CMAKE_BINARY_DIR}/bin)2.3 绑定代码2.3.1 绑定声明文件src中如xxx1.cpp即为绑定声明文件用于声明xxx1.py中我们将要调用的函数。编写方法如下#include pybind11/pybind11.h #include pybind11/stl.h #include pybind11/functional.h #include pybind11/numpy.h //调用pybind11头文件 namespace py pybind11; void init_xxx1(py::module_ m) { // 绑定暴露函数 m.def(函数名, [](带有数据类型的python中参数) { py::object module py::module_::import(xxx1); module.attr(函数名)(无声明类型的参数); }, 备注); }其中带有数据类型的python中参数应带有类型声明如const std::string path无声明类型的参数只写path即可。常见的数据类型声明方法字符串const std::string数组const std::vector数据类型整形/浮点int / float其他诸如xxx2.cpp类似编写即可。2.3.2 主绑定文件src中bind.cpp为主绑定文件其中包含了全部函数的声明编写方法如下#include pybind11/pybind11.h namespace py pybind11; void init_xxx1(py::module_ ); void init_xxx2(py::module_ ); //这里的init_xxx1要与绑定声明文件中对应 PYBIND11_MODULE(xctd_bind, m) { m.doc() 模型注释; init_xxx1(m); init_xxx2(m); }2.4 构建项目在项目根目录下打开cmd打开根目录后在路径栏输入cmd回车即可打开。mkdir build #创建build目录 cd build #进入build目录 cmake .. #在上一级目录运行cmake此时将自动识别出CMakeLists文件 #并根据配置信息在build目录下生成项目文件 cmake --build . --config Release #以release模式生成项目运行后即可在项目名/build/bin/Release中找到bind.xxxxxxxx.pyd其中xxxxxxxx为python版本表示与平台架构表示也可能没有不影响使用。此时得到.pyd链接库可以正常复制到其他项目中调用使用时应与py源代码一起使用。三、使用说明3.1 使用样例使用时应将bind.pyd与xxx1.py、xxx2.py一同复制使用调用样例如下#include pybind11/embed.h #include pybind11/stl.h #include Python.h #include iostream #include vector #include algorithm namespace py pybind11; static bool xxx1Once(py::module xctd, 带有类型声明的参数) { try { xctd.attr(xxx1函数名)(无声明类型的参数); } catch (const py::error_already_set e) { std::cerr Python 异常: e.what() std::endl; return false; } catch (const std::exception e) { std::cerr C 异常: e.what() std::endl; return false; }//捕获异常 return true; }//ProcessXctdData static bool xxx2Once(py::module xctd, 带有类型声明的参数) { try { xctd.attr(xxx2函数名)(无声明类型的参数); } catch (const py::error_already_set e) { std::cerr Python 异常: e.what() std::endl; return false; } catch (const std::exception e) { std::cerr C 异常: e.what() std::endl; return false; }//捕获异常 return true; }//DelayCorrected.main int main() { //初始化py解释器******仅初始化一次需放在主线程、main函数中使用 py::scoped_interpreter guard{}; if (!Py_IsInitialized()) { std::cout Init Py Fail!\n; return -1; } //声明模型 auto xctd py::module_::import(bind); //调用函数 xxx1Once(xctd, 无声明类型的参数); xxx2Once(xctd, 无声明类型的参数); return 0; }3.2 注意事项3.2.1 环境配置使用vs环境下调用python与pybind11需要添加附加包含目录、附加库目录与附加依赖项方法如下。3.2.1.1 附加包含目录右键单击右侧列表中项目名称选择“属性”在弹出的窗口中依次选择:C/C - 常规并在右侧“附加包含目录”后输入pybind11与python的include目录目录之间使用半角分号;隔开。目录获取方法如下在cmd中输入pip show pybind11其中Location项为pybind11库绝对位置将冒号后面复制并添加\pybind11\include此为pybind11包含目录目录将Lib及后续部分删除改为include即为python包含目录目录。3.2.1.2 附加库目录在项目属性窗口中依次选择:连接器 - 常规并在右侧“附加库目录”后输入python的库目录。库目录为附加包含目录条目下获取的Location项Lib及后续部分删除改为libs即为python库目录。3.2.1.3 附加依赖项在项目属性窗口中依次选择:连接器 - 输入在右侧“附加依赖项”后输入python的库目录并在目录结尾添加\pythonxxx.lib其中xxx为python版本可打开python库目录自行查看。3.2.2 第三方python库pybind11运行python代码时需要安装代码中使用的三方库使用pip安装即可