這比想象的要難,但可以做到。
如果你要提供一個回調一個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
結構中。
請參閱http://stackoverflow.com/questions/6626167/build-a-pyobject-from-a-c-function(需要合併;出於mod點)。 – ecatmur
@ecatmur鏈接的答案不正確。 'methd'必須靜態分配,因爲'PyCFunction'保留了一個指向提供的'PyMethodDef'的指針,稍後用它來檢索C函數和標誌。答案中的代碼自動分配'PyMethodDef',當調用結果Python函數時會導致崩潰。 – user4815162342
@ user4815162342:在相關的答案中,我認爲Manux只是對過程進行了概述。 IMO有點愚蠢,Manux使用函數名稱作爲模塊名稱而不是「NULL」。在實際的代碼中,我假設'PyMethodDef'被分配在堆上。 – eryksun