C++与Python的互操作(pybind11): 从零开始

所谓互操作,我觉得不妨先简单理解为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();
}

我使用的是vs2015 x64 Native Tools Command Prompt,需要指定前面说的几个重要路径。命令行如下:

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;
}

python的脚本是这样:

"""tttt.py located in the working directory"""

def add(i, j):
    print("hello,pybind11")
    return i + j

名字起成tttt.py主要是避免一些不必要的和已有库重名的麻烦。

还是用命令行编译:

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。


你可能感兴趣的:(C++,Python)