python - 嵌入 CPython : how do you constuct Python callables to wrap C callback pointers?

标签 python c cpython python-c-api

假设我将 CPython 解释器嵌入到一个用 C 编写的更大程序中。程序的 C 组件有时需要调用用 Python 编写的函数,将回调函数作为参数提供给它们。

使用 CPython extending and embedding API,我如何构造一个包装 C 函数指针的 Python“可调用”对象,以便我可以将该对象传递给 Python 代码并让 Python 代码成功回调到 C 代码中?

Note: this is a revised version of a question originally posted by user dhanasubbu, which I answered, but which was then deleted. I think it was actually a good question, so I have converted what I wrote into a self-answer to my own statement of the question. Alternative answers are welcome.

最佳答案

在 Python 使用的意义上定义一个“可调用”的扩展类型
术语,您填写 tp_call类型对象的槽,它是 __call__ 的 C 等价物特殊方法。进入该槽的函数将是调用实际 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 函数;只要确保在从 C 创建实例后总是初始化 ->cfun,否则 Callback_call 会崩溃。)
现在,假设您需要调用的实际函数名为 real_callback ,并且您想要将其传递给的 Python 函数名为 function_to_call .首先,您创建一个回调对象,通过
像往常一样调用类型对象,并初始化它的->cfun field :
    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 异常和/或从外部上下文接收“关闭”信息,留作练习。

关于python - 嵌入 CPython : how do you constuct Python callables to wrap C callback pointers?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52079835/

相关文章:

c - 二进制、 float 和现代计算机

python - 运行时错误 : lost sys. 标准输出

python - 如何使用 __pycache__ 文件夹运行 Python 项目?

python - 如何与普通用户一起使用 root 创建的套接字

c - 在 C 中只接受字母而不接受整数

python - 使python进程名称更明确

c++ - 有时无法从/proc/self/maps 获取堆栈范围

基于 Python 多处理的 WSGI 网络服务器

python - 根据日期连接两个 DataFrame

python - 如何对图中每一行的 Y 轴标签进行不同的排序?