2012-11-23 50 views
5

我想在C應用程序中使用基於事件的Python庫。我使用用於嵌入Python的官方C-API:http://docs.python.org/2/c-api/index.html#c-api-index將Python嵌入到C中使用C

從C調用方法並收集返回值是沒有問題的。但是,我不知道如何執行以下操作:

幾個python庫函數將我認爲是一個python函數指針作爲參數。

從C調用這些方法時,是否可以傳遞C函數指針,以便Python函數使用C函數作爲回調函數?

如果不是,如何完成讓Python使用C回調?

+0

請參閱http://stackoverflow.com/questions/6626167/build-a-pyobject-from-a-c-function(需要合併;出於mod點)。 – ecatmur

+1

@ecatmur鏈接的答案不正確。 'methd'必須靜態分配,因爲'PyCFunction'保留了一個指向提供的'PyMethodDef'的指針,稍後用它來檢索C函數和標誌。答案中的代碼自動分配'PyMethodDef',當調用結果Python函數時會導致崩潰。 – user4815162342

+0

@ user4815162342:在相關的答案中,我認爲Manux只是對過程進行了概述。 IMO有點愚蠢,Manux使用函數名稱作爲模塊名稱而不是「NULL」。在實際的代碼中,我假設'PyMethodDef'被分配在堆上。 – eryksun

回答

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結構中。

+0

有一個快速的樣子,似乎boost :: python是這樣包裝C函數。 – neodelphi

+0

@neodelphi它是有道理的,但問題被標記爲C,所以'boost :: python'並不適用。順便提一下是'boost :: python'積極維護?當我檢查它時,文檔看起來很老。 – user4815162342

+0

你說得對。剛剛發現這篇文章,因爲我試圖實現一個boost :: python像庫,因爲它已經很多年沒有更新。我沒有找到任何其他的解決方案,因爲我看到boost :: python這樣做,這似乎是要做的。 – neodelphi