C++調(diào)用Python淺析
環(huán)境
VS2005Python2.5.4 Windows XP SP3
?
簡(jiǎn)述
一般開(kāi)發(fā)過(guò)游戲的都知道Lua和C++可以很好的結(jié)合在一起,取長(zhǎng)補(bǔ)短,把Lua腳本當(dāng)成類似動(dòng)態(tài)鏈接庫(kù)來(lái)使用,很好的利用了腳本開(kāi)發(fā)的靈活性。而作為一門(mén)流行的通用型腳本語(yǔ)言python,也是可以做到的。在一個(gè)C++應(yīng)用程序中,我們可以用一組插件來(lái)實(shí)現(xiàn)一些具有統(tǒng)一接口的功能,一般插件都是使用動(dòng)態(tài)鏈接庫(kù)實(shí)現(xiàn),如果插件的變化比較頻繁,我們可以使用Python來(lái)代替動(dòng)態(tài)鏈接庫(kù)形式的插件(堪稱文本形式的動(dòng)態(tài)鏈接庫(kù)),這樣可以方便地根據(jù)需求的變化改寫(xiě)腳本代碼,而不是必須重新編譯鏈接二進(jìn)制的動(dòng)態(tài)鏈接庫(kù)。靈活性大大的提高了。
?
Python/CAPI簡(jiǎn)介
通過(guò)C++調(diào)用Python腳本主要要用到如下的一些Python提供的API,因?yàn)閷?shí)際上C++要調(diào)用的是Python的解釋器,而Python解釋器本質(zhì)就是實(shí)現(xiàn)在動(dòng)態(tài)鏈接庫(kù)里面的,因此在調(diào)用前和調(diào)用后要進(jìn)行一些初始化和資源釋放的工作,另外,要調(diào)用Python腳本里面的函數(shù)等等東西,需要Python提供的一些特殊API來(lái)包裝C++調(diào)用。(可以參考[2])。
?
void Py_Initialize(void)
初始化Python解釋器,如果初始化失敗,繼續(xù)下面的調(diào)用會(huì)出現(xiàn)各種錯(cuò)誤,可惜的是此函數(shù)沒(méi)有返回值來(lái)判斷是否初始化成功,如果失敗會(huì)導(dǎo)致致命錯(cuò)誤。
?
int Py_IsInitialized(void)
檢查是否已經(jīng)進(jìn)行了初始化,如果返回0,表示沒(méi)有進(jìn)行過(guò)初始化。
?
void?Py_Finalize()
反初始化Python解釋器,包括子解釋器,調(diào)用此函數(shù)同時(shí)會(huì)釋放Python解釋器所占用的資源。
?
int PyRun_SimpleString(const char *command)
實(shí)際上是一個(gè)宏,執(zhí)行一段Python代碼。
?
PyObject* PyImport_ImportModule(char *name)
導(dǎo)入一個(gè)Python模塊,參數(shù)name可以是*.py文件的文件名。類似Python內(nèi)建函數(shù)import。
?
PyObject* PyModule_GetDict( PyObject *module)
相當(dāng)于Python模塊對(duì)象的__dict__屬性,得到模塊名稱空間下的字典對(duì)象。
?
PyObject* PyRun_String(const char* str, int start,PyObject* globals, PyObject* locals)
執(zhí)行一段Python代碼。
?
int PyArg_Parse(PyObject* args, char* format, ...)
把Python數(shù)據(jù)類型解析為C的類型,這樣C程序中才可以使用Python里面的數(shù)據(jù)。
?
PyObject* PyObject_GetAttrString(PyObject *o, char*attr_name)
返回模塊對(duì)象o中的attr_name?屬性或函數(shù),相當(dāng)于Python中表達(dá)式語(yǔ)句,o.attr_name。
?
PyObject* Py_BuildValue(char* format, ...)
和PyArg_Parse剛好相反,構(gòu)建一個(gè)參數(shù)列表,把C類型轉(zhuǎn)換為Python對(duì)象,使得Python里面可以使用C類型數(shù)據(jù)。
?
PyObject* PyEval_CallObject(PyObject* pfunc, PyObject*pargs)
此函數(shù)有兩個(gè)參數(shù),而且都是Python對(duì)象指針,其中pfunc是要調(diào)用的Python?函數(shù),一般說(shuō)來(lái)可以使用PyObject_GetAttrString()獲得,pargs是函數(shù)的參數(shù)列表,通常是使用Py_BuildValue()來(lái)構(gòu)建。
?
更多的API請(qǐng)參考官方的文檔,比較直觀簡(jiǎn)單,譬如怎樣初始化一個(gè)類實(shí)例,怎樣調(diào)用類成員函數(shù)。下面上點(diǎn)代碼,感受下這個(gè)過(guò)程。
?
C++代碼
#include?"stdafx.h"
#include?"Python.h"
?
int?_tmain(int?argc,?_TCHAR*?argv[])
{
???????int?nRet?= -1;
?
???????PyObject*?pName?=?NULL;
???????PyObject*?pModule?=NULL;
???????PyObject*?pDict?=?NULL;
???????PyObject*?pFunc?=?NULL;
???????PyObject*?pArgs?=?NULL;
???????PyObject*?pRet?=?NULL;
???????do
???????{
??????????????//?初始化Python
??????????????//?在使用Python系統(tǒng)前,必須使用Py_Initialize對(duì)其
??????????????//?進(jìn)行初始化。它會(huì)載入Python的內(nèi)建模塊并添加系統(tǒng)路
??????????????//?徑到模塊搜索路徑中。這個(gè)函數(shù)沒(méi)有返回值,檢查系統(tǒng)
??????????????//?是否初始化成功需要使用Py_IsInitialized。
??????????????Py_Initialize();
?
??????????????//?檢查初始化是否成功
??????????????if?(!Py_IsInitialized())
??????????????{
?????????????????????break;
??????????????}
?
??????????????//?添加當(dāng)前路徑
??????????????//?把輸入的字符串作為Python代碼直接運(yùn)行,返回
??????????????//?表示成功,-1表示有錯(cuò)。大多時(shí)候錯(cuò)誤都是因?yàn)樽址?/p>
??????????????//?中有語(yǔ)法錯(cuò)誤。
??????????????PyRun_SimpleString("importsys");
??????????????PyRun_SimpleString("sys.path.append('./')");
?
??????????????//?載入名為PyPlugin的腳本
??????????????pName?=?PyString_FromString("PyPlugin");
??????????????pModule?=?PyImport_Import(pName);
??????????????if?(!pModule)
??????????????{
?????????????????????printf("can't findPyPlugin.pyn");
?????????????????????break;
??????????????}
?
??????????????pDict?=?PyModule_GetDict(pModule);
??????????????if?(!pDict)
??????????????{
?????????????????????break;
??????????????}
?
??????????????//?找出函數(shù)名為AddMult的函數(shù)
??????????????pFunc?=?PyDict_GetItemString(pDict,?"AddMult");
??????????????if?(!pFunc?|| !PyCallable_Check(pFunc))
??????????????{
?????????????????????printf("can't findfunction [AddMult]n");
?????????????????????break;
??????????????}
?
??????????????pArgs?=?Py_BuildValue("ii", 12, 14);
??????????????PyObject*?pRet?=?PyEval_CallObject(pFunc,pArgs);
??????????????int?a?= 0;
??????????????int?b?= 0;
??????????????if?(pRet?&&?PyArg_ParseTuple(pRet,"ii", &a,&b))
??????????????{
?????????????????????printf("Function[AddMult] call successful a + b = %d, a * b = %dn",?a,?b);
?????????????????????nRet?= 0;
??????????????}
?
??????????????if?(pArgs)
?????????????????????Py_DECREF(pArgs);
??????????????if?(pFunc)
?????????????????????Py_DECREF(pFunc);
??????????????//?找出函數(shù)名為HelloWorld的函數(shù)
??????????????pFunc?=?PyDict_GetItemString(pDict,?"HelloWorld");
??????????????if?(!pFunc?|| !PyCallable_Check(pFunc))
??????????????{
?????????????????????printf("can't findfunction [HelloWorld]n");
?????????????????????break;
??????????????}
??????????????pArgs?=?Py_BuildValue("(s)",?"magictong");
??????????????PyEval_CallObject(pFunc,pArgs);
???????}?while?(0);
??????
???????if?(pRet)
??????????????Py_DECREF(pRet);
???????if?(pArgs)
??????????????Py_DECREF(pArgs);
???????if?(pFunc)
??????????????Py_DECREF(pFunc);
???????if?(pDict)
??????????????Py_DECREF(pDict);
???????if?(pModule)
??????????????Py_DECREF(pModule);
???????if?(pName)
??????????????Py_DECREF(pName);
???????Py_Finalize();
?
???????return?0;
}
?
Python代碼
#!/usr/bin/python
import?string
?
class?CMyClass:
???????def?HelloWorld(self):
print?'HelloWorld'
?
class?SecondClass:
???????def?invoke(self,obj):
obj.HelloWorld()
?
def?HelloWorld(strName):
print?"Hello ",?strName
?
def?Add(a,?b,?c):
return?a?+?b?+?c
?
def?AddMult(a,?b):
"""
"""
print?"in FunctionAddMult..."
print?a
print?b
return?a?+?b,?a?*?b
?
def?StringToUpper(strSrc):
return?string.upper(strSrc)
?
下面還有幾個(gè)比較重要的問(wèn)題需要解決,且聽(tīng)慢慢道來(lái)。
?
C++怎么向Python傳遞參數(shù)
C++向Python傳參數(shù)是以元組(tuple)的方式傳過(guò)去的,因此我們實(shí)際上就是構(gòu)造一個(gè)合適的Python元組就可以了,要用到PyTuple_New,Py_BuildValue,PyTuple_SetItem等幾個(gè)函數(shù),其中Py_BuildValue可以有其它一些的替換函數(shù)。
PyObject*?pyParams?=?PyTuple_New(2);
???????PyObject*?pyParams1=?Py_BuildValue("i",5);
???????PyObject*?pyParams2=?Py_BuildValue("i",6);
???????PyTuple_SetItem(pyParams,0,?pyParams1);
???????PyTuple_SetItem(pyParams,1,?pyParams2);
???????pRet?=?PyEval_CallObject(pFunc,?pyParams);
<p style="color:rgb(57,57,57);font-family:Ve