【问题标题】:Embedding Python with C用 C 嵌入 Python
【发布时间】:2012-11-23 23:03:53
【问题描述】:

我想在 C 应用程序中使用基于事件的 Python 库。我使用官方 C-API 嵌入 Python:http://docs.python.org/2/c-api/index.html#c-api-index

从 C 中调用方法并收集返回值是没有问题的。但是,我不知道如何执行以下操作:

几个 python 库函数将我认为是 python 函数指针作为参数。

当从 C 调用这些方法时,是否可以传递一个 C 函数指针,以便 Python 函数使用 C 函数作为回调?

如果没有,如何让 Python 使用 C 回调?

【问题讨论】:

  • stackoverflow.com/questions/6626167/…(需要合并;模组点数不足)。
  • @ecatmur 链接的答案不正确。 methd 必须是静态分配的,因为 PyCFunction 保留了一个指向所提供的 PyMethodDef 的指针,并在以后使用它来检索 C 函数和标志。答案中的代码会自动分配PyMethodDef,这会在调用生成的Python函数时导致崩溃。
  • @user4815162342:在链接的答案中,我认为 Manux 只是展示了该过程的概述。 IMO 有点傻,Manux 使用函数名作为模块名,而不仅仅是 NULL。在实际代码中,我假设 PyMethodDef 是在堆上分配的。
  • @eryksun 也许应该澄清答案,注意PyMethodDef 应该分配在堆上。这引发了后续问题,即何时以及如何释放动态分配的PyMethodDef。我的回答,通过一些初始投资,很好地解决了这个问题。
  • 您好,我在这篇帖子下提供了一个很好的例子stackoverflow.com/a/46441794/5842403

标签: python c python-2.7


【解决方案1】:

这比人们想象的要难,但可以做到。

如果您想提供一个 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_to_python 函数,将 C 回调函数转换为 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 结构中即可。

【讨论】:

  • 快速浏览一下,boost::python 似乎确实喜欢这样包装 C 函数。
  • @neodelphi 这样做是有道理的,但问题被标记为 C,所以 boost::python 并不真正适用。顺便说一句,boost::python 是否在积极维护?当我检查它时,文档似乎很旧。
  • 你是对的。刚刚找到这篇文章是因为我正在尝试实现类似 boost::python 的库,因为它已经多年没有更新了。我没有找到任何其他解决方案,并且当我看到 boost::python 这样做时,这似乎是这样做的方法。
猜你喜欢
  • 1970-01-01
  • 2014-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-16
  • 2020-12-23
  • 1970-01-01
  • 2020-12-12
相关资源
最近更新 更多