(5)python开发经验
文章目录
- 1 同时生成dll和py模块
- 2 pybind11封装回调函数
- 3 pybind11封装回调获取GIL
更多精彩内容 |
---|
👉内容导航 👈 |
👉Qt开发 👈 |
👉python开发 👈 |
1 同时生成dll和py模块
由于add_library和pybind11_add_module会冲突,所以想要同时生成dll动态库和py模块,有两种解决办法;
- 方法1:使用不同的名称;
- 方法2:需要使用cmake子工程。
可以在不修改原代码的情况下通过拓展pybind11代码,生成py模块。
方法1
cmake_minimum_required(VERSION 3.10)
# project(PCBA_TestLib)set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)add_library(testc SHARED test.cpp)# 寻找python库,Interpreter:表示查找 Python 解释器,Development:表示查找 Python 开发包
find_package(Python COMPONENTS Interpreter Development)
find_package(pybind11 PATHS "D:/Python/Python313/Lib/site-packages/pybind11/share/cmake" REQUIRED)
pybind11_add_module(testPy toPy.cpptest.cpp) # 使用pybind11_add_module函数生成模块
方法2
-
test.h
#ifndef TEST_H #define TEST_Hextern "C" { __declspec(dllexport) int add(int a, int b); } #endif
-
test.cpp
#include "test.h"int add(int a, int b) {return a + b; }
-
toPy.cpp
#include <pybind11/pybind11.h> #include "test.h"namespace py = pybind11; // 这里是关键,声明一个命名空间 py// 参数1:模块名,参数2:模块对象 PYBIND11_MODULE(test, m) { m.doc() = "pybind11 example plugin"; // zh-CN: 这里是模块的描述信息m.def("add", &add, "A function which adds two numbers"); // 这里是关键,声明一个函数 add }
-
主CMakeLists.txt
cmake_minimum_required(VERSION 3.10)add_subdirectory(cLib) add_subdirectory(pyLib)
-
cLib/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)project(test)set(SOURCES ../test.h../test.cpp)add_library(${PROJECT_NAME} SHARED ${SOURCES})
-
pyLib/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)project(testPy)# 寻找python库,Interpreter:表示查找 Python 解释器,Development:表示查找 Python 开发包 find_package(Python COMPONENTS Interpreter Development) find_package(pybind11 PATHS "D:/Python/Python313/Lib/site-packages/pybind11/share/cmake" REQUIRED)set(SOURCES ../test.cpp../toPy.cpp)pybind11_add_module(${PROJECT_NAME} ${SOURCES}) # 使用pybind11_add_module函数生成模块
2 pybind11封装回调函数
使用pybind11封装回调函数,实现将python函数传入c++代码;
方法1:使用pybind11::function
,支持自动失败传入类型。
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>namespace py = pybind11; // 这里是关键,声明一个命名空间 py// C++ 函数接受一个 Python 回调
void py_callback(py::function cb) {// 调用前需获取 GIL(自动通过 py::function 管理)cb(123);cb("Hello from C++"); // 传递参数给 Python 回调
}
// 参数1:模块名,参数2:模块对象
PYBIND11_MODULE(testPy, m)
{ m.doc() = "pybind11 example plugin"; // zh-CN: 这里是模块的描述信息m.def("callback", &py_callback, "Call a Python callback from C++");
}
方法2:使用std::function
,必须要包含#include <pybind11/functional.h>
;
- 关键要点说明:
- 类型转换:
pybind11/functional.h
头文件已内置std::function
与Python callable对象的自动转换 - 线程安全:若C++回调可能跨线程调用,需在回调执行时获取GIL:
- 类型转换:
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include "test.h"// 定义回调函数类型
using CallbackFunc = std::function<void(int)>;// 需要暴露的函数
void std_callback(CallbackFunc callback)
{// 处理逻辑(示例:累加操作)int result = 2;// 调用 Python 传入的回调if (callback) {callback(result); // 触发 Python 回调}
}// 参数1:模块名,参数2:模块对象
PYBIND11_MODULE(testPy, m)
{ m.doc() = "pybind11 example plugin"; // zh-CN: 这里是模块的描述信息m.def("process_with_callback", &std_callback, "A function that takes a callback function");
}
python调用:
import testPydef fun(a):print("Hello", a)if __name__ == "__main__":testPy.py_callback(fun)testPy.std_callback(fun)
3 pybind11封装回调获取GIL
若在 C++ 创建的子线程中需要回调 Python 函数、修改 Python 数据结构,必须在此线程中先获取 GIL。
在不修改已有代码的情况下,通过封装实现回调,并且获取GIL。
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include "test.h"
#include <iostream>using CallbackFunc = std::function<void(int)>;
CallbackFunc callback1;// 定义封装回调
void fun(int a)
{py::gil_scoped_acquire acquire; // 手动获取 GILcallback1(a);
}
// 对python暴露的回调接口
void py_std_callback(CallbackFunc callback)
{callback1 = callback; // 记录回调函数std_callback(fun); // 将封装的回调函数传入C++
}// 参数1:模块名,参数2:模块对象
PYBIND11_MODULE(testPy, m)
{ m.doc() = "pybind11 example plugin"; // zh-CN: 这里是模块的描述信息m.def("std_callback", &py_std_callback, "A function that takes a callback function");
}