早就听说过了对于复杂的系统一般会结合多种语言进行开发,最近摸索了下C++和Python混合编程,在此总结一下,做为笔记。
对于C++和python混合编程实际上包含两部分内容:
- 用C++写python扩展(extending): 即所谓的在python中调用C/C++代码,一般用于对效率要求高核心模块用C/C++编写,通过一些处理后生成动态库如cppmod.so,然在python代码中import cppmod,即可调用C/C++
- python脚本嵌入到C++程序中(embedding): 即所谓的C++代码调用python,C++ app要嵌入python解析器从而调用python代码
就实现而言,对于这两者都可以直接用Python/C API,具体可以去参考python文档,但都比较麻烦
- 对于1, 主要的方案有 boost.python 以及swig, 后者不太了解,本篇主要讲利用boost.python,对C/C++处理后生成动态库,python导入后调用C/C++代码
- 对于2, C/C++代码中嵌入python,也可以用 boost.python,当然直接 Python/C API也可以
一、安装boost(latest version boost1.55.0)
[tanli@p04bc boost_1_55_0]$./bootstrap.sh --with-python=/usr/bin/python2.7 --with-python-root=/usr --with-python-version=2.7[tanli@p04bc boost_1_55_0]$./b2 variant=debug link=static threading=multi --with-python
这里为节省时间,只编译boost.python库的static-debug-multi版本,默认在当前目录构建,生成的静库文件位于./statge, 构建完成好了看到信息如下:
The Boost C++ Libraries were successfully built!The following directory should be added to compiler include paths: /home/tanli/soft/boost_1_55_0The following directory should be added to linker library paths: /home/tanli/soft/boost_1_55_0/stage/lib
注意: boost.python 库是和python版本相关的,当安装Boost, 会自动探测Python版本, 如果有多个python版本,还是需要指定的,否则后面出现一些错误。
二、用boost.python库导出C++函数
在cpp文件中增加导出接口代码
#include <iostream>#include <boost/python.hpp>using namespace boost::python;void SayHello(){ std::cout<<"hello, world!"<<std::endl;}void SayBye(){ std::cout<<"good bye!"<<std::endl;}BOOST_PYTHON_MODULE(hello) // generate python module hello{ def("SayHello", SayHello); def("SayBye", SayBye);}
编译成动态库:
[tanli@p04bc boost_1_55_0]$ g++ hello.cpp -shared -fPIC -o hello.so -I ~/soft/boost_1_55_0 -L ~/soft/boost_1_55_0/stage/lib -lboost_python -I /usr/include/python2.7/usr/bin/ld: /home/tanli/soft/boost_1_55_0/stage/lib/libboost_python.a(registry.o): relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC/home/tanli/soft/boost_1_55_0/stage/lib/libboost_python.a: could not read symbols: Bad valuecollect2: ld returned 1 exit status
原来是boost.python静态库有问题,要加编译选项-fPIC, 看来得改编译选项,google了下,可以怎么改 针对boost_1_55_0版本,修改tools/build/v2/tools/gcc.jam,注释掉391行:
#if $(link) = shared
按照上面命令重新编译boost.python库,然后在编译上面程序,这回好了,再打开python导入动态库调用吧
[tanli@p04bc boostpython]$g++ hello.cpp -shared -fPIC -o hello.so -I ~/soft/boost_1_55_0 -L ~/soft/boost_1_55_0/stage/lib -lboost_python -I /usr/include/python2.7[tanli@p04bc boostpython]$pythonPython 2.7 (r27:82500, Oct 20 2010, 13:31:35)[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import hello>>> hello.SayHello()hello, world!>>> hello.SayBye()good bye!>>> exit()
三、用boost.python库导出C++类
刚才的例子是导出一个函数给python用,那么对于class呢?
#include <string>#include <boost/python.hpp>#include <boost/python/module.hpp>#include <boost/python/def.hpp>using namespace std;class CHello{public: string GetString() { return "CHello::GetString()"; } int GetInt() { return 100; }};BOOST_PYTHON_MODULE(hello_class){ using namespace boost::python; class_<CHello>("CHello") .def("GetString",&CHello::GetString) .def("GetInt",&CHello::GetInt) ;}
编译运行:
[tanli@p04bc boostpython]$ g++ helloclass.cpp -shared -fPIC -o hello_class.so -I ~/soft/boost_1_55_0 -L ~/soft/boost_1_55_0/stage/lib -lboost_python -I /usr/include/python2.7[tanli@p04bc boostpython]$pythonPython 2.7 (r27:82500, Oct 20 2010, 13:31:35)[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import hello_class>>> a = hello_class.CHello()>>> a.GetTraceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'CHello' object has no attribute 'Get'>>> a.GetString()'CHello::GetString()'>>> a.GetInt() 100>>> quit()
四、用boost.python库导出构造函数
对于非默认构造函数怎么导出呢:
#include <string>#include <boost/python.hpp>using namespace std;class CHello{public: CHello(string msg) : m_msg(msg) { m_id = 0; } CHello(int id) { m_id = id; } string GetString() const { return m_msg; } string SetString(string msg) { m_msg = msg; } int GetInt() { return m_id; } int SetInt(int id) { m_id = id; } string m_msg; int m_id;};BOOST_PYTHON_MODULE(hello_class){ using namespace boost::python; class_<CHello>("CHello", init<string>()) .def(init<int>()) .def("SetString", &CHello::SetString) .def("GetString", &CHello::GetString) .def("GetInt", &CHello::GetInt) ;}
测试结果:
[tanli@p04bc boostpython]$g++ helloclass.cpp -shared -fPIC -o hello_class.so -I ~/soft/boost_1_55_0 -L ~/soft/boost_1_55_0/stage/lib -lboost_python -I /usr/include/python2.7[tanli@p04bc boostpython]$pythonPython 2.7 (r27:82500, Oct 20 2010, 13:31:35)[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import hello_class>>> a = hello_class.CHello() Traceback (most recent call last): File "<stdin>", line 1, in <module>Boost.Python.ArgumentError: Python argument types in CHello.__init__(CHello)did not match C++ signature: __init__(_object*, int) __init__(_object*, std::string)>>> a = hello_class.CHello(1)>>> a.GetInt()1>>> b = hello_class.CHello("hello world!")>>> b.GetString()'hello world!'
五、总结
总的来说就是在C++里引入导出接口代码,然后生成动态库,python里以导入模块的方式加载动态库,然后调用C++程序,更多复杂的导出, 比如继续,虚函数,还是参考手册吧,这里就不介绍了。