Questo è più difficile di quanto ci si aspetterebbe, ma può essere fatto.
Se si dispone di una singola funzione C che si desidera fornire come un callback, è possibile utilizzare PyCFunction_New
per convertirlo in un 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);
Questo approccio non funziona se si desidera scegliere diversi callback in fase di esecuzione, ad es per fornire una funzione generica c_to_python
che converte una funzione di callback C in un callback Python. In tal caso, è necessario implementare un tipo di estensione con il proprio 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;
}
Questo codice è banalmente prorogato per il accettare anche un puntatore user_data
; è sufficiente memorizzare i dati utente nella struttura CallbackObject
.
Vedere http://stackoverflow.com/questions/6626167/build-a-pyobject-from-a-c-function (deve essere unito, fuori dai punti mod). – ecatmur
@ecatmur La risposta collegata non è corretta. 'methd' deve essere allocato staticamente perché' PyCFunction' mantiene un puntatore al 'PyMethodDef' fornito e lo usa in seguito per recuperare la funzione C ei flag. Il codice nella risposta auto-alloca il 'PyMethodDef', che causerà un arresto anomalo quando viene richiamata la funzione Python risultante. – user4815162342
@ user4815162342: Nella risposta collegata penso che Manux stava solo mostrando una panoramica del processo. È un po 'sciocco, IMO, che Manux usi il nome della funzione come nome del modulo invece di solo 'NULL'. Nel codice attuale presumo che il 'PyMethodDef' sia stato allocato sull'heap. – eryksun