Python与c++的相互调用(一)

来源:互联网 发布:沈阳盘古网络实施顾问 编辑:程序博客网 时间:2024/06/06 16:42

    最近公司项目使用python,个人突然对游戏引擎中python与c++之间的交互产生了兴趣。正赶上朋友要我帮忙做个五行相生相克的演示程序,于是想到写个hge的python导出库,顺便熟悉下python的c api。特写下此文,记录python与C++相互调用的要点,以便日后查阅。

准备工作

        首先是配置开发环境了,可以选择编译python源代码,或者直接使用python的安装包。如果想要发布出去,在无python环境的用户机上直接运行,建议直接编译源代码。

下面是我们熟悉的过程,在VS中配置好python的路径、代码中include python的头文件、以及将lib引入工程。

如果你不是编译的源代码,在debug下,链接时会找不到pythonxx_d.lib。简单的解决办法是:

python的安装目录下 include/pyconfig.h368行(我使用的是python2.7)。将定义Py_DEBUG的宏注释掉:

/*

#ifdef _DEBUG

#define Py_DEBUG

#endif

*/

           再到326行将python27_d.lib改为python27.lib

ifdef _DEBUG

#pragma comment(lib,"python27.lib")

#elif

#pragma comment(lib,"python27.lib")

#endif /* _DEBUG */

这样就可以通过链接。

 

1. python调用C++函数

一. Python解释器的开启与关闭

        本文是这个系列第一篇、主要是讲述如何将C++中定义的函数导出到python中调用。

        首先在程序中先启动python的解释器:

                    Py_Initialize();

        这个函数要在调用其他任何python API函数之前调用。

        对应的,在程序结束的时候,需要关闭:

                    Py_Finalize();        

 

二. C++函数的导出

        下面说明如何导出C++的函数到py本文的最后会提到如何将函数注册到module中,以及如何在Py中调用C++导出的函数。

static PyObject*  Function(PyObject *pSelf, PyObject *pParams)

{

.......

}

        所有函数都以这种形式导出。由于不是某类型的成员函数,可以不理会第一个参数pSelf。第二个参数pParams是从py中传进来的参数。这里解释个概念,PyObject是一个结构体,它代表了Python相关的数据。实际上,在PythonAPI中任何东西都可用PyObject结构体来表示,如:整型、字符串、函数或者整个脚本。PythonC++之间数据解析的关键就是搞清楚PyObject,即:如何将PyObject解析成C++类型,传给C++函数(传参)。如何将C++类型解析成PyObject,用以返回(传给python的返回值)

       给个简单的例子:

 static PyObject* Print(PyObject *pSelf, PyObject *pParams)

{

       printf("hello my python world");

       Py_INCREF(Py_None);

       return Py_None;

}

        这就是个最简单、无参数的函数。

        下面个例子可以了解如何解析从py传来的参数,以及返回值。

static PyObject* Add(PyObject *pSelf, PyObject *pParams)

{

float x, y;

if (!PyArg_ParseTuple(pParams, "ff", &x, &y))

{

return NULL;

}

float result = x + y;

return PyFloat_FromDouble(result);

}

        解释下这个函数PyArg_ParseTuple。Py的参数传递,其实是以tuple的形式进行的。pParams实际上是个Python  Tuple Object的指针,这个tuple的每个元素对应于py函数的参数PyArg_ParseTuple函数可以将一个Tuple类型,用代表格式的字符串解析成C++类型。见上例,其中pParams就是从PY传进来的参数,"ff"是代表格式的字符串,f代表浮点型。x, y是返回值。好了,解析一个py传进来的参数就是这么简单。可能有人会问,我怎么知道他传几个参数、以及参数的类型?别忘了,这个函数是你提供给py脚本程序员的,参数个数是你自己定的。

    返回值PyFloat_FromDouble是用double创建了一个指向PyFloat类型的指针,内容是函数计算的结果。这里也可以使用另一个函数Py_BuildValue("f", result);第一个参数也是代表格式的字符串。

    下面罗列一下,各常用字符串所代表的类型。

s s# z z# b h i l(小写的L) c f d D O O! O& S (items) | : ; 

三. 将函数注册到python的module中,导出module到python

      文档中将要导出到python的函数称作 host api。

      首先,我们需要将要导出函数的名字、函数地址等信息罗列到PyMethodDef类型的数组中,如:

static PyMethodDef Methods[]=

 {

      {"print", Print,METH_NOARGS,    "print hello to python"},

      {"add", Add,METH_VARARGS,    "add two float."},

      {NULL,NULL,0,NULL} 
 };

PyMethodDef成员分别是:导出到python的函数名,函数指针, 参数形式(常用的就这两种:METH_NOARGS代表没有参数。METH_VARARGS:代表有参数),函数的doc,在py中可以通过__doc__打印出来。最后一行的NULL是结束标记。

     下面要注册module到python了。    

if  (!PyImport_AddModule("Sample"))
{
     cout<<"Host API module could not be created."<<endl;
}

PyObject* module = Py_InitModule("Sample", Methods);
if (!module)
{
     cout<<"Host Api functions could not be initialized."<<endl;
     return;
}

     函数PyImport_AddModule创建了一个module, Py_InitModule在init这个module的时候,将定义host api的数组传进去。这样就可以了

四. python中调用这些函数

import Sample

Sample.print()

result = Sample.add(1, 2)

print result

五. 具体例子

      往往简单的例子不能让人快速的将技术使用到项目中,下面给出,我做HGE导出的一段函数。

C++:

static PyObject* HGE_System_Effect_Load(PyObject *pSelf, PyObject *pParams)
{
     char* effectPath;
     if (!PyArg_ParseTuple(pParams, "s", &effectPath))
     {
         return NULL;
     }
     DWORD effectHandle = g_hge->Effect_Load(effectPath);
     return PyInt_FromLong(effectHandle);
}

.......

static PyMethodDef HostAPIFuncs[] =
{

.......
{"Effect", HGE_System_Effect_Load, METH_VARARGS, NULL},

.......
{NULL, NULL, NULL, NULL}

};

 

........

if  (!PyImport_AddModule("Hge"))
 {
         cout<<"Host API module could not be created."<<endl;
 }

 PyObject* module = Py_InitModule("Hge", HostAPIFuncs);

.........

Python:

import Hge

eff = Hge.Effect(".....")

这个函数实际上就是将hge的 Effect_Load函数导出到py中调用。

 

 下一篇,将介绍如何导出C++定义的类型、以及成员函数和属性