我想在C应用程序中使用基于事件的Python库。我使用官方C-API嵌入Python:http://docs.python.org/2/c-api/index.html#c-api-index
从C调用方法并收集返回值没有问题。但是,我不知道如何做到以下几点:
一些python库函数采用我认为是python函数指针作为参数。
从C调用这些方法时,是否可以传递C函数指针,以便Python函数使用C函数作为回调?
如果没有,如何让Python使用C回调?
答案 0 :(得分:9)
这比人们预期的要难,但可以做到。
如果您想要提供一个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, /* or METH_O, METH_NOARGS, etc. */
NULL
};
static PyObject *py_callback;
...
py_callback = PyCFunction_New(&callback_descr, NULL);
如果您想在运行时选择不同的回调,则此方法无效,例如提供将C回调函数转换为Python回调的通用c_to_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, /*ob_size*/
"Callback", /*tp_name*/
sizeof(CallbackObject), /*tp_basicsize*/
0, /*tp_itemsize*/
0, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
(ternaryfunc) callback_call, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
};
PyObject *
c_to_python(PyObject (*callback)(PyObject *, PyObject *))
{
CallbackObject *pycallback = PyObject_New(CallbackObject, &CallbackType);
if (pycallback)
pycallback->callback = callback;
return pycallback;
}
这个代码被简单地扩展,以便也接受user_data
指针;只需将用户数据存储在CallbackObject
结构中。