PyBind11 是目前最流行、最推荐的 C++ 与 Python 交互工具。它是一个 Header-only 库,语法极其简洁,能够自动处理类型转换、引用计数和 STL 容器。

uv pip install pybind11 进行使用,然后在 CMakeLists.txt 添加依赖

1
2
3
4
5
cmake_minimum_required(VERSION 3.4)
project(example)

add_subdirectory(pybind11) # 指向 pybind11 源码
pybind11_add_module(example example.cpp)

导入函数

example.cpp 里定义需要的函数。这里的语法是:

1
2
3
PYBIND11_MODULE(exported_import_name, m) {
m.def("exported_function_name", &cpp_function, "doc string", /* arguments */);
}

首先必须要用 PYBIND11_MODULE() 进行包裹.这里的 exported_import_name 表示导出的包名,Python 侧可以调用 exported_import_name.exported_function_name(...) 进行调用.

对于 arguments 这一部分,py::args("exported_argument_name") 定义了 Python 侧的参数名称,当然也可以赋默认值,py::arg("some_arg") = value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// example.cpp
#include <pybind11/pybind11.h>

int add(int i, int j) {
return i + j;
}

namespace py = pybind11;

// 定义模块名
// 这里的 myext 就表示 Python 侧的 import 包名,e.g. import myext
PYBIND11_MODULE(myext, m) {
m.doc() = "pybind11 example plugin";
m.def("add", &add, "computes a+b", py::arg("i"), py::arg("j"));
}

最后在 Python 里进行调用

1
2
import myext
print(myext.add(10, 30))

导入 C++ Class

假设 C++ 侧的类长这样

1
2
3
4
5
6
struct Foo {
Foo(int x) : x(x) {}
int x;
int inc(int d) { x += d; return x; }
std::string repr() const { return "Foo(x=" + std::to_string(x) + ")"; }
};

那么 C++ 侧的 PYBIND 导出就可以这么写.这其中

  • py::class_<ClassName>(m, "ExportedClassName") 告诉 Python 类名
  • 后续的 .def 则是定义其具有的方法
    • py::init<int>(), py::arg("x") 表示这是一个初始化函数,接收一个 int 参数
    • .def_readwrite() 则可以对 public member 进行访问、修改
    • 注意这里的 __repr__,没错,也可以重载 Python 的方法
    • 其他的就和定义函数一样
1
2
3
4
5
6
7
8
9
10
#include <pybind11/pybind11.h>
namespace py = pybind11;

PYBIND11_MODULE(myext, m) {
py::class_<Foo>(m, "Foo")
.def(py::init<int>(), py::arg("x"))
.def("inc", &Foo::inc, py::arg("d") = 1)
.def_readwrite("x", &Foo::x)
.def("__repr__", &Foo::repr);
}