这比人们预想的要困难,但是它可以完成。
如果您有一个希望作为回调提供的单个C函数,您可以使用PyCFunction_New
将其转换为Python可调用函数:
#include <python.h>
static PyObject *my_callback(PyObject *ignore, PyObject *args)
{
}
static struct PyMethodDef callback_descr = {
"function_name",
(PyCFunction) my_callback,
METH_VARARGS,
NULL
};
static PyObject *py_callback;
...
py_callback = PyCFunction_New(&callback_descr, NULL);
如果您想在运行时选择不同的回调函数,例如提供一个通用的c_to_python函数将C回调函数转换为Python回调函数,则此方法将无法起作用。 在这种情况下,您需要实现一个带有自己的tp_call
的扩展类型。
typedef struct {
PyObject_HEAD
static PyObject (*callback)(PyObject *, PyObject *);
} CallbackObject;
static PyObject *
callback_call(CallbackObject *self, PyObject *args, PyObject *kwds)
{
return self->callback(args, kwds);
}
static PyTypeObject CallbackType = {
PyObject_HEAD_INIT(NULL)
0,
"Callback",
sizeof(CallbackObject),
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
(ternaryfunc) callback_call,
0,
0,
0,
0,
Py_TPFLAGS_DEFAULT,
};
PyObject *
c_to_python(PyObject (*callback)(PyObject *, PyObject *))
{
CallbackObject *pycallback = PyObject_New(CallbackObject, &CallbackType);
if (pycallback)
pycallback->callback = callback;
return pycallback;
}
这段代码可以轻松地进行扩展,以便还能够接受一个user_data
指针;只需将用户数据存储在CallbackObject
结构体中即可。
methd
必须是静态分配的,因为PyCFunction
保留了对提供的PyMethodDef
的指针,并在稍后使用它来检索C函数和标志。答案中的代码自动分配了PyMethodDef
,这将导致调用生成的Python函数时崩溃。 - user4815162342NULL
有点傻。在实际代码中,我假设PyMethodDef
是在堆上分配的。 - Eryk SunPyMethodDef
。这引出了后续问题,即动态分配的PyMethodDef
何时以及如何释放。我的答案,在一些初始投资的基础上,可以很好地解决这个问题。 - user4815162342