将Cython类对象作为参数传递给C函数

4
我正在尝试将 Cython 实例类对象作为参数传递给 C 函数,以便它可以回调到它的方法。
这是我尝试过的代码:
#include "python.h"

void c_func(PyObject *obj){
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    /* Dictionary Object - can be of any dynamic type */

    PyObject *dict_obj = PyDict_New();
    PyObject *key = PyUnicode_FromString("average-run");
    PyObject *val = PyLong_FromLong(45445);
    PyDict_SetItem(dict_obj, key, val);

    /* Make a callback */

    PyObject_CallMethod(obj, "func_a", "(d)", dict_obj);

    PyGILState_Release(gstate);
}

cdef extern from "sample.h":
    void c_func(object obj)

cdef class Test(object):
    cdef func_a(self, object dict_obj)

cimport pysample
from pysample cimport Test

cdef class Test(object):
    cdef func_a(self, object dict_obj):
        print(dict_obj)

    def run_test(self):
        # Here I make a call to the C function
        pysample.c_func(self)

很遗憾,来自 C 的回调没有起作用。你能发现我做错了什么或者提出修复的建议吗?

c_func函数应该如何作为Test的方法附加?self.c_func似乎应该会产生属性错误。您能否展示编译、导入和尝试调用时的错误? - ely
我错过了 pysample 的独立 cimport,我会在帖子中进行编辑。谢谢。 - Bitmap
1
我们仍然需要实际看到您编译、加载并调用run_test函数,以便我们可以尝试帮助调试。 - ely
文档中可能有帮助的两件事。1)格式字符串中的“d”被解释为浮点数,而不是字典(https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue)。 2)“请注意,如果您只传递PyObject * args,则PyObject_CallMethodObjArgs()是更快的替代方法。”(https://docs.python.org/2/c-api/object.html) - DavidW
我强烈怀疑,如果你将调用方法的行改为:PyObject_CallMethod(obj, "func_a", "(O)", dict_obj);(将字典作为通用 Python 对象传递),它就能正常工作。 - DavidW
显示剩余2条评论
1个回答

1
问题已经解决。原因是类中的方法定义为 cdef 而不是 cpdef 。
sample.c
#include "python.h"

void c_func(PyObject *obj){
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    /* Dictionary Object - can be of any dynamic type */

    PyObject *dict_obj = PyDict_New();
    PyObject *key = PyUnicode_FromString("average-run");
    PyObject *val = PyLong_FromLong(45445);
    PyDict_SetItem(dict_obj, key, val);

    /* Make a callback */

   PyObject *res = PyObject_CallMethod(obj, "func_a", "(O)", dict_obj);

   if( !res )
   {
       PyErr_Print();
   }else
   {
      Py_DECREF(res); 
   }
   /* 
    * Note: Do not remove reference counting
    * for callback argument - python will take
    * care of it when the callback python method
    * go out of scope. If you do - it will cause
    * inconsistent data behaviour at the callback 
    * method side.
    */

   PyGILState_Release(gstate);
}
pysample.pxd
cdef extern from "sample.h":
    void c_func(object obj)

cdef class Test(object):
    cpdef func_a(self, object dict_obj)
pysample.pyx
cimport pysample
from pysample cimport Test

cdef class Test(object):
    cpdef func_a(self, object dict_obj):
        print(dict_obj)

    def run_test(self):
        # Here I make a call to the C function
        pysample.c_func(self)

很高兴你解决了它!我在第一次查看时错过了cdef/cpdef/def问题。你应该修复两个更多的东西(但不是直接与你的核心问题相关):1)c_func不返回PyObject*(但你说它会)。2)PyObject_CallMethod返回一个PyObject*(对于你特定的测试用例可能是None),你应该减少它 - 如果从func_a返回任何非平凡值,你将有一个内存泄漏。 - DavidW
同时(在c_func的末尾):Py_DECREF(dict_obj); Py_DECREF(key); Py_DECREF(val);,并更改调用方式为:PyObject* res = PyObject_CallMethod( /* 和之前一样 */); if (!res) { PyErr_Print(); } else { Py_DECREF(res); }。在C-API中涉及到很多引用计数,这实际上有点让人噩梦般。 - DavidW
还有一件让我困惑的事情是,有两个 cdef class Test(object),导入的那一个会在 .pyx 文件中被覆盖吗? - Nick
是的。可以将 .pxd 视为接口,.pyx 视为实现。 - Bitmap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接