Python与C/C++混合编程

| 分类 c/c++之函数  c/c++之指针与内存  c/c++之面向对象  python之面向对象  c/c++之so  | 标签 ctypes  boost  c  c++  python  内存  跨语言  linux  windows  混合编程  otool  so  mac 

本文展示使用ctypes、boost::python实现c/c++和python的混合编程,当然这只是一种方法的展示,除此之外还有其他的方式!包括boost::python的使用应该也不止一种!

Python调用C函数

完全参考CoolShell博客《PYTHON调用C语言函数》

使用Python的ctypes,我们可以直接调用由C编译出来的函数。其实就是调用动态链接库中的函数。为什么要这样做?因为有些时候,我们可能需要一个性能上比较讲究的算法;有些时候我们可以在Python中使用已经有了的现成的被封闭在动态链接库中的函数

比如,下面这样一个用C实现的加法程序

int add(int n1, int n2){
    return n1 + n2;
}

如果在Windows下,需要写成下面这样

#include <windows.h>

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved){
    return TRUE;
}

__declspec(dllexport) int add(int n1, int n2){
    return n1 + n2;
}

Linux下用以下命令编译

gcc -c -fPIC libtest.c
gcc -shared libtest.o -o libtest.so

Windows下使用以下命令编译

cl -LD libtest.c -libtest.dll

然后在Python中如下使用(Windows的话将libtest.so改为libtest.dll即可)

[root@root test]# python
Python 2.6.6 (r266:84292, Sep  4 2013, 07:46:00) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes import *
>>> import os
>>> libtest = cdll.LoadLibrary(os.getcwd() + '/libtest.so')
>>> print libtest.add(100, 10)
110
>>> 

Boost::Python粘合C++和Python

Python已经提供了API,用于粘合Python和C。那Boost::Python又是什么?Boost::Python是Python/C API的一个封装(wrapper)。使用Python/C API,你需要在Python和C代码之间传递指针的前后,手动处理指针问题,比如指针不再指向原来的对象时,Boost::Python接管了这个任务,自动处理。此外,Boost::Python是我们能用C++的OOP方式,在Python对象上写操作

比如,对于Python代码y = object_x[i],同样的功能用Python/C API实现,需要这样写

y = PySequence_GetItem(object_x, i);

作为对比,用Boost::Python这样写就好了

y = object_x[i];

Boost::Python使得将我们的C++类输出为Python变得容易,甚至都不需要改变。Boost::Python的设计理念是:用户永远不需要接触PyObject*

以下的事例是在Mac OS平台的实验情况,Windows、Linux还未做研究!

下载、编译安装boost

在浏览器中输入链接http://jaist.dl.sourceforge.net/project/boost/boost/1.55.0/boost_1_55_0.tar.bz2下载boost

解压缩tar zxvf boost_1_55_0.tar.bz2,然后进入文件夹cd boost_1_55_0

运行shell脚本bash bootstrap.sh

如果在当前目录下(比如/Users/xumenger/Desktop/library/boost_1_55_0)运行b2进行编译,那么在对应目录下生成如下内容

image

也可以./b2 install进行安装,执行完毕后头文件在/usr/local/include下、库文件在/usr/local/lib下

C++导出函数

编写C++代码hello.cpp如下

#include <iostream>
#include <string>
#include <boost/python.hpp>

using namespace std;
using namespace boost::python;

void say()
{
   cout<<"Hello World!"<<endl;
}

BOOST_PYTHON_MODULE(hello)
{
   def("say", say);
}

g++使用第三方库编译是如下命令

g++ -fpic -c -L/Users/xumenger/Desktop/library/boost_1_55_0/ -I/usr/include/python2.7 hello.cpp
g++ -shared -L/usr/lib -L/Users/xumenger/Desktop/library/boost_1_55_0/stage/lib -lpython2.7 -lboost_python -o hello.so hello.o

编译结果如下

image

然后将/Users/xumenger/Desktop/library/boost_1_55_0/stage/lib/libboost_python.dylib拷贝到hello.so所在文件夹下

然后在hello.so所在目录启动Python,期间可能遇到ImportError的错误

image

参考unsafe use of relative rpath libboost.dylib when making boost.python helloword demo?,运行以下命令

install_name_tool -change libboost_python.dylib /usr/local/lib/libboost_python.dylib hello.so 
otool -L hello.so

然后再在Python命令下执行即可成功!

xumenger-mac:boost xumenger$ python
Python 2.7.10 (default, Feb  7 2017, 00:08:15) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>> hello.say()
Hello World!
>>> 

C++导出类

编写程序testclass.cpp如下

#include <iostream>
#include <string>
#include <boost/python.hpp>

using namespace std;
using namespace boost::python;
  
class person  
{  
public:  
    void setname(string str)  
    {  
        name_ = str;  
    }  
  
    string getname()  
    {  
        return name_;  
    }  
      
    void setage(int age)  
    {  
        age_ = age;  
    }  
      
    int getage()  
    {  
        return age_;  
    }  
      
private:  
    string name_;  
    int age_;  
  
};

BOOST_PYTHON_MODULE(testclass)  //python模块  
{  
    class_<person>("person")  
        .def("setname", &person::setname)  
        .def("getname", &person::getname)  
        .def("setage", &person::setage)  
        .def("getage", &person::getage)  
        .add_property("name", &person::getname, &person::setname)  
        .add_property("age", &person::getage, &person::setage)  
        ;  
}  

编译程序,得到testclass.so

g++ -fpic -c -L/Users/xumenger/Desktop/library/boost_1_55_0/ -I/usr/include/python2.7 testclass.cpp
g++ -shared -L/usr/lib -L/Users/xumenger/Desktop/library/boost_1_55_0/stage/lib -lpython2.7 -lboost_python -o testclass.so testclass.o

执行下面命令,防止ImportError

install_name_tool -change libboost_python.dylib /usr/local/lib/libboost_python.dylib testclass.so 
otool -L testclass.so

然后开启Python进行测试

xumenger-mac:boost xumenger$ python
Python 2.7.10 (default, Feb  7 2017, 00:08:15) 
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import testclass
>>> p = testclass.person()
>>> p.setname("xumenger")
>>> p.setage(24)
>>> print p.name
xumenger
>>> print p.age
24
>>> 

参考资料

主要参考

其他资料




如果本篇文章对您有所帮助,您可以通过微信(左)或支付宝(右)对作者进行打赏!


上一篇     下一篇