早就听说过了对于复杂的系统一般会结合多种语言进行开发,最近摸索了下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++程序,更多复杂的导出, 比如继续,虚函数,还是参考手册吧,这里就不介绍了。