嵌入CPython:如何构造Python可调用函数来包装C回调指针? [英] Embedding CPython: how do you constuct Python callables to wrap C callback pointers?

查看:207
本文介绍了嵌入CPython:如何构造Python可调用函数来包装C回调指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我将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?

注意:这是问题的修订版 dhanasubbu 发布回答,但后来被删除了.我认为这实际上是一个好问题,因此我已将自己写的内容转换为对问题的自我回答.欢迎提供其他答案.

推荐答案

要定义一种扩展类型,在Python使用该扩展类型的意义上,它是可调用的" 术语,则填充类型对象的tp_call插槽,这是__call__特殊方法的C等效项.该插槽中的函数将是一个胶水例程,该例程调用实际的C回调.这是最简单的情况下的代码,当C回调不接受任何参数且不返回任何内容时.

To define an extension type that is "callable" in the sense Python uses that term, 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, by calling 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 function object 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回调指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆