C++調用Python淺析


環境

VS2005Python2.5.4 Windows XP SP3

 

簡述

一般開發過游戲的都知道Lua和C++可以很好的結合在一起,取長補短,把Lua腳本當成類似動態鏈接庫來使用,很好的利用了腳本開發的靈活性。而作為一門流行的通用型腳本語言python,也是可以做到的。在一個C++應用程序中,我們可以用一組插件來實現一些具有統一接口的功能,一般插件都是使用動態鏈接庫實現,如果插件的變化比較頻繁,我們可以使用Python來代替動態鏈接庫形式的插件(堪稱文本形式的動態鏈接庫),這樣可以方便地根據需求的變化改寫腳本代碼,而不是必須重新編譯鏈接二進制的動態鏈接庫。靈活性大大的提高了。

 

Python/CAPI簡介

通過C++調用Python腳本主要要用到如下的一些Python提供的API,因為實際上C++要調用的是Python的解釋器,而Python解釋器本質就是實現在動態鏈接庫里面的,因此在調用前和調用后要進行一些初始化和資源釋放的工作,另外,要調用Python腳本里面的函數等等東西,需要Python提供的一些特殊API來包裝C++調用。(可以參考[2])。

 

void Py_Initialize(void)

初始化Python解釋器,如果初始化失敗,繼續下面的調用會出現各種錯誤,可惜的是此函數沒有返回值來判斷是否初始化成功,如果失敗會導致致命錯誤。

 

int Py_IsInitialized(void)

檢查是否已經進行了初始化,如果返回0,表示沒有進行過初始化。

 

void Py_Finalize()

反初始化Python解釋器,包括子解釋器,調用此函數同時會釋放Python解釋器所占用的資源。

 

int PyRun_SimpleString(const char *command)

實際上是一個宏,執行一段Python代碼。

 

PyObject* PyImport_ImportModule(char *name)

導入一個Python模塊,參數name可以是*.py文件的文件名。類似Python內建函數import。

 

PyObject* PyModule_GetDict( PyObject *module)

相當於Python模塊對象的__dict__屬性,得到模塊名稱空間下的字典對象。

 

PyObject* PyRun_String(const char* str, int start,PyObject* globals, PyObject* locals)

執行一段Python代碼。

 

int PyArg_Parse(PyObject* args, char* format, ...)

把Python數據類型解析為C的類型,這樣C程序中才可以使用Python里面的數據。

 

PyObject* PyObject_GetAttrString(PyObject *o, char*attr_name)

返回模塊對象o中的attr_name 屬性或函數,相當於Python中表達式語句,o.attr_name。

 

PyObject* Py_BuildValue(char* format, ...)

和PyArg_Parse剛好相反,構建一個參數列表,把C類型轉換為Python對象,使得Python里面可以使用C類型數據。

 

PyObject* PyEval_CallObject(PyObject* pfunc, PyObject*pargs)

此函數有兩個參數,而且都是Python對象指針,其中pfunc是要調用的Python 函數,一般說來可以使用PyObject_GetAttrString()獲得,pargs是函數的參數列表,通常是使用Py_BuildValue()來構建。

 

更多的API請參考官方的文檔,比較直觀簡單,譬如怎樣初始化一個類實例,怎樣調用類成員函數。下面上點代碼,感受下這個過程。

 

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系統前,必須使用Py_Initialize對其

              // 進行初始化。它會載入Python的內建模塊並添加系統路

              // 徑到模塊搜索路徑中。這個函數沒有返回值,檢查系統

              // 是否初始化成功需要使用Py_IsInitialized。

              Py_Initialize();

 

              // 檢查初始化是否成功

              if (!Py_IsInitialized())

              {

                     break;

              }

 

              // 添加當前路徑

              // 把輸入的字符串作為Python代碼直接運行,返回

              // 表示成功,-1表示有錯。大多時候錯誤都是因為字符串

              // 中有語法錯誤。

              PyRun_SimpleString("importsys");

              PyRun_SimpleString("sys.path.append('./')");

 

              // 載入名為PyPlugin的腳本

              pName = PyString_FromString("PyPlugin");

              pModule = PyImport_Import(pName);

              if (!pModule)

              {

                     printf("can't findPyPlugin.py\n");

                     break;

              }

 

              pDict = PyModule_GetDict(pModule);

              if (!pDict)

              {

                     break;

              }

 

              // 找出函數名為AddMult的函數

              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 = %d\n", a, b);

                     nRet = 0;

              }

 

              if (pArgs)

                     Py_DECREF(pArgs);

              if (pFunc)

                     Py_DECREF(pFunc);

              // 找出函數名為HelloWorld的函數

              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)

 

下面還有幾個比較重要的問題需要解決,且聽慢慢道來。

 

C++怎么向Python傳遞參數

C++向Python傳參數是以元組(tuple)的方式傳過去的,因此我們實際上就是構造一個合適的Python元組就可以了,要用到PyTuple_New,Py_BuildValue,PyTuple_SetItem等幾個函數,其中Py_BuildValue可以有其它一些的替換函數。

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

也可以直接使用PyObject* Py_BuildValue(char *format, ...) 函數來直接來構造tuple,此函數的使用也很簡單,記住一些轉換的格式常量即可輕松進行轉換(格式常量有點類似printf,參考[9])。譬如s 表示字符串,i表示整型變量,f表示浮點數,o表示一個Python對象等等。

Py_BuildValue("")                       None

Py_BuildValue("i",123)                 123

Py_BuildValue("iii",123, 456, 789)     (123, 456, 789)

Py_BuildValue("s","hello")             'hello'

Py_BuildValue("ss","hello", "world")    ('hello', 'world')

Py_BuildValue("s#","hello", 4)         'hell'

Py_BuildValue("()")                     ()

Py_BuildValue("(i)",123)               (123,)

Py_BuildValue("(ii)",123, 456)         (123, 456)

Py_BuildValue("(i,i)",123, 456)        (123, 456)

Py_BuildValue("[i,i]",123, 456)        [123, 456]

Py_BuildValue("{s:i,s:i}",

                 "abc", 123, "def", 456)    {'abc': 123, 'def': 456}

Py_BuildValue("((ii)(ii))(ii)",

                 1, 2, 3, 4, 5, 6)         (((1, 2), (3, 4)), (5, 6))

 

C++怎么轉換Python的返回值

Python傳回給C++的都是PyObject對象,因此可以調用Python里面的一些類型轉換API來把返回值轉換成C++里面的類型。類似PyInt_AsLong,PyFloat_AsDouble這些系列的函數。Python比較喜歡傳回一個元組,可以使用PyArg_ParseTuple這個函數來解析。這個函數也要用到上面的格式常量(參考[10])。還有一個比較通用的轉換函數是PyArg_Parse,也需要用到格式常量,夠不夠強大,用了就知道了。

 

直接調用Python腳本文件——另一種調用方式

初始化,反初始化都一樣,此種方式其實就是直接調用PyRun_SimpleString函數。

if(fp && PyRun_SimpleString("execfile('PyFile.py')") != 0)

       {

              fclose(fp);

              printf("PyRun_SimpleFile(%s)failed!", szFile);

              return -1;

}

 

還有一種方法是調用PyRun_SimpleFile()函數來直接運行一個Python文件,不過這種方式有點危險,因為這個API要求傳入一個FILE指針,而微軟的幾個CRT版本FILE指針的定義有了變化,因此傳入你使用VS2005編譯的FILE指針或者其它版本的FILE極有可能崩潰,如果你想安全調用,最好是自己把Python的源代碼使用和應用程序相同的環境一起編譯出lib來使用。

char szFile[] = "PyFile.py";

       FILE* fp = fopen(szFile, "r");

       if(fp && PyRun_SimpleFile(fp,szFile) != 0)

       {

              fclose(fp);

              printf("PyRun_SimpleFile(%s)failed!", szFile);

              return -1;

}

 

參考文獻

[1] python官網 http://www.python.org/

[2] python/c APIReference Manual http://docs.python.org/2/c-api/index.html

[3] 用C語言擴展python的功能 https://www.ibm.com/developerworks/cn/linux/l-pythc/

[4] C++擴展和嵌入Python http://www.vckbase.com/index.php/wv/1258

[5] Python調用C/C++模塊 http://blog.csdn.net/masefee/article/details/4750920

[6] Extendingand Embedding the Python Interpreter

http://docs.python.org/2/extending/index.html

[7] EmbeddingPython in Another Application

http://docs.python.org/2/extending/embedding.html

[8] C調用Python類/函數簡單代碼

http://www.360doc.com/content/12/0506/13/9369336_209021809.shtml

[9] The Py_BuildValue()Function

http://docs.python.org/release/1.5.2p2/ext/buildValue.html

[10] FormatStrings for PyArg_ParseTuple()

http://docs.python.org/release/1.5.2p2/ext/parseTuple.html

       [11]Python編程

http://wiki.woodpecker.org.cn/moin/PP3eD

 

 

http://blog.csdn.net/magictong/article/details/8947892


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM