2012-11-23 11 views
5

Ich möchte eine ereignisbasierte Python-Bibliothek in einer C-Anwendung verwenden. Ich benutze die offizielle C-API für die Einbettung von Python: http://docs.python.org/2/c-api/index.html#c-api-indexEinbetten von Python mit C

Es ist kein Problem, Methoden von C aufrufen und Rückgabewerte sammeln. Allerdings kann ich Folgendes nicht tun:

Einige der Python-Bibliothek Funktionen nehmen, was ich denke, ist ein Python-Funktionszeiger als Argument.

Ist es beim Aufruf dieser Methoden aus C möglich, einen C-Funktionszeiger zu übergeben, sodass die Python-Funktion eine C-Funktion als Callback verwendet?

Wenn nicht, wie erreicht man, dass Python einen C-Callback verwendet?

+0

Siehe http://stackoverflow.com/questions/6626167/build-a-pyobject-from-a-c-function (muss zusammengeführt werden; out of mod points). – ecatmur

+1

@ecatmur Die verknüpfte Antwort ist falsch. 'methd' muss statisch zugewiesen werden, da' PyCFunction' einen Zeiger auf das angegebene 'PyMethodDef' behält und es später zum Abrufen der C-Funktion und Flags verwendet. Der Code in der Antwort weist das 'PyMethodDef' automatisch zu, was zu einem Absturz führt, wenn die resultierende Python-Funktion aufgerufen wird. – user4815162342

+0

@ user4815162342: In der verknüpften Antwort, ich denke, Manux zeigte nur einen Überblick über den Prozess. Es ist ein bisschen albern, IMO, dass Manux den Funktionsnamen als Modulnamen anstatt nur "NULL" verwendet. Im tatsächlichen Code gehe ich davon aus, dass der 'PyMethodDef' auf dem Heap zugewiesen wurde. – eryksun

Antwort

9

Dies ist schwieriger als man erwarten würde, aber es kann getan werden.

Wenn Sie eine einzelne C-Funktion, die Sie als Rückruf zur Verfügung stellen möchten, können Sie PyCFunction_New verwenden Sie es in einen Python zu konvertieren aufrufbar:

#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); 

Dieser Ansatz wird nicht funktionieren, wenn Sie wählen möchten verschiedene Rückrufe zur Laufzeit, z um eine generische c_to_python-Funktion bereitzustellen, die eine C-Callback-Funktion in einen Python-Callback konvertiert. In diesem Fall müssen Sie einen Erweiterungstyp mit einer eigenen tp_call implementieren.

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; 
} 

Dieser Code wird für die auch zu akzeptieren, einen Zeiger user_data trivially verlängert; Speichern Sie einfach die Benutzerdaten in der Struktur CallbackObject.

+0

Mit einem kurzen Blick scheint es boost :: Python funktioniert so, um C-Funktionen zu wickeln. – neodelphi

+0

@neodelphi Es macht Sinn, dass es tut, aber die Frage wurde mit C markiert, so dass 'boost :: python' nicht wirklich zutrifft. BTW wird 'boost :: python' aktiv gepflegt? Die Dokumentation schien ziemlich alt zu sein, als ich sie überprüfte. – user4815162342

+0

Sie haben Recht. Ich habe gerade diesen Beitrag gefunden, weil ich versuche, eine boost :: python-ähnliche Bibliothek zu implementieren, da sie seit vielen Jahren nicht mehr aktualisiert wurde. Ich habe keine andere Lösung gefunden, und wie ich sehe, scheint boost :: python so zu funktionieren. – neodelphi

Verwandte Themen