我是Python / C API的新手...我正在尝试为我的C程序添加新功能,其中我可以将python嵌入其中并同时扩展功能,以便嵌入式解释器可以执行将与之交互的脚本作为我的C程序的一部分编写的扩展python模块。我的C程序没有全局变量。我想保持这种方式;同时为了向Cthon公开C功能,看来扩展C函数至少需要访问全局变量来访问程序的状态。我该如何解决这个问题?
例如这是我如何计划嵌入从main调用PYINTERFACE_Initialize的地方
void PYINTERFACE_Initialize(State *ptr, FILE *scriptFile, const char* scriptFileName)
{
Py_Initialize();
PyObject *module = Py_InitModule("CInterface", CInterfaceMethods);
if (PyRun_SimpleFileEx(scriptFile, scriptFileName, 1) != 0)
{
printf("PYINTERFACE script execution failed!\n");
}
**//ADD State *ptr TO module**
}
这是扩展功能:
static PyObject*
CInterface_GetStateInfo(PyObject *self, PyObject *args)
{
const char *printStr;
double stateInfo;
State *ptr;
if(!PyArg_ParseTuple(args, "s", &printStr))
{
return NULL;
}
printf("Received %s\n", printStr);
**//RETRIEVE State *ptr FROM self**
stateInfo = ptr->info;
return Py_BuildValue("d", currentTime);
}
这是通过State * ptr的最干净的方法吗?我当然不认为需要将内部状态暴露给python。我曾考虑使用胶囊,但似乎并不是胶囊的意图支持这种行为。
提前致谢!
V
Capsule基本上是python-opaque void指针,你可以传递或与模块关联。它们是解决问题的“方法”。
这是一个使用不必是静态的实例x的示例。首先将指针指向您的模块,如下所示(删除错误检查)...
// wrap the methods to be exposed to python in a module
// i.e. this is a list of method descriptions for the module
static PyMethodDef InitializeTurkeyMethods[] = {
// this block describes one method.. turkey.do_something()
{"do_something",
turkey_do_something, // fn pointer to wrap (defined below)
METH_VARARGS,
"do something .. return an int."},
{NULL, NULL, 0, NULL} // sentinel.
};
int init(X * x) {
// initialize embedded python scripting ..
// (this method a no-op on second or later calls).
Py_Initialize();
// initialize the turkey python module
PyObject * module = Py_InitModule("turkey", InitializeTurkeyMethods);
// Create a capsule containing the x pointer
PyObject * c_api_object = PyCapsule_New((void *)x, "turkey._X_C_API", NULL);
// and add it to the module
PyModule_AddObject(module, "_X_C_API", c_api_object);
}
然后在你要暴露给python的函数中,为了得到那个X指针,你做了这样的事情(这实际上必须在你开始在上面的代码中引用它之前):
static PyObject* turkey_do_something(PyObject *self, PyObject *args) {
if(!PyArg_ParseTuple(args, ":turkey_do_something"))
return NULL;
// get the x pointer back from the capsule
X * x = (X*)PyCapsule_Import("turkey._X_C_API", 0);
// call some fn on x
return Py_BuildValue("i", x->some_fn_that_returns_an_int());
}
这里“turkey._X_C_API”只是一些添加类型检查的名称 - 在这里为您的应用程序添加一些有意义的名称。土耳其是我刚才编写的演示模块名称。
现在假设,并且根据方式,你在调用Py_InitModule()时导出了turkey_do_something fn,你可以从python脚本中调用它:
import turkey
print turkey.do_something()
检查一下: http://docs.python.org/2/c-api/arg.html 关于如何格式化元组和这个.. http://docs.python.org/3.1/c-api/capsule.html 用于胶囊上的doco
假设你想要一些向后兼容性,你想使用PyCObjects:
http://docs.python.org/c-api/cobject.html
如果你只想使用python 3,你可以使用PyCapsule:
http://docs.python.org/c-api/capsule.html
基本上PyCObject的东西会将你的不透明指针转换成你可以在python中传递的PyObject,当你用一个返回C时,你可以打开并使用它。
PyCapsules非常相似,只是它们添加了一些功能,主要是它们
允许你在Capsule中存储多个指针,所以它基本上是一个dict。
在您添加指针的特定情况下,您只需要执行此操作(删除错误检查和销毁代码):
PyObject *pystate = PyCObject_FromVoidPtr(State, NULL);
PyObject *dict = PyModule_GetDict(module);
PyDict_SetItemString(dict, "CStateObject", pystate);
# To retrieve it from self (assuming that it is an object)
PyObject *pystate = PyObject_GetAttrString(self, "CStateObject");
State *state = (State *)PyCObject_AsVoidPtr(pystate);