首頁  >  文章  >  後端開發  >  Python呼叫C++程式的方法詳解

Python呼叫C++程式的方法詳解

高洛峰
高洛峰原創
2017-02-20 10:28:281412瀏覽

前言

大家都知道Python的優點是開發效率高,使用方便,C++則是運作效率高,這兩者可以相輔相成,不管是在Python專案中嵌入C++程式碼,或是在C++專案中用Python實現外圍功能,都可能遇到Python呼叫C++模組的需求,以下列舉出集中c++程式碼導出成Python介面的幾種基本方法,一起來學習學習吧。

原生態導出

Python解釋器就是用C實現,因此只要我們的C++的資料結構能讓Python認識,理論上就是可以直接呼叫的。我們實作test1.cpp如下

#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);
}

編譯指令如下

g++ -fPIC -shared test1.cpp -I/usr/include/python2.6 -o test1.so

運行Python

如果產生的動態函式庫名字為test1,則來源檔案裡必須有inittest1這個函數,且Py_InitModule的第一個參數必須是“test1”,否則Python導入模組會失敗

如果是cpp來源文件,inittest1函數必須用extern "C"修飾,如果是c源文件,則不需要。原因是Python解釋器在導入函式庫時會尋找initxxx這樣的函數,而C和C++對函數符號的編碼方式不同,C++在對函數符號進行編碼時會考慮函數長度和參數類型,具體可以透過nm test1 .so

查看函數符號,c++filt工具可透過符號反解出函數原型
  1. 透過boost實作

  2. 我們使用和上面相同的例子,實作如下圖2.c
>>> import test1
>>> test1.Add(1,2)
3

其中BOOST_PYTHON_MODULE的參數為要匯出的模組名字編譯指令如下

#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);
}

usr/local/include和/usr/local/lib

或透過setup.py導出模組

g++ test2.cpp -fPIC -shared -o test2.so -I/usr/include/python2.6 -I/usr/local/include -L/usr/local/lib -lboost_python

Extension的第一個參數為模組名,第二個參數為檔案名稱

Extension的第一個參數為模組名,第二個參數為檔案名稱

#!/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"])
 ])

這時會產生build目錄,找到裡面的test2.so,並進入同一級目錄,驗證如下

python setup.py build

注意:

BOOST_PYTHON_MODULE裡的.def使用方法有點類似Python的語法,等同於

>>> import test2
>>> test2.Add(1,2)
3
>>> test2.Del(1,2)
-1

#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);
}


導出變參函數

test4.cpp實作如下

class_<Test>("Test").def("Add", &Test::Add);
class_<Test>("Test").def("Del", &Test::Del);

這裡Add和Del函數都採用了預設參數,Del函數為普通函數,Add為類別成員,這裡分別是了不同函數的宏,宏的最後兩個參數分別代表函數的最小參數個數和最多參數個數

編譯指令如下

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
介面

既然是導出為Python接口,呼叫者難免會使用Python特有的資料結構,例如tuple,list,dict,由於原生態方法太麻煩,這裡只記錄boost的使用方法,假設要實現如下的Python函數功能

#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"));
}

即對傳入的list每個元素計算平方,返回list類型的結果

程式碼如下

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

如下

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

boost實作了

boost::python::tuple

,

boost::python::list

,

boost::pythondicdicboost::python::list, boost::pythondicdicboost::python::list, boost::pythondicdic Python保持一致,具體方法可以查看boost頭檔裡的boost/python/tuple.hpp及其它對應檔

另外比較常用的一個函數是

boost::python::make_tuple()

,使用方法如下

#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);
}

更多Python呼叫C++程式的方法詳解相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn