在 Python/C API 中传递一个 C 指针,以扩展 C 程序的功能,在 C 程序中嵌入 Python 并同时扩展功能,以便嵌入的解释器可以执行一个脚本,该脚本将与作为 C 程序的一部分编写的扩展 Python 模块进行交互。然而,C 程序没有全局变量并且不能有全局变量。为了向 Python 公开 C 功能,扩展的 C 函数至少需要访问全局变量以访问程序的状态。那么,如何解决如何在没有全局变量的情况下向 Python 公开 C 功能?
2、解决方案:
方法一:使用胶囊(Capsules)
胶囊(Capsules)是基本上是 Python 不透明的 void 指针,可以传递或与模块相关联。它们是解决这个问题的“方法”。
首先,将指针附加到模块中,如下所示:
// 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”只是一个用于添加类型检查的名称,可以通过编写一个有意义的名称来代替。Turkey 是一个虚拟模块名称,可以根据需要进行修改。
假设已经导出了 turkey_do_something 函数,那么可以从以下 Python 脚本中使用它:
import turkey
print turkey.do_something()
方法二:使用 PyCObject
如果想要向后兼容,则可以使用 PyCObject:
http://docs.python.org/c-api/cobject.html
如果只想使用 Python 3,则可以使用 PyCapsule:
http://docs.python.org/c-api/capsule.html
基本上,PyCObject 将不透明指针转换为可以在 Python 中传递的 PyObject,当在 C 中以某个 PyObject 返回时,可以取消包装并使用它。PyCapsules 非常相似,只是它添加了一些特性,主要的一个是它允许在 Capsule 中存储多个指针,因此它基本上是一个字典。
在上面的特定情况下,需要做的就是:
PyObject *pystate = PyCObject_FromVoidPtr(State, NULL);
PyObject *dict = PyModule_GetDict(module);
PyDict_SetItemString(dict, "CStateObject", pystate);
如果想从 self 中检索它(假设 self 是一个对象),则需要这样做:
PyObject *pystate = PyObject_GetAttrString(self, "CStateObject");
State *state = (State *)PyCObject_AsVoidPtr(pystate);
文章评论