python调用c++程序的方法详解

前言

大家都知道python的优点是开发效率高,使用方便,c++则是运行效率高,这两者可以相辅相成,不管是在python项目中嵌入c++代码,或是在c++项目中用python实现外围功能,都可能遇到python调用c++模块的需求,下面列举出集中c++代码导出成python接口的几种基本方法,一起来学习学习吧。

原生态导出

python解释器就是用c实现,因此只要我们的c++的数据结构能让python认识,理论上就是可以被直接调用的。我们实现test1.cpp如下

#include
int add(int x, int y)
{
return x + y;
}
int del(int x, int y)
{
return x – y;
}
pyobject* wrappadd(pyobject* self, pyobject* args)
{
int x, y;
if (!pyarg_parsetuple(args, “ii”, &x, &y))
{
return null;
}
return py_buildvalue(“i”, add(x, y));
}
pyobject* wrappdel(pyobject* self, pyobject* args)
{
int x, y;
if (!pyarg_parsetuple(args, “ii”, &x, &y))
{
return null;
}
return py_buildvalue(“i”, del(x, y));
}
static pymethoddef test_methods[] = {
{“add”, wrappadd, meth_varargs, “something”},
{“del”, wrappdel, meth_varargs, “something”},
{null, null}
};
extern “c”
void inittest1()
{
py_initmodule(“test1”, test_methods);
}

编译命令如下

g++ -fpic -shared test1.cpp -i/usr/include/python2.6 -o test1.so

运行python解释器,测试如下

>>> import test1
>>> test1.add(1,2)
3

这里要注意一下几点

如果生成的动态库名字为test1,则源文件里必须有inittest1这个函数,且py_initmodule的第一个参数必须是“test1”,否则python导入模块会失败

如果是cpp源文件,inittest1函数必须用extern “c”修饰,如果是c源文件,则不需要。原因是python解释器在导入库时会寻找initxxx这样的函数,而c和c++对函数符号的编码方式不同,c++在对函数符号进行编码时会考虑函数长度和参数类型,具体可以通过nm test1.so查看函数符号,c++filt工具可通过符号反解出函数原型

通过boost实现

我们使用和上面同样的例子,实现test2.cpp如下

#include
#include
using namespace boost::python;
int add(const int x, const int y)
{
return x + y;
}
int del(const int x, const int y)
{
return x – y;
}
boost_python_module(test2)
{
def(“add”, add);
def(“del”, del);
}

其中boost_python_module的参数为要导出的模块名字

编译命令如下

g++ test2.cpp -fpic -shared -o test2.so -i/usr/include/python2.6 -i/usr/local/include -l/usr/local/lib -lboost_python

注意: 编译时需要指定boost头文件和库的路径,我这里分别是/usr/local/include和/usr/local/lib

或者通过setup.py导出模块

#!/usr/bin/env python
from distutils.core import setup
from distutils.extension import extension
setup(name=”packagename”,
ext_modules=[
extension(“test2”, [“test2.cpp”],
libraries = [“boost_python”])
])

extension的第一个参数为模块名,第二个参数为文件名

执行如下命令

python setup.py build

这时会生成build目录,找到里面的test2.so,并进入同一级目录,验证如下

>>> import test2
>>> test2.add(1,2)
3
>>> test2.del(1,2)
-1

导出类

test3.cpp实现如下

#include
using namespace boost::python;
class test
{
public:
int add(const int x, const int y)
{
return x + y;
}
int del(const int x, const int y)
{
return x – y;
}
};
boost_python_module(test3)
{
class_(“test”)
.def(“add”, &test::add)
.def(“del”, &test::del);
}

注意:boost_python_module里的.def使用方法有点类似python的语法,等同于

class_(“test”).def(“add”, &test::add);
class_(“test”).def(“del”, &test::del);

编译命令如下

g++ test3.cpp -fpic -shared -o test3.so -i/usr/include/python2.6 -i/usr/local/include/boost -l/usr/local/lib -lboost_python

测试如下

>>> import test3
>>> test = test3.test()
>>> test.add(1,2)
3
>>> test.del(1,2)
-1

导出变参函数

test4.cpp实现如下

#include
using namespace boost::python;
class test
{
public:
int add(const int x, const int y, const int z = 100)
{
return x + y + z;
}
};
int del(const int x, const int y, const int z = 100)
{
return x – y – z;
}
boost_python_member_function_overloads(add_member_overloads, add, 2, 3)
boost_python_function_overloads(del_overloads, del, 2, 3)
boost_python_module(test4)
{
class_(“test”)
.def(“add”, &test::add, add_member_overloads(args(“x”, “y”, “z”), “something”));
def(“del”, del, del_overloads(args(“x”, “y”, “z”), “something”));
}

这里add和del函数均采用了默认参数,del为普通函数,add为类成员函数,这里分别调用了不同的宏,宏的最后两个参数分别代表函数的最少参数个数和最多参数个数

编译命令如下

g++ test4.cpp -fpic -shared -o test4.so -i/usr/include/python2.6 -i/usr/local/include/boost -l/usr/local/lib -lboost_python

测试如下

>>> import test4
>>> test = test4.test()
>>> print test.add(1,2)
103
>>> print test.add(1,2,z=3)
6
>>> print test4.del(1,2)
-1
>>> print test4.del(1,2,z=3)
-1

导出带python对象的接口

既然是导出为python接口,调用者难免会使用python特有的数据结构,比如tuple,list,dict,由于原生态方法太麻烦,这里只记录boost的使用方法,假设要实现如下的python函数功能

def square(list_a)
{
return [x * x for x in list_a]
}

即对传入的list每个元素计算平方,返回list类型的结果

代码如下

#include
boost::python::list square(boost::python::list& data)
{
boost::python::list ret;
for (int i = 0; i < len(data); ++i) { ret.append(data[i] * data[i]); } return ret; } boost_python_module(test5) { def("square", square); }

编译命令如下

g++ test5.cpp -fpic -shared -o test5.so -i/usr/include/python2.6 -i/usr/local/include/boost -l/usr/local/lib -lboost_python

测试如下

>>> import test5
>>> test5.square([1,2,3])
[1, 4, 9]

boost实现了boost::python::tuple, boost::python::list, boost::python::dict这几个数据类型,使用方法基本和python保持一致,具体方法可以查看boost头文件里的boost/python/tuple.hpp及其它对应文件

另外比较常用的一个函数是boost::python::make_tuple() ,使用方法如下

boost::python::tuple(int a, int b, int c)
{
return boost::python::make_tuple(a, b, c);
}

更多python调用c++程序的方法详解相关文章请关注php中文网!

Posted in 未分类

发表评论