早就听说过了对于复杂的系统一般会结合多种语言进行开发,最近摸索了下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_0

The 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 value
collect2: 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]$python
Python 2.7 (r27:82500, Oct 20 2010, 13:31:35)
[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2
Type "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]$python
Python 2.7 (r27:82500, Oct 20 2010, 13:31:35)
[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello_class
>>> a = hello_class.CHello()
>>> a.Get
Traceback (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]$python
Python 2.7 (r27:82500, Oct 20 2010, 13:31:35)
[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2
Type "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++程序,更多复杂的导出, 比如继续,虚函数,还是参考手册吧,这里就不介绍了。



-EOF-
Simple is beautiful博客,转载请注明出处