带有类方法的 Cython 回调
Cython callback with class method
我开始使用Cython将一些C++代码与Python接口。由于我需要在C++中进行一些异步数据采集,因此我计划使用从C++代码到 Python 的回调。到目前为止,我已经能够成功地从C++调用 Python 函数,但是,我无法从C++调用 Python 类方法。我在StackOverflow上看到过几个关于它的问题,但没有人对一件看似简单的事情提供简单的答案。这是一个工作玩具的例子。
myclass.h:
#ifndef MYCLASS_H
#define MYCLASS_H
#define PY_SSIZE_T_CLEAN
#include <Python.h>
typedef void (*callbackfun)(PyObject* obj);
class MyClass
{
public:
void start(callbackfun user_func, PyObject* obj);
};
#endif // MYCLASS_H
我的类.cpp:
#include "myclass.h"
void MyClass::start(callbackfun user_func, PyObject* obj)
{
user_func(obj);
}
PyMyClass.pyx:
# distutils: language = c++
# cython: language_level=3
cdef extern from "myclass.h":
ctypedef void (*callbackfun)(object obj)
cdef cppclass MyClass:
MyClass()
void start(callbackfun user_func, object obj)
cdef void callback(self):
print('Called back')
cdef class PyMyClass:
cdef MyClass *c_myClass_ptr
def __cinit__(self):
self.c_myClass_ptr = new MyClass()
def __dealloc__(self):
del self.c_myClass_ptr
def start(self):
self.c_myClass_ptr.start(callback, self)
main.py:
from PyMyClass import PyMyClass
def main():
myClass = PyMyClass()
myClass.start()
if __name__ == "__main__":
main()
现在,如果我将PyMyClass.pyx
中的callback
函数转换为方法,如下所示:
PyMyClass.pyx:
# distutils: language = c++
# cython: language_level=3
cdef extern from "myclass.h":
ctypedef void (*callbackfun)(object obj)
cdef cppclass MyClass:
MyClass()
void start(callbackfun user_func, object obj)
cdef class PyMyClass:
cdef MyClass *c_myClass_ptr
def __cinit__(self):
self.c_myClass_ptr = new MyClass()
def __dealloc__(self):
del self.c_myClass_ptr
cdef void callback(self):
print('Called back')
def start(self):
self.c_myClass_ptr.start(self.callback, self)
现在编译器抱怨:
Compiling PyMyClass.pyx because it changed.
[1/1] Cythonizing PyMyClass.pyx
Error compiling Cython file:
------------------------------------------------------------
...
cdef void callback(self):
print('Called back')
def start(self):
self.c_myClass_ptr.start(self.callback, self) ^
------------------------------------------------------------
PyMyClass.pyx:24:36: Cannot assign type 'void (PyMyClass)' to 'callbackfun'
Traceback (most recent call last):
File "setup.py", line 22, in <module>
setup(ext_modules = cythonize(ext_modules))
File "/Users/andreac/anaconda3/lib/python3.7/site-packages/Cython/Build/Dependencies.py", line 1097, in cythonize
cythonize_one(*args)
File "/Users/andreac/anaconda3/lib/python3.7/site-packages/Cython/Build/Dependencies.py", line 1220, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: PyMyClass.pyx
有没有(简单(解决此问题的方法?(是的,我知道使用PyObject*
也存在潜在的引用计数问题(
您有一些问题,但它们相对容易解决:
-
self.callback
尝试将函数与self
指针绑定。在这种情况下,我不是 100% 确定它是如何工作的 - 它最终可能会创建一个可调用的 Pythonobject
,但无论哪种方式,它都绝对与您想要的回调函数不匹配。将其更改为&PyMyClass.callback
。我还添加了一个&
,因为 Cython 似乎需要它来了解情况。 -
&PyMyClass.callback
仍然与callbackfun
的签名不匹配。callbackfun
需要一个通用的 Python 对象,而PyMyClass.callback
的第一个参数专门用作指向PyMyClass
的底层 C 结构的指针。这很容易通过石膏解决,给self.c_myClass_ptr.start(<callbackfun>&PyMyClass.callback, self)
这种转换为基于
PyObject
的结构是使用 Python C API 的常见习惯用语,所以很好。不幸的是,投射最终可能会隐藏错误,所以要警惕这一点。 -
您正在酝酿一个潜在的引用计数灾难:您将"借用的引用"传递给
self
。你在问题中提到了这一点,但我只是重申一下,以防未来的读者错过它(就像我在第一次浏览时所做的那样(。在这种情况下,这很好 - 您立即使用self
然后丢弃它。但是,如果您将其保存在C++代码中,则可能会将其销毁,从而使C++代码带有有效的指针。这确实需要在设置回调时Py_INCREF
,在取消设置回调时需要Py_DECREF
。确保这种情况发生可能很难正确。
- 架构决策:返回std::future还是提供回调
- 正在为Xtensa simcall函数编写回调函数
- 如何在C++中使用非静态成员函数作为回调函数
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 在简单示例中,Python3 + ctypes 回调会导致内存泄漏
- 用于在回调中调用解析器的设计模式
- 如何使用C++对象的成员函数作为 C 样式回调?
- Java从C++回调到C++回调
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 从不同的 cpp 调用回调函数会导致bad_function_call
- pcap_handler回调仅在使用 NPCAP v0.9991 时包含空数据包
- 不带轮询的 SDL2 事件回调
- C++存储带有可变参数的回调
- 如何使用 Node-addon-API 实现 node-nan 回调
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 带有类方法的 Cython 回调
- 如何在本机回调中使用Cython CDEF类成员方法
- Cython:具有自定义参数类型的STD ::功能回调
- Cython:在DLL中注册用于回调的Python函数
- Cython使用python-c-api调用回调segfault