接触到的很多开源项目都由纯 C++ 转到 Python + C++了,我也因此一直心向往之,今天动手实验了一下,发现并没有很复杂(初步观察)。趁热把这个过程记录下来,也免得明天到办公室就忘了。。
其实做Python 和 C++ 的接口有很多种方式,比如Swig,Cython,Boost 等。我用Boost是因为之前贡献过代码的一个项目用的就是这种方式,不想跳其他坑了而已。所以这里并没有要尝试去比较几种方案的优劣。
用Boost做Python 和 C++ 接口的好处是不用修改C++的代码,只需要单独再写一个wrapper就好了,很方便。
首先配置环境,你要有一个Linux,然后有一个g++,最后有一个boost的库。
我自己的环境是这样的:
Ubuntu 14 (Windows Subsystem for Linux)
g++ 版本:4.8.4
boost 版本:1.56
其次要有C/C++源代码,比如我们想要暴露接口有:
class Foo {
// blah blah...
public:
Foo(int a, int b, std::string c ...); // constructor
bar(std::string d);
}
std::string duh() {
return "duh!";
}
当然为了行文简洁很多细节都忽略了,你可以随便写个hello world来代替这一部分的实现。这里的重点是要把常用的接口(class 和 function)暴露出来。
接下来你需要单独写一个c/cpp文件,假设命名为
// pymodule.cc
// this is in your boost/lib dir#include
// your stuff#include "foo.h"
// use this name space so that you can call the following functionusing namespace boost::python;
BOOST_PYTHON_MODULE(foo) // foo will be the name you import in python{
class_("Foo",
init()) // constructor args .def("bar", &Foo::bar) // member function ;
def("duh", duh); // other functions}
// EOF
最后编译就好了,然而这个地方是各种文档里一带而过的地方,也是我花时间最多的地方。。(主要是设置各种PATH)
方便起见,我们还是写个Makefile,重点是要把那几个linker的option搞定:
PYTHON_VERSION = 2.7
PYTHON_INC = /usr/include/python$(PYTHON_VERSION)
PYTHON_CONFIG = /usr/lib/python2.7/config
BOOST_INC = path/to/your/boost/include
BOOST_LIB = path/to/your/boost/lib
SRC = $(wildcard *.cc)
OBJ = $(addsuffix .o, $(basename $(SRC)))
# this target has to match whatever you have in BOOST_PYTHON_MODULE()TARGET = foo
# make this a so library$(TARGET).so: $(OBJ)
g++ -shared -Wl,--export-dynamic $(OBJ) -L$(BOOST_LIB) -lboost_python -L$(PYTHON_CONFIG) -lpython2.7 -o $(TARGET).so
%.o: %.cc
g++ -I$(PYTHON_INC) -I$(BOOST_INC) -fPIC -o $@ -c $<
clean:
-rm -f *.o *.so
经评论提示,可以用cmake把这块做的更优雅:
cmake_minimum_required(VERSION 2.8.3)FIND_PACKAGE(PythonInterp)FIND_PACKAGE(PythonLibs)FIND_PACKAGE(Boost COMPONENTS python)INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})LINK_LIBRARIES(${BOOST_LIBRARIES} ${PYTHON_LIBARIES})add_library(foo SHARED
pymodule.cc
foo.cc)# set prefix otherwise the output will be libfoo.so instead of foo.soset_target_properties(foo PROPERTIES PREFIX "")target_link_libraries(foo
${BOOST_LIBRARIES}
${PYTHON_LIBARIES}
)
然后命令行敲make就好了。成功的话你应该看到你的foo.so。
然后还有一些PATH需要设置,比如要把你的 boost/lib 加入到 LD_LIBRARY_PATH中,以及确保你的库文件(so)是在你运行python时候的PATH里的。
最后你就可以在python里:
import foo
f = foo.Foo(1, 2, "23333")
f.bar(1.234)
foo.duh()
这样做的好处无非在于,可以用python实现处理输入输出这种dirty work,然后把最吃性能的部分用C++实现,从而既有易用性又有不错的性能,感觉可以用来做比较cool的东西。当然我也是初次尝试,以前只是用别人做好的现成的,不知道这里面有哪些坑,慢慢探索吧。。这篇的评论我会一直看,欢迎讨论及指教~