深入浅析 C++ 调用 Python 模块
一般开发过游戏的都知道Lua和C++可以很好的结合在一起,取长补短,把Lua脚本当成类似动态链接库来使用,很好的利用了脚本开发的灵活性。而作为一门流行的通用型脚本语言Python,也是可以做到的。在一个C++应用程序中,我们可以用一组插件来实现一些具有统一接口的功能,一般插件都是使用动态链接库实现,如果插件的变化比较频繁,我们可以使用Python来代替动态链接库形式的插件(堪称文本形式的动态链接库),这样可以方便地根据需求的变化改写脚本代码,而不是必须重新编译链接二进制的动态链接库。灵活性大大的提高了。
作为一种胶水语言,Python能够很容易地调用C、C++等语言,也能够通过其他语言调用Python的模块。
Python提供了C++库,使得开发者能很方便地从C++程序中调用Python模块。
具体的文档参考官方指南:
EmbeddingPythoninAnotherApplication
调用方法
1链接到Python调用库
Python安装目录下已经包含头文件(include目录)和库文件(Windows下为python27.lib)。
使用之前需要链接到此库。
2直接调用Python语句
<codeclass="language-cpphljs">#include"python/Python.h" intmain() { Py_Initialize();##初始化 PyRun_SimpleString("print'hello'"); Py_Finalize();##释放资源 } </code>
3加载Python模块并调用函数
~/test目录下含有test.py:
<codeclass="language-pythonhljs">deftest_add(a,b): print'add',a,'and',b returna+b</code>
则可以通过以下代码调用test_add函数:
<codeclass="language-cpphljs">#include"python/Python.h" #include<iostream> usingnamespacestd; intmain() { Py_Initialize();//初始化 //将Python工作路径切换到待调用模块所在目录,一定要保证路径名的正确性 stringpath="~/test"; stringchdir_cmd=string("sys.path.append(\"")+path+"\")"; constchar*cstr_cmd=chdir_cmd.c_str(); PyRun_SimpleString("importsys"); PyRun_SimpleString(cstr_cmd); //加载模块 PyObject*moduleName=PyString_FromString("test");//模块名,不是文件名 PyObject*pModule=PyImport_Import(moduleName); if(!pModule)//加载模块失败 { cout<<"[ERROR]Pythongetmodulefailed."<<endl; return0; } cout<<"[INFO]Pythongetmodulesucceed."<<endl; //加载函数 PyObject*pv=PyObject_GetAttrString(pModule,"test_add"); if(!pv||!PyCallable_Check(pv))//验证是否加载成功 { cout<<"[ERROR]Can'tfindfunftion(test_add)"<<endl; return0; } cout<<"[INFO]Getfunction(test_add)succeed."<<endl; //设置参数 PyObject*args=PyTuple_New(2);//2个参数 PyObject*arg1=PyInt_FromLong(4);//参数一设为4 PyObject*arg2=PyInt_FromLong(3);//参数二设为3 PyTuple_SetItem(args,0,arg1); PyTuple_SetItem(args,1,arg2); //调用函数 PyObject*pRet=PyObject_CallObject(pv,args); //获取参数 if(pRet)//验证是否调用成功 { longresult=PyInt_AsLong(pRet); cout<<"result:"<<result; } Py_Finalize();##释放资源 return0; } </iostream></code>
参数传递
1C++向Python传递参数
Python的参数实际上是元组,因此传参实际上就是构造一个合适的元组。
常用的有两种方法:
使用PyTuple_New创建元组,PyTuple_SetItem设置元组值
<codeclass="language-cpphljs">PyObject*args=PyTuple_New(3); PyObject*arg1=Py_BuildValue("i",100);//整数参数 PyObject*arg2=Py_BuildValue("f",3.14);//浮点数参数 PyObject*arg3=Py_BuildValue("s","hello");//字符串参数 PyTuple_SetItem(args,0,arg1); PyTuple_SetItem(args,1,arg2); PyTuple_SetItem(args,2,arg3);</code>
直接使用Py_BuildValue构造元组
<codeclass="language-cpphljs">PyObject*args=Py_BuildValue("ifs",100,3.14,"hello"); PyObject*args=Py_BuildValue("()");//无参函数</code>
i,s,f之类的格式字符串可以参考格式字符串
2转换Python返回值
调用Python得到的都是PyObject对象,因此需要使用Python提供的库里面的一些函数将返回值转换为C++,例如PyInt_AsLong,PyFloat_AsDouble,PyString_AsString等。
还可以使用PyArg_ParseTuple函数来将返回值作为元组解析。
PyArg_Parse也是一个使用很方便的转换函数。
PyArg_ParseTuple和PyArg_Parse都使用格式字符串
注意事项
需要将Python的工作目录切换到模块所在路径按照模块名加载而不是文件名模块加载或者函数加载需要验证是否成功,否则可能会引起堆栈错误导致程序崩溃需要使用Py_DECREF(PyObject*)来解除对象的引用(以便Python垃圾回收)
以上所述是小编给大家介绍的C++调用Python模块的相关知识,希望对大家有所帮助!