前言
大家都知道Python的優點是開發效率高,使用方便,C++則是運行效率高,這兩者可以相輔相成,不管是在Python項目中嵌入C++代碼,或是在C++項目中用Python實現外圍功能,都可能遇到Python調用C++模塊的需求,下面列舉出集中c++代碼導出成Python接口的幾種基本方法,一起來學習學習吧。
原生態導出
Python解釋器就是用C實現,因此只要我們的C++的數據結構能讓Python認識,理論上就是可以被直接調用的。我們實現test1.cpp如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#include <Python.h> 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); } |
編譯命令如下
1
|
g + + - fPIC - shared test1.cpp - I / usr / include / python2. 6 - o test1.so |
運行Python解釋器,測試如下
1
2
3
|
>>> 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如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <boost/python/module.hpp> #include <boost/python/def.hpp> 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的參數為要導出的模塊名字
編譯命令如下
1
|
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導出模塊
1
2
3
4
5
6
7
8
9
|
#!/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的第一個參數為模塊名,第二個參數為文件名
執行如下命令
1
|
python setup.py build |
這時會生成build目錄,找到里面的test2.so,并進入同一級目錄,驗證如下
1
2
3
4
5
|
>>> import test2 >>> test2.Add( 1 , 2 ) 3 >>> test2.Del( 1 , 2 ) - 1 |
導出類
test3.cpp實現如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <boost/python.hpp> 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>( "Test" ) . def ( "Add" , &Test::Add) . def ( "Del" , &Test::Del); } |
注意:BOOST_PYTHON_MODULE里的.def使用方法有點類似Python的語法,等同于
1
2
|
class_ <Test>( "Test" ). def ( "Add" , &Test::Add); class_ <Test>( "Test" ). def ( "Del" , &Test::Del); |
編譯命令如下
1
|
g + + test3.cpp - fPIC - shared - o test3.so - I / usr / include / python2. 6 - I / usr / local / include / boost - L / usr / local / lib - lboost_python |
測試如下
1
2
3
4
5
6
|
>>> import test3 >>> test = test3.Test() >>> test.Add( 1 , 2 ) 3 >>> test.Del( 1 , 2 ) - 1 |
導出變參函數
test4.cpp實現如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <boost/python.hpp> 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>( "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為類成員函數,這里分別調用了不同的宏,宏的最后兩個參數分別代表函數的最少參數個數和最多參數個數
編譯命令如下
1
|
g + + test4.cpp - fPIC - shared - o test4.so - I / usr / include / python2. 6 - I / usr / local / include / boost - L / usr / local / lib - lboost_python |
測試如下
1
2
3
4
5
6
7
8
9
10
|
>>> 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函數功能
1
2
3
4
|
def Square(list_a) { return [x * x for x in list_a] } |
即對傳入的list每個元素計算平方,返回list類型的結果
代碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#include <boost/python.hpp> 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); } |
編譯命令如下
1
|
g + + test5.cpp - fPIC - shared - o test5.so - I / usr / include / python2. 6 - I / usr / local / include / boost - L / usr / local / lib - lboost_python |
測試如下
1
2
3
|
>>> 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()
,使用方法如下
1
2
3
4
|
boost::python:: tuple ( int a, int b, int c) { return boost::python::make_tuple(a, b, c); } |
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
原文鏈接:http://littlewhite.us/archives/304