安全地在 Python 和 C 之间传递指针

在 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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值