Чтобы определить тип расширения, который может быть вызван в том смысле, в котором Python использует это
термин, вы заполняете слот tp_call
объекта типа, который является эквивалентом C специального метода __call__
. Функция, которая входит в этот слот, будет клейкой подпрограммой, которая вызывает реальный обратный вызов C. Вот код для простейшего случая, когда обратный вызов C не принимает аргументов и ничего не возвращает.
typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
void (*cfun)(void); /* or whatever parameters it actually takes */
} CallbackObj;
static PyObject *Callback_call(PyObject *self, PyObject *args, PyObject *kw)
{
/* check that no arguments were passed */
const char no_kwargs[] = { 0 };
if (!PyArg_ParseTupleAndKeywords(args, kw, "", no_kwargs))
return 0;
CallbackObj *cself = (CallbackObj *)self;
cself->cfun();
Py_RETURN_NONE;
}
static PyTypeObject CallbackType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mymodule.Callback",
.tp_doc = "Callback function passed to foo, bar, and baz.",
.tp_basicsize = sizeof(CallbackObj),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
.tp_call = Callback_call,
};
Создайте объект типа с PyType_Ready
как обычно. Не помещайте его в любой модуль, видимый для Python, потому что код Python не может правильно создавать экземпляры этого типа. (Из-за этого мне не надоела функция tp_init
; просто убедитесь, что вы всегда инициализируете ->cfun
после создания экземпляров из C, иначе Callback_call
вылетит.)
Теперь предположим, что фактическая функция, которую нужно вызвать, называется real_callback
, а функция Python, которой вы хотите ее передать, называется function_to_call
. Сначала вы создаете один из объектов обратного вызова,
вызовите объект типа, как обычно, и инициализируйте его поле ->cfun
:
PyObject *args = PyTuple_New(0);
CallbackObj *cb = (CallbackObj *)PyObject_CallObject(
(PyObject *)CallbackType, args);
Py_DECREF(args);
cb->cfun = real_callback;
Затем вы помещаете cb
в кортеж аргумента и вызываете функцию Python.
возражать с этим, как обычно.
args = Py_BuildValue("(O)", cb);
PyObject *ret = PyObject_CallObject(function_to_call, args);
Py_DECREF(args);
Py_DECREF(cb);
// do stuff with ret, here, perhaps
Py_DECREF(ret);
Распространение этого на более сложные случаи, когда обратный вызов C должен принимать аргументы и / или возвращать значения и / или вызывать исключения Python при ошибках и / или получать информацию «замыкания» из внешнего контекста, оставляется в качестве упражнения.