python与c的集成
记得在大学里和同学一起进行游戏开发,可到了后来完全无法继续下去,现在想想原因,一是自己的水平有限,另一个就是没做到游戏引擎与数据的分离,也就是没有理解脚本。那时的我根本就不知道什么叫做脚本编程,现在随着工作的深入也渐渐理解了一点。虽然脚本语言有很多种,但是我毫不犹豫了选择了python,我觉得它真的是一个好东西,但是我用到的只是它最基本的东西,毕竟我只是用它来进行游戏方面的脚本编程。所以我最先关心的就是怎么将它与c集成,即可以与系统引擎进行交互。(主要参考图书《游戏脚本高级编程》)
1. 首先就是在编译器中把python安装目录include/与libs/加入,对于这点我在vc6中可以,但是在dev c++中即使加入了编译也会出错,说找不到python头文件,这点比较郁闷,不过考虑到一般windows编程都用的是vc,所以并没有什么影响吧!!!
然后用#include <Python.h>就可以把python的主头文件包含进来了。
但是在调试的时候,会出现说找不到python25_d.lib的链接错误,出现这个错误的原因是python_d.lib是库的调试后形式,当我们以debug模式编译工程时,python就用这个lib文件,但是这个文件是不可用的。对于这点,最快的办法就是强制要求python在任何情况下都是用非调试版本,就可以了。要做到这一点
a) 在python目录include文件夹下,打开pyconfig.h,找到如下语句
# ifdef _DEBUG
# pragma comment(lib,"python25_d.lib")
# else
# pragma comment(lib,"python25.lib")
# endif
将python25_d.lib改成python25.lib
b) 找到
#ifdef _DEBUG
# define Py_DEBUG
#endif
将其用屏蔽
这样就可以了。
2. Python初始化
python的初始化很简单我们只需在程序开始时调用Py_Initialize(),结束的时候调用Py_Finalize()就可以了
3. PyObject
在c语言程序中,我们使用PyObject来操纵python对象,一个PyObejct就是一个结构体,他表示某个与python相关的数据。只不过我们处理的都是该对象的指针。
PyObject *t; //t is a pointer to the python object
4. Reference counting
对于python,跟directx比较类似,都采用了一种引用计数的机制,我们不需要自己去释放python对象,只需减少它的引用计数就可以了,当计数为0时,系统自己会安全释放。
对于减少引用计数,我们使用Py_XDECREF(),而增加引用计数则是由代码负责完成。
5. 载入脚本
我们首先写一个python脚本,命名为test.py
printf “Hello World”
然后我们在主程序中调用,如下
PyObject *pName = PyString_FromString("test");
PyObject * pModule = PyImport_Import(pName);
if(!pModule)
{
printf("could not open script \n");
return 0;
}
这样就可以在控制台上面看见Hello World了。
6. 调用脚本定义函数
我们在test.py中定义一个函数,
def GetMax(X, Y):
print "\tGet Max was called from the host with",X,"and",Y
if X > Y:
return X
else:
return Y
对于python,它提供了一个模块词典的概念,一个模块词典就是一个数据结构,负责将脚本中的标识符分别映射到与它们对应的代码或是数据上,如果在c程序中我们要调用getMax函数,我们就需要用脚本词典去获得一个包含有函数GetMax的python对象。如下:
PyObject *pName = PyString_FromString("test");
PyObject * pModule = PyImport_Import(pName);
if(!pModule)
{
printf("could not open script \n");
return 0;
}
PyObject *pDict = PyModule_GetDict(pModule);
然后我们获得GetMax函数
PyObject *pFunc = PyDict_GetItemString(pDict, "GetMax");
在我们向该函数中传递两个参数,python使用是tuple结构来传递参数
PyObject *pParams = PyTuple_New(2);
PyObject *pCurrParam;
pCurrParam = PyInt_FromLong(16);
PyTuple_SetItem(pParams, 0, pCurrParam);
pCurrParam = PyInt_FromLong(32);
PyTuple_SetItem(pParams, 1, pCurrParam);
我们向GetMax函数传递了16与32,然后就要调用函数并输出返回值了,如下
PyObject *pMax = PyObject_CallObject(pFunc, pParams);
int max = PyInt_AsLong(pMax);
这样max就为32了
7. Python调用c语言函数
同样我们可以用python调用c语言函数,这样就能做到真正的交互了。首先定义一个c语言函数。
PyObject * RepeatString (PyObject *pSelf, PyObject *pParams)
{
printf("\tRepeatString was called from:\n");
char *pstrString;
int iRepCount;
if(!PyArg_ParseTuple(pParams, "si", &pstrString, &iRepCount))
{
printf("uable to parse parameter tuple.\n");
exit(0);
}
for (int i = 0; i < iRepCount; i++)
{
printf("\t\t%d: %s \n", i, pstrString);
}
return PyInt_FromLong(iRepCount);
}
该函数接受两个参数,pSelf没有什么用处,而另一个pParams则是用来传递参数的。由于传递参数都是用tuple结构,我们就使用PyArg_ParseTuple这个函数,对于”si”,表示的是按照字符串与整数来读取参数。
然后我们在主应用程序中定义一个api来存放该函数
PyMethodDef HostAPIFuncs [] =
{
{"RepeatString", RepeatString, METH_VARARGS, NULL},
{NULL, NULL, NULL, NULL}
};
对于这个数组的最后一行NULL来说,表明该数组结束。
接下来我们创建一个module,用来供脚本调用
if (!PyImport_AddModule("HostAPI"))
{
printf("Host API module could not be created");
}
然后将我们定义的函数表加入这个模块
if (!Py_InitModule("HostAPI", HostAPIFuncs))
{
printf("Host API module could not be initialized");
}
创建一个python脚本test.py,然后在第一行写上
import HostAPI
再定义一个函数用来调用HostAPI函数
def PrintStuff():
RepCount = HostAPI.RepeatString("String repetition", 4)
最后我们再在主程序中调用PrintStuff函数,就可以了
总体程序如下:
//create a module
if (!PyImport_AddModule("HostAPI"))
{
printf("Host API module could not be created");
}
//create a function table
PyMethodDef HostAPIFuncs [] =
{
{"RepeatString", RepeatString, METH_VARARGS, NULL},
{NULL, NULL, NULL, NULL}
};
//initial the module with the function table
if (!Py_InitModule("HostAPI", HostAPIFuncs))
{
printf("Host API module could not be initialized");
}
//load a python script
PyObject *pName = PyString_FromString("test");
PyObject * pModule = PyImport_Import(pName);
if(!pModule)
{
printf("could not open script \n");
return 0;
}
//get the module dict
PyObject * pDict = PyModule_GetDict ( pModule );
//get the function with the dict
PyObject * pFunc = PyDict_GetItemString ( pDict, "PrintStuff" );
//call the function
PyObject_CallObject ( pFunc, NULL );
Py_XDECREF ( pFunc );
Py_XDECREF ( pDict );
Py_XDECREF(pModule);
Py_XDECREF(pName);
这样就完成了python与c的集成,不过这只是一些简单的应用,具体还是要参考python manual。(以上代码来自于《游戏脚本高级编程》)