python_cpp_interaction
1. Python调用C++
python调用C++需要编译出 python 的 module so,是一种区别于普通依赖库的动态库,可以通过pybind11 中的cmake 函数进行生成。 - step 1 首先要配置好pybind11 的依赖,其是纯头文件库,在CMakeLists.txt 中可以通过CPM 拉取,除此之外还需要依赖的 eigen, pybind11中有一些矩阵计算需要用到的。
1 |
|
- step 2 生成扩展包格式的动态库需要使用pybind11 提供的
pybind11_add_module 函数,然后去链接pybind11、和 python
库,以及引用头文件
1
2
3
4
5
6
7pybind11_add_module(cpp2py_binding src/cpp2py_binding.cpp)
target_link_libraries(cpp2py_binding PUBLIC python3.8)
target_include_directories(cpp2py_binding
PRIVATE ${pybind11_SOURCE_DIR}/include
PRIVATE ${eigen_SOURCE_DIR}
PRIVATE /usr/include/python3.8
) - step 3 编写 src/cpp2py_binding.cpp,其中实现提供给python
调用的接口。如果有C++中定义的结构体需要传递给python,则需要实现类型转换器,示例如下,get_lane_instance_from_cpp
是暴漏给 python 的接口,type_caster 是python 接收到 c++ 中的
LaneInstance 类型时进行的转换,其由解释器调用,无需用户操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48#include <pybind11/pybind11.h>
#include <iostream>
namespace py = pybind11;
class LaneInstance {
public:
int id;
LaneInstance() : id(0) {}
void print() const {
std::cout << "Lane ID: " << id << std::endl;
}
};
LaneInstance get_lane_instance_from_cpp() {
LaneInstance lane;
// 填充 lane 对象的数据
lane.id = 99;
return lane;
}
// 自定义类型转换器
namespace pybind11 { namespace detail {
template <>
struct type_caster<LaneInstance> {
public:
PYBIND11_TYPE_CASTER(LaneInstance, _("LaneInstance"));
bool load(handle src, bool convert) {
// 从 Python 对象转换到 C++ 对象(这里不实现,因为我们只关心从 C++ 到 Python 的转换)
return false;
}
static handle cast(const LaneInstance &src, return_value_policy policy, handle parent) {
// 将 C++ 对象转换为 Python 字典
pybind11::dict dict;
dict["id"] = src.id;
return dict.release();
}
};
}} // namespace pybind11::detail
PYBIND11_MODULE(cpp2py_binding, m) {
m.def("get_lane_instance", &get_lane_instance_from_cpp, "Get a LaneInstance object from C++");
} - step 4 编译后会有生成一个 cpp2py_binding.cpython-38-x86_64-linux-gnu.so 的东西,其作为python 的扩展包由 python 使用,需要在启动 python 前将 .so 的目录路径放在 PYTHONPATH 环境变量中,然后就可以在 python 中调用 get_lane_instance 了。注意 get_lane_instance 是python 中的接口,其背后的实现是c++ 中的get_lane_instance_from_cpp。
1 |
|
- note
- 如果是C++中的自定义数据类型,一定要有类型转换器,不然就会报 TypeError: Unregistered type
- cpp文件中定义的 PYBIND11_MODULE 名称 一定要和 CMakeLists.txt pybind11_add_module 名称一致,不然会报 ImportError: dynamic module does not define module export function
2. C++调用Python
简单来说,就是通过pybind11中的数据类型,将C++类型转化为python 类型,比如 pybind11::dict、pybind11::list,也有一些cast 操作
step 1
1
2
3
4
5
6
7
8# c++ call python script
add_executable(pyprocess src/pyprocess.cpp)
target_include_directories(pyprocess
PRIVATE ${pybind11_SOURCE_DIR}/include
PRIVATE ${eigen_SOURCE_DIR}
PRIVATE /usr/include/python3.8
)
target_link_libraries(pyprocess PUBLIC python3.8)step 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
class LaneInstance {
public:
int id;
LaneInstance() : id(0) {}
void print() const {
std::cout << "Lane ID: " << id << std::endl;
}
};
int py_get_lane_instances(LaneInstance lane) {
#if 0
auto start_time = std::chrono::high_resolution_clock::now();
std::shared_ptr<void> time_cost(nullptr, [start_time](void* p) {
auto end_time = std::chrono::high_resolution_clock::now();
std::cout << "py_get_valid_other_instances time cost: "
<< std::chrono::duration_cast<std::chrono::microseconds>(
end_time - start_time).count()
<< " us" << std::endl;
});
#endif
static py::scoped_interpreter py_interpreter_init = []() {
py::scoped_interpreter guard {};
return guard;
}();
// 准备参数
// py::dict py_lane_instance = cpp2py_lane_instance(lane);
py::dict py_lane_instance;
py_lane_instance["id"] = lane.id; // py::dict 的key 只支持字符串类型
py::int_ out_ret = -1;
// 调用 Python 函数
out_ret = py::module_::import("callee").attr("get_lane_instances")(
py_lane_instance
);
std::cout << "in c++, out_ret: " << out_ret.cast<int>() << std::endl;
return 0;
}
int main() {
LaneInstance lane;
lane.id = 999;
py_get_lane_instances(lane);
return 0;
}step 3
1
2
3
4
5
6
7
def get_lane_instances(lane_instance):
print("in python, lane_instance: ", lane_instance)
out_ret = 999
print("in python out_ret: ", out_ret)
return out_ret
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!