I want to use an event based Python library in a C application. I use the offical C-API for embedding Python: http://docs.python.org/2/c-api/index.html#c-api-index
It is no problem to call methods from C and collect return values. However, I don't know how to do the following:
Several of the python library functions take what I think is a python function pointer as an argument.
Is it possible, when calling these methods from C, to pass a C function pointer so the Python function uses a C function as callback?
If not, how to accomplish having Python use a C callback?
This is harder than one would expect, but it can be done.
If you have a single C function that you want to provide as a callback, you can use PyCFunction_New
to convert it into a Python callable:
#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);
This approach won't work if you want to choose different callbacks at run-time, e.g. to provide a generic c_to_python
function that converts a C callback function to a Python callback. In that case, you'll need to implement an extension type with its own 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;
}
This code is trivially extended for the to also accept a user_data
pointer; just store the user data in the CallbackObject
struct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With