C++调用Python浅析环境VS2005Python2.5.4WindowsXPSP3简述一般开发过游戏的都知道Lua和C++可以很好的结合在一起,取长补短,把Lua脚本当成类似动态链接库来使用,很好的利用了脚本开发的灵活性。而作为一门流行的通用型脚本语言Python,也是可以做到的。在一个C++应用程序中,我们可以用一组插件来实现一些具有统一接口的功能,一般插件都是使用动态链接库实现,如果插件的变化比较频繁,我们可以使用Python来代替动态链接库形式的插件(堪称文本形式的动态链接库),这样可以方便地根据需求的变化改写脚本代码,而不是必须重新编译链接二进制的动态链接库。灵活性大大的提高了。Python/CAPI简介通过C++调用Python脚本主要要用到如下的一些Python提供的API,因为实际上C++要调用的是Python的解释器,而Python解释器本质就是实现在动态链接库里面的,因此在调用前和调用后要进行一些初始化和资源释放的工作,另外,要调用Python脚本里面的函数等等东西,需要Python提供的一些特殊API来包装C++调用。(可以参考[2])。voidPy_Initialize(void)初始化Python解释器,如果初始化失败,继续下面的调用会出现各种错误,可惜的是此函数没有返回值来判断是否初始化成功,如果失败会导致致命错误。intPy_IsInitialized(void)检查是否已经进行了初始化,如果返回0,表示没有进行过初始化。voidPy_Finalize()反初始化Python解释器,包括子解释器,调用此函数同时会释放Python解释器所占用的资源。intPyRun_SimpleString(constchar*command)实际上是一个宏,执行一段Python代码。PyObject*PyImport_ImportModule(char*name)导入一个Python模块,参数name可以是*.py文件的文件名。类似Python内建函数import。PyObject*PyModule_GetDict(PyObject*module)相当于Python模块对象的__dict__属性,得到模块名称空间下的字典对象。PyObject*PyRun_String(constchar*str,intstart,PyObject*globals,PyObject*locals)执行一段Python代码。intPyArg_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++代码#includestdafx.h#includePython.hint_tmain(intargc,_TCHAR*argv[]){intnRet=-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'tfindPyPlugin.py\n);break;}pDict=PyModule_GetDict(pModule);if(!pDict){break;}//找出函数名为AddMult的函数pFunc=PyDict_GetItemString(pDict,AddMult);if(!pFunc||!PyCallable_Check(pFunc)){printf(can'tfindfunction[AddMult]\n);break;}pArgs=Py_BuildValue(ii,12,14);PyObject*pRet=PyEval_CallObject(pFunc,pArgs);inta=0;intb=0;if(pRet&&PyArg_ParseTuple(pRet,ii,&a,&b)){printf(Function[AddMult]callsuccessfula+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'tfindfunction[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();return0;}Python代码#!/usr/bin/pythonimportstringclassCMyClass:defHelloWorld(self):print'HelloWorld'classSecondClass:definvoke(self,obj):obj.HelloWorld()defHelloWorld(strName):printHello,strNamedefAdd(a,b,c):returna+b+cdefAddMult(a,b):printinFunctionAddMult...printaprintbreturna+b,a*bdefStringToUpper(strSrc):returnstring.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()NonePy_BuildValue(i,123)123Py_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极有可能崩溃,如