2012-11-23 14 views
5

Desidero utilizzare una libreria Python basata su eventi in un'applicazione C. Uso la C-API ufficiale per incorporare Python: http://docs.python.org/2/c-api/index.html#c-api-indexIncorporamento Python con C

Non è un problema chiamare i metodi da C e raccogliere i valori di ritorno. Tuttavia, non so come fare quanto segue:

Molte delle funzioni della libreria Python prendono come argomento un puntatore a funzioni di Python.

E 'possibile, quando si richiamano questi metodi da C, passare un puntatore a funzione C in modo che la funzione Python utilizzi una funzione C come callback?

In caso negativo, come ottenere che Python usi un callback in C?

+0

Vedere http://stackoverflow.com/questions/6626167/build-a-pyobject-from-a-c-function (deve essere unito, fuori dai punti mod). – ecatmur

+1

@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

+0

@ 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

risposta

9

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.

+0

Avendo una rapida occhiata, sembra boost :: python fa così per avvolgere le funzioni C. – neodelphi

+0

@neodelphi Ha senso che lo faccia, ma la domanda è stata taggata con C, quindi 'boost :: python' non si applica realmente. BTW è 'boost :: python' attivamente mantenuto? La documentazione sembrava piuttosto vecchia quando l'ho controllata. – user4815162342

+0

Hai ragione. Ho appena trovato questo post perché sto cercando di implementare una libreria boost :: python like perché non è stata aggiornata da molti anni. Non ho trovato alcuna altra soluzione e come vedo boost :: python fa in questo modo, questo sembra essere il modo di fare. – neodelphi

Problemi correlati