首先要明白pybind11是干啥的,对于一个C/C++库,可以用pybind11封装它的接口为Python接口,这样得到一个python库,就可以把功能强大的库丢给使用python的boys & girls使用了~
因此,使用pybind11做封装,是我们“library developer”干的事情,说不上底层,但也比较底层了。你应该会用CMake,你也应该熟悉C/C++和Python。你编译出来的python库,其实就是一个.so结尾的动态库,也就是python里的一个module。
现在实现一个add(a, b)的接口,功能是计算两个数字的和。不考虑各种边界情况。
mkdir -p ~/work/test/toy
cd $_
mkdir 3rdparty
git clone https://github.com/pybind/pybind11 3rdparty/pybind11
mkdir build
mkdir src
touch src/example.cpp
touch CMakeLists.txt
目录结构
src/example.cpp
#include <pybind11/pybind11.h>int add(int i, int j) {return i + j;
}PYBIND11_MODULE(example, m) {m.doc() = "pybind11 example plugin"; // optional module docstringm.def("add", &add, "A function which adds two numbers");
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.14)project(toy)set(CMAKE_CXX_STANDARD 11)add_subdirectory(3rdparty/pybind11)
pybind11_add_module(example src/example.cpp)
执行构建:
cd build
cmake ..
make
其中红框里的.so文件就是我们生成的python库了。在python中加载它(盘它!):
import exampleexample.add(100, 200)
example.__doc__
简要分析说明
这里的CMakeLists.txt中,通过加载pybind11目录,会把pybind11/tools/pybind11Tools.cmake中的pybind11_add_module()函数引入。这个函数的功能,是创建一个指定名字的库(target是一个动态库);但是,会修改库文件名字的前缀后缀;并且还有一堆其他的编译链接设定。因此这里不通过手动add_library()命令来创建target,而是先包含pybind11目录再用pybind11_add_module()函数。
也就是说,pybind11被当成一个3rdparty库被引入当前工程。从代码版本控制的角度看,可以把它弄成一个submodule,当然如果强项作为源码管理也可以,但是要注意tests子目录有3M大,可以考虑删除。
C/C++代码中的example和CMakeLists.txt中的target名字,要保持一致吗?
是的,要保持一致。譬如把C++中的example改为exampleMod则虽然能编译出库,但是Python中无法import: