简介

PyBind11 的主要目的是将已有的C++代码接口暴露给Python去调用。

使用者:

  • onnxruntime (一个用于ONNX格式的神经网络模型推理的引擎)
  • pytorch (一个开源的Python机器学习库)

环境配置

macOS

1
2
3
# python3.7
brew install python3
python3 -m pip install pybind11

下载其他python版本:官方下载

Ubuntu

系统版本:18.04

1
sudo apt-get -y install python3 python3-dev python3-pip

CentOS

系统版本:7.5

1
2


Win

官方下载

设置环境变量:

Q&A

fatal error: ‘Python.h’ file not found

1
2
3
4
5
# centos
yum install python3-devel # 将会安装至 /usr/include
# ubuntu
echo "export C_INCLUDE_PATH=${C_INCLUDE_PATH}:/usr/include/python3.6" >> ~/.bashrc
echo "export CPLUS_INCLUDE_PATH=${CPLUS_INCLUDE_PATH}:/usr/include/python3.6" >> ~/.bashrc

亦或是在 CMakeLists.txt 中包含

1
2
include_directories(SYSTEM /usr/include/python3.6m)
include_directories(SYSTEM /usr/local/include/python3.6m)

ImportError: libSM.so.6: cannot open shared object file: No such file or directory

1
2
3
4
# ubuntu
apt-get install libsm6 libxrender1 libxext-dev
# centos
yum install libXext libSM libXrender

使用

前置

工欲善其事,必先利其器。

C++ 暴露给 Python 主要由两个方向:

  1. 函数 暴露给 Python
  2. 暴露给 Python

暴露函数

普通函数

C++示例源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// compute_pybind_state.cc

#include "pybind11/pybind11.h"
namespace py = pybind11;

int add(int l, int r) {
std::cout << "compute " << l << " + " << r << " :" << std::endl;
return l + r;
}

PYBIND11_MODULE(compute_pybind_state, m) {
m.doc() = "pybind11 compute plugin";
m.def("add", &add, "a function which adds two numbers.", py::arg("l") = 1, py::arg("r") = 2);
}

Python测试代码:

1
2
3
4
# main.py

import compute_pybind_state as CXX
print(CXX.add(2, 5))

匿名函数

C++示例源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// compute_pybind_state.cc

#include "pybind11/pybind11.h"
namespace py = pybind11;

PYBIND11_MODULE(compute_pybind_state, m) {
m.doc() = "pybind11 compute plugin";
m.def(
"sub",
[](int l, int r) {
std::cout << "compute " << l << " - " << r << " :" << std::endl;
return l - r;
},
"a function which one sub another.", py::arg("l") = 2, py::arg("r") = 1);
}

Python测试代码:

1
2
3
4
# main.py

import compute_pybind_state as CXX
print(CXX.sub(3, 1))

模板函数

C++示例源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// compute_pybind_state.cc

#include "pybind11/pybind11.h"
namespace py = pybind11;

template <typename T>
T square(T x) { return x * x; }

PYBIND11_MODULE(compute_pybind_state, m) {
m.doc() = "pybind11 compute plugin";
m.def("square", &square<double>);
m.def("square", &square<float>);
m.def("square", &square<int>);
}

Python测试代码:

1
2
3
4
5
# main.py

import compute_pybind_state as CXX
print(CXX.square(2))
print(CXX.square(2.2))

重载函数

pybind11::overload_cast 只需要制定输入参数的类型,函数的返回值类型会自动推断

注意,该方法仅限于 C++14 及其以上版本方可支持。

C++示例源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// compute_pybind_state.cc

#include "pybind11/pybind11.h"
namespace py = pybind11;

int sub(int l, int r) {
std::cout << "compute int " << l << " - " << r << " :" << std::endl;
return l - r;
}
float sub(float l, float r) {
std::cout << "compute float " << l << " - " << r << " :" << std::endl;
return l - r;
}

PYBIND11_MODULE(compute_pybind_state, m) {
m.doc() = "pybind11 compute plugin";
m.def("sub", py::overload_cast<int, int>(&sub), "a function which int numbers sub another.",
py::arg("l") = 3, py::arg("r") = 2);
m.def("sub", py::overload_cast<float, float>(&sub), "a function which float numbers sub another.",
py::arg("l") = 3.0f, py::arg("r") = 2.0f);
}

Python测试代码:

1
2
3
4
5
# main.py

import compute_pybind_state as CXX
print(CXX.sub(3, 2))
print(CXX.sub(5.2, 2.1))

指针参数的函数

暴露类

C++示例源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// binary.h

class Binary {
public:
Binary() = default;
Binary(const std::string& type) : type_(type) {}

void set(const std::string& type) { type_ = type; }
void set(const int v) { std::cout << "overloaded function: " << v << std::endl; }
const std::string get() { return type_ + "_mutable"; }
const std::string get() const { return type_ + "_const"; }

float compute(float a, float b) {
std::cout << "compute " << a << " " << type_ << " " << b << " :" << std::endl;
if (0 == type_.compare("add")) return a + b;
else if (0 == type_.compare("sub")) return a - b;
else if (0 == type_.compare("mul")) return a * b;
else if (0 == type_.compare("div")) return a / b;
else std::cout << "unsupport type: " << type_ << std::endl;
return 0.0f;
}

std::string type_ = "add";
};
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
// compute_pybind_state.cc

#include "pybind11/pybind11.h"
#include "binary.h"
namespace py = pybind11;

int sub(int l, int r) {
std::cout << "compute int " << l << " - " << r << " :" << std::endl;
return l - r;
}
float sub(float l, float r) {
std::cout << "compute float " << l << " - " << r << " :" << std::endl;
return l - r;
}

PYBIND11_MODULE(compute_pybind_state, m) {
m.doc() = "pybind11 compute plugin";
py::class_<Binary>(m, "Binary")
.def(py::init<>())
.def(py::init<const std::string&>())
.def("set", py::overload_cast<const std::string&>(&Binary::set), "set the binary's type.")
.def("set", py::overload_cast<const int>(&Binary::set), "test overloaded function.")
.def("get_mutable", py::overload_cast<>(&Binary::get), "get the binary's type by mutable.")
.def("get_const", py::overload_cast<>(&Binary::get, py::const_), "get the binary's type.")
.def("compute", &Binary::compute, "compute the binary's result.")
.def_readwrite("type_", &Binary::type_, "the attribute of binary.")
.def("__repr__",
[](const Binary& a) { return "<example.Binary type '" + a.type_ + "'>"; });
}

注意,py::overload_castc++14 及其以上版本方可支持。

类函数重载在 c++11 的写法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
PYBIND11_MODULE(compute_pybind_state, m) {
m.doc() = "pybind11 compute plugin";
py::class_<Binary>(m, "Binary")
.def(py::init<>())
.def(py::init<const std::string&>())
.def("set", (void(Binary::*)(const std::string&))&Binary::set, "set the binary's type.")
.def("set", (void(Binary::*)(const int))&Binary::set, "test overloaded function.")
.def("get", &Binary::get, "get the binary's type.")
.def("compute", &Binary::compute, "compute the binary's result.")
.def_readwrite("type_", &Binary::type_, "the attribute of binary.")
.def("__repr__",
[](const Binary& a) { return "<example.Binary type '" + a.type_ + "'>"; });
}

Python测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
# main.py

b1 = CXX.Binary()
print(b1)
b1.type_ = "div"
# print(b1.get())
print(b1.get_mutable())
print(b1.get_const())

b2 = CXX.Binary("sub")
print(b2.compute(11.0, 2.0))
b2.set("mul")
print(b2.compute(11.0, 2.0))

其他用法

注册模块变量

C++示例源码:

1
2
3
4
5
6
7
8
9
10
11
// compute_pybind_state.cc

#include "pybind11/pybind11.h"
namespace py = pybind11;

PYBIND11_MODULE(compute_pybind_state, m) {
m.doc() = "pybind11 compute plugin";
m.attr("author") = "wzx1";
py::object content = py::cast("HelloWorld");
m.attr("content") = content;
}

参考资料