问题描述
假设我将CPython解释器嵌入到用C编写的更大程序中.该程序的C组件有时需要调用用Python编写的函数,并向它们提供回调函数作为参数.
Suppose I am embedding the CPython interpreter into a larger program, written in C. The C component of the program occasionally needs to call functions written in Python, supplying callback functions to them as arguments.
使用CPython 扩展和嵌入 API,如何构造Python"callable" 包装了C指向函数的指针的对象,这样我就可以将该对象传递给Python代码,并使Python代码成功地回调到C代码中?
Using the CPython extending and embedding APIs, how do I construct a Python "callable" object that wraps a C pointer-to-function, so that I can pass that object to Python code and have the Python code successfully call back into the C code?
推荐答案
要定义一种扩展类型,在Python使用该扩展类型的意义上,它是可调用的"术语,则填充类型对象的tp_call
插槽,这是__call__
特殊方法的C等效项.该插槽中的函数将是一个胶水例程,该例程调用实际的C回调.这是最简单的情况下的代码,当C回调不接受任何参数且不返回任何内容时.
To define an extension type that is "callable" in the sense Python uses thatterm, you fill the tp_call
slot of the type object, which is the C equivalent of the __call__
special method. The function that goes in that slot will be a glue routine that calls the actual C callback. Here’s code for the simplest case, when the C callback takes no arguments and returns nothing.
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
函数烦恼;只需确保在使用C创建实例后始终初始化->cfun
,否则Callback_call
会崩溃.)
Instantiate the type object with PyType_Ready
as usual. Don’t put it in any module visible to Python, though, because Python code can’t correctly create instances of this type. (Because of this, I haven’t bothered with a tp_init
function; just make sure you always initialize ->cfun
after creating instances from C, or Callback_call
will crash.)
现在,假设您需要调用的实际函数被命名为real_callback
,而您想要传递给它的Python函数被命名为function_to_call
.首先,通过以下方式创建一个回调对象像往常一样调用类型对象,并初始化其->cfun
字段:
Now, suppose the actual function you need to call is named real_callback
, and the Python function that you want to pass it to is named function_to_call
. First you create one of the callback objects, bycalling the type object, as usual, and initialize its ->cfun
field:
PyObject *args = PyTuple_New(0);
CallbackObj *cb = (CallbackObj *)PyObject_CallObject(
(PyObject *)CallbackType, args);
Py_DECREF(args);
cb->cfun = real_callback;
然后将cb
放入参数元组,并调用Python函数像往常一样反对.
Then you put cb
into an argument tuple, and call the Python functionobject with that, as usual.
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异常和/或从外部上下文接收关闭"信息,这是一个练习.
Extending this to more complex cases, where the C callback needs to take arguments and/or return values and/or raise Python exceptions on error and/or receive "closure" information from the outer context, is left as an exercise.
这篇关于嵌入CPython:如何构造Python可调用函数来包装C回调指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!