编译运行Boost.Python的HelloWorld

对于C/C++开发经验不多、或者只有Python经验的人来说,要跑通一个Boost.Python的HelloWord程序,是一件不容易的事。 本文介绍如何避开bjam,用Makefile打通最不容易的那个环节——编译。

简介

Boost.PythonBoost项目的一部分,是一个C++写的库,用来提供Python调用C++代码的接口。

在Python中,直接使用C语言代码是很容易的,用ctypes即可。 而如果需要使用C++的类、对象、方法、虚函数等高级语言特性,则没那么简单。 Boost.Python通过简单的包装,就可以暴露C++接口给Python使用,支持多数常见的语言特性。

Tutorial

如其Tutorial所述,其实Boost.Python的使用方式很简单。 写一个wrapper,即可提供给Python层调用。

#include <boost/python.hpp>

char const* greet()
{
   return "hello, world";
}

BOOST_PYTHON_MODULE(hello_ext)
{
    using namespace boost::python;
    def("greet", greet);
}

编译以上文件(可命名为greet.c),即可在Python中使用。

>>> import hello_ext
>>> print hello_ext.greet()
hello, world

坑?

Demo看上去似乎很简单,为什么是坑呢?

编译准备

首先,#include那行就泄露了一点天机。 为了编译这个Demo文件,得安装libboost-all-dev。 这东西及其依赖,有好几百兆,得花不少准备时间。 另外还需python-devpython3-dev

sudo apt install libboost-all-dev python-dev python3-dev

当然,编译常用的gcc、make等组件(build-essential)必须提前具备。

(本文创作平台是Debian Stretch,Boost的版本是1.62.0.1,相关库为boost_python-py27、boost_python-py35。 Python的库是python2.7和python3.5m。)

编译

其次,编译仍然是个问题。 如果翻到文档的下一页,看到《Building Hello World》,初学者会更加不幸! 这不仅无助于解决问题,反而加剧了困惑。 不要指望能迅速从bjam、Jamroot、Jamfile这个坑里出来,除非已经是资深的C++开发者。

如果实在是想跑通这个HelloWorld,可以尝试以下的Makefile。

hello_ext.so : greet.o
	g++ -shared greet.o -o hello_ext.so -lpython2.7 -lboost_python

greet.o : greet.cpp
	g++ -c -fPIC -O2 -I/usr/include/python2.7 greet.cpp -o greet.o

把以上代码,写入同目录的Makefile文件。 并且按照以下步骤执行,可以成功看到Boost.Python的效果。

$ make
g++ -c -fPIC -O2 -I/usr/include/python2.7 greet.cpp -o greet.o
g++ -shared greet.o -o hello_ext.so -lpython2.7 -lboost_python
$ ipython2
>>> import hello_ext
>>> print hello_ext.greet()
hello, world

以上是Python 2.7的例子,因为-lboost_python默认是Python 2.x的。 以下再给出一个Python 3.5的Makefile。

hello_ext.so : greet.o
	g++ -shared greet.o -o hello_ext.so -lpython3.5m -lboost_python-py35

greet.o : greet.cpp
	g++ -c -fPIC -I/usr/include/python3.5m greet.cpp -o greet.o

这个Makefile中记录的每一个g++参数都是必须的,连文件命名都不能乱改,一点微调都会走样。 其中有许多深坑,就不一一介绍了。

总结

ctypes的直接可用相比,Boost.Python是一个略显复杂的方案。 不过在某些必须直接调用C++代码的情况下,它可以取代ctypes。 可以说,Boost.Python的适用性远在ctypes之上。

ctypes是以动态链接的方式使用libxxx.so,而Boost.Python是直接import写好的xxx.so。 不过,Boost.Python需要一个额外的C++包装层,并且在编译时还需要很多额外依赖。 此外,对开发者的C/C++开发实力,尤其是用gcc编译、链接的能力,也有较高的要求。 所以,开发效率和发布时的通用性,比标准库ctypes差很多。

还是优先用ctypes吧,不行再来Boost.Python里想办法。

参考

Boost.Python在GitHub上的项目主页:https://github.com/boostorg/python


相关笔记