pybind11快速实现c,c++ 与 python互通
2022-01-28 本文已影响0人
python测试开发
简介
pybind11是一个轻量级的头文件库, 为Python和C、C++实现了类型互通,主要用于为现有C++代码创建Python绑定。它的目标和语法与Boost.Python库相似:通过使用编译时自省来推断类型信息,最大限度地减少传统扩展模块中的模板代码。
Boost是一个庞大而复杂的实用程序库套件,几乎可以与现存的所有C++编译器一起使用。这种兼容性是有代价的:为了支持最古老和最有问题的编译器,必须使用神秘的模板技巧和变通方法。现在与C++11兼容的编译器已经广泛使用,Boost已经包含过于庞大和不必要的依赖。
pybind11是Boost.Python的精简版本,剥离了所有与绑定生成无关的东西。在没有注释的情况下,核心头文件只需要大约4K行代码,并且依赖于Python(2.7或3.5+,或PyPy)和C++标准库。这种紧凑的实现是由于一些新的C++11语言特性(特别是:tuple、lambda函数和变量模板)而实现的。
教程和参考文档在https://pybind11.readthedocs.io/en/latest/提供。源代码可以在https://github.com/pybind/pybind11/获得。
核心功能
以下核心C ++功能可以映射到Python
- 函数入参和返回值可以是自定义数据结构的值、引用或者指针
- 实例方法和静态方法
- 重载
- 实例属性和静态属性
- 任意异常类型
- 枚举
- 回调
- 迭代器和range
- 自定义操作符
- 单继承和多继承
- STL数据结构
- 带有引用计数的智能指针,类似于std::shared_ptr
- 正确的引用计数的内部引用
- 带有虚函数和纯虚函数的C ++类可以在Python中扩展
其他有用功能
- Python 2.7,3.5+和PyPy/PyPy3 7.3支持与实现无关接口。
- 将C++11 lambda函数与捕获的变量绑定在一起。lambda捕获的数据存储在生成的Python函数对象中。
- pybind11使用C++11 move constructors 和 move assignment operators从而有效地转换自定义数据类型。
- 通过Python的buffer协议,可以很容易地公开内部存储的自定义数据类型,比如C++矩阵类型如Eigen和NumPy之间进行快速转换。
- pybind11可以自动将函数矢量化,以便它们透明地应用于以NumPy数组为参数的所有条目。
- 只需几行代码就可以支持Python的基于切片的访问和赋值操作。
- 一切都只包含在几个头文件中,没有必要链接任何其他库。
- 与Boost.Python比二级制文件更小,编译时间更短,5倍速以上。
- 函数签名是在编译时预先计算的(使用constexpr),生成更小的二进制文件。
- 只需很少的额外工作,C ++类型就可以像Python对象一样进行pickled和unpickled。
快速入门
- 安装
调试环境:ubuntu 20.04.3 LTS
# pip install pybind11
# git clone https://github.com/pybind/pybind11
# cd pybind11/include
创建文件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 docstring
m.def("add", &add, "A function that adds two numbers");
}
编译执行
# c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
# ls
example.cpp example.cpython-38-x86_64-linux-gnu.so pybind11
# python
Python 3.8.8 (default, Apr 13 2021, 19:58:26)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.add(1, 2)
3
在c++中调用python
demo.cpp
#include <iostream>
#include <pybind11/embed.h>
namespace py = pybind11;
using namespace std;
int main()
{
cout << "Hello PyBind World" << endl;
// start the interpreter and keep it alive
py::scoped_interpreter guard{};
py::module math = py::module::import("math");
py::object result = math.attr("sqrt")(25);
std::cout << "Sqrt of 25 is: " << result.cast<float>() << std::endl;
}
新建CMakeLists.txt
project(pybind_demo)
cmake_minimum_required(VERSION 3.13)
set(CMAKE_CXX_STANDARD 14)
include(FetchContent)
FetchContent_Declare(
pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11.git
GIT_TAG v2.6.2
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(pybind11)
add_executable(demo demo.cpp)
target_link_libraries(demo PRIVATE pybind11::embed)
编译执行:
$ cmake .
CMake Warning (dev) at CMakeLists.txt:1 (project):
Policy CMP0048 is not set: project() command manages VERSION variables.
Run "cmake --help-policy CMP0048" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.
The following variable(s) would be set to empty:
CMAKE_PROJECT_VERSION
CMAKE_PROJECT_VERSION_MAJOR
CMAKE_PROJECT_VERSION_MINOR
CMAKE_PROJECT_VERSION_PATCH
This warning is for project developers. Use -Wno-dev to suppress it.
-- pybind11 v2.6.2
-- Configuring done
-- Generating done
-- Build files have been written to: /home/andrew/code/pybind11/include
$ make
Scanning dependencies of target demo
[ 50%] Building CXX object CMakeFiles/demo.dir/demo.cpp.o
[100%] Linking CXX executable demo
[100%] Built target demo
$ ./demo
Hello PyBind World
Sqrt of 25 is: 5
以上步骤需要保证对github的访问。参考资料:https://blog.devgenius.io/calling-python-and-c-code-using-pybind-99ab7fefa685
在python中调用c++
example.cpp
#include <pybind11/embed.h>
int multiply(int i, int j) {
return i * j;
}
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("multiply", &multiply, "A function which multiplies two numbers");
}
新建CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(pybind_demo)
set(CMAKE_CXX_STANDARD 14)
include(FetchContent)
FetchContent_Declare(
pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11.git
GIT_TAG v2.6.2
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(pybind11)
pybind11_add_module(example example.cpp)
target_compile_features(example PUBLIC cxx_std_14)
set_target_properties(example PROPERTIES SUFFIX ".so")
编译执行:
$ cmake .
-- pybind11 v2.6.2
-- Configuring done
-- Generating done
-- Build files have been written to: /home/andrew/code/pybind11/include/exmaple
$ make
Scanning dependencies of target example
[ 50%] Building CXX object CMakeFiles/example.dir/example.cpp.o
[100%] Linking CXX shared module example.so
[100%] Built target example
$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt _deps example.cpp example.so Makefile
$ python
Python 3.8.8 (default, Apr 13 2021, 19:58:26)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.multiply(3, 2)
6