所谓互操作,我觉得不妨先简单理解为C++调Python和Python调C++。前者主要是为了灵活和扩展,后者主要是为了效率。实现python和C++互操作的框架不少,包括著名的boost.python。我无意也没有实力评价各种框架的优劣,尝试pybind11也是因为公司的一次技术讨论,权当娱乐。
既然是从零开始,我想第一步就是下载pybind11,然后再能跑起来两个最简单的例子,一个是C++调python,另一个是python调C++.
0. 准备工作:
C++编译器:我是在windows上折腾,选择的就是visual studio 2015。pybind11因为用了C++11的新特性,对编译器还是比较挑剔。官网上说visual studio需要2015及以上。总之越新越好吧。
python: 我是从https://www.python.org/下载的最新版本。安装后,确保在安装目录下能看到include目录和libs目录。这两个目录里提供了头文件和链接时需要的库,对使用pybind11来说很重要。include下应该能找到Python.h,libs目录下应该能找到python35.lib (以python 3.5为例)。
git:这个主要是下载源码时用。不装也无所谓,直接从github上下载源码压缩包也行。如果用git的话,在本地空目录下git clone https://github.com/pybind/pybind11.git
cmake: 可以暂时不用
1. 几个重要的路径
pybind11的头文件: pybind11是header only的,所以我们只需要关心其头文件的路径即可。我的是:
E:\PYBIND11\PYBIND11\INCLUDE
└───pybind11
attr.h
buffer_info.h
cast.h
chrono.h
class_support.h
common.h
complex.h
descr.h
eigen.h
embed.h
eval.h
functional.h
numpy.h
operators.h
options.h
pybind11.h
pytypes.h
stl.h
stl_bind.h
typeid.h
python的头文件:我的是在C:\Users\shenfang\AppData\Local\Programs\Python\Python35\include
python的库文件:我的是在C:\Users\shenfang\AppData\Local\Programs\Python\Python35\libs
2. Python调C++的简单例子:
其实就是用C++写pyd,只是pybind11把过程简化了。C++代码源自pybind11的帮助
#include
namespace py = pybind11;
int add(int i, int j)
{
return i + j;
}
PYBIND11_PLUGIN(example)
{
py::module m("example", "pybind11 example plugin");
m.def("add", &add, "A function which adds two numbers");
return m.ptr();
}
cl example.cpp /I "e:\pybind11\pybind11\include" /I "C:\Users\shenfang\AppData\Local\Programs\Python\Python35\include" /LD /Fe:example.pyd /link/LIBPATH:"C:\Users\shenfang\AppData\Local\Programs\Python\Python35\libs\"
执行成功后会生成一个example.pyd,也就是我们用C++写的python模块。
简单的试一下,结果如下:
E:\pybind11\mytest>python
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> dir(example)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add']
>>> example.add(3,4)
7
>>>
3. C++调python的简单例子
我的目的是想从C++程序里调用python脚本里的函数。所以C++的code是这样
#include
#include
namespace py = pybind11;
int main() {
py::scoped_interpreter python;
py::module sys = py::module::import("sys");
py::print(sys.attr("path"));
py::module t = py::module::import("tttt");
t.attr("add")(1,2);
return 0;
}
"""tttt.py located in the working directory"""
def add(i, j):
print("hello,pybind11")
return i + j
还是用命令行编译:
cl invoker.cpp /I "e:\pybind11\pybind11\include" /I "C:\Users\shenfang\AppData\Local\Programs\Python\Python35\include" /Fe:invoker.exe /link /LIBPATH:"C:\Users\shenfang\AppData\Local\Programs\Python\Python35\libs\"
同样重点还是那几个关键的路径,这里不再赘述了。
运行invoker.exe,可以得到
E:\pybind11\mytest>invoker
['C:\\Users\\shenfang\\AppData\\Local\\Programs\\Python\\Python35\\python35.zip', 'C:\\Users\\shenfang\\AppData\\Local\\Programs\\Python\\Python35\\Lib', 'C:\\Users\\shenfang\\AppData\\Local\\Programs\\Python\\Python35\\DLLs', 'E:\\pybind11
\\mytest', 'C:\\Users\\shenfang\\AppData\\Local\\Programs\\Python\\Python35', 'C:\\Users\\shenfang\\AppData\\Local\\Programs\\Python\\Python35\\lib\\site-packages', '.']
hello,pybind11
4. 结论
感觉pybind11对双向互操作的支持还是挺不错的。互操作一个比较重要的方面就是C++ runtime和python runtime的相互访问,包括内存管理,数据类型转换等等。貌似pybind11都有不错的支持。下一步我准备从Python调C++入手,继续深入学习使用pybind11。