0%

使用C++为python编写扩展

编译一时爽,环境火葬场

因为使用A*算法对于路径的寻找需要更高的效率,很明显,python自身完全不能满足我们的需求,因此使用C++编写python扩展变得非常重要.

不幸的是,我昨天花了整整一天都没有解决这个问题,但是今天却进行的异常顺利,下面我来讲一下在使用C++编写python中的几个坑

准备

我们使用的是pybind的库,具体pybind应当如何使用在这里我就不过多介绍了,下面先贴出来我用做测试的代码:

1
2
3
4
5
6
7
8
9
10
11
#include"pybind11/pybind11.h"
namespace py = pybind11;

int testadd(int i,int j)
{
return i+j;
}

PYBIND11_MODULE(pytest2,m)
{
m.def("testadd",&testadd,"A function to add");

需要注意将安装好的pybind11中的include文件夹的pybind11文件夹拷贝到工程文件夹中.

使用g++直接编译

接下来就到了万恶的编译环节,首先,我看见文档里面直接给g++编译的命令

1
g++ -O3 -march=native -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` test.cpp -o test `python3-config --extension-suffix`

这个命令看起来很简单,但是存在一个问题,里面依赖python-config命令的输出,但是众所周知,这个命令只有在Linux下才能使用.

我再一次感觉到了windows开发的险恶

使用CMake编译

接下来,我看到网上说cmake可以进行编译,这样看起来挺好,然而暗藏杀机.

因为cmake也需要类似于python-config的环境,然后虽然我安装mingw确实来了一个python-config但是这会导致我编译工具链使用的是MS的产品,但是采用的解释器却是GCC编译的.

中间省略了因为使用anaconda而不得不一步步教cmake如何找到我的pybind的过程

然后这就带来一个完全爆炸.pyd文件,这个pydmingw里面的python不认,然后到了conda里面的python直接提示我dll丢失.

你以为我没查Google,直接提示你:“你安装的库可能有误,建议卸载重装”

我自己写的库怎么卸载重装

但是如果我换做mingw编译,则更加刺激,直接python给我闪退了

我拿windbg查了一下,是内存冲突,但是别的我也不知道了

使用setup.py编译

这种方法看起来简单,但是结果和那CMake一样

简而言之,我电脑的状态就是这样:

  1. 使用windows操作系统
  2. 卸载了VisualStudio
  3. 装了mingw
  4. 使用anaconda管理python环境

直接buff叠满,能做出来才有鬼.

使用cppimport

这是我目前看到的最好的方法来处理这个问题,源网页

使用这个的好处在于反正我编译出来成功了

这个库用法并不复杂,首先是在文件开头需要添加一行注释

1
// cppimport

然后在文件的末尾需要有

1
2
3
4
5
6
7
/*
<%
cfg['sources'] = ['a_star.cpp']
cfg['extra_compile_args'] = ['/O2 /Oi']
setup_pybind11(cfg)
%>
*/

这实际上是一段Mako块,这一部分的python代码将会被运行

其中第一行是由于多文件编译,指定除了这个文件外的另外源文件

第二行则是使用相关的编译选项

cppimport调用

虽然cppimport支持实时调用(每次import)的时候预编译,但是这么做对我们来讲没有必要,我们可以采用cppimport的预编译方式(就是pyd)实现

使用以下代码

1
python -m cppimport build pytest.cpp

虽然理论上没有pytest.cpp也可以自动检测,但是我们不可避免会带有Unicode字符,然后就出错了