从python回调到c++的选项
您好,我一直在尝试使用cython从C++调用一个由用户定义的Python回调。但是,如果没有对C++端或静态函数缓冲区的更改,这看起来是不可能的。 那么,绑定Proper回调只有一个选项吗(ctype与CFuncYPE)?
Cython0.29.23
A.hpp:
typedef void (*Callback) ();
class A{
Callback callback;
public:
A(){
this->callback = nullptr;
}
void set_callback(Callback callback){
this->callback = callback;
}
void call_callback(){
this->callback();
}
};
A.pxd:
cdef extern from "A.hpp":
ctypedef void (*Callback) ()
cdef cppclass A:
A() except +
void set_callback(Callback callback)
void call_callback()
B.pyx
from A cimport A, Callback
cdef class B:
cdef A *c_self
cdef object callback
def __cinit__(self):
self.c_self = new A()
def __dealloc__(self):
del self.c_self
cdef void callback_func(self) with gil:
print("I'm here")
self.callback()
def set_callback(self, callback):
self.callback = callback
self.c_self.set_callback(<Callback>self.callback_func)
def call_callback(self):
self.c_self.call_callback()
def print_():
print("hello")
b = B()
b.set_callback(print)
b.call_callback()
输出:
I'm here
[segmentation fault]
看起来ctypes: get the actual address of a c function是一个很好的变通方法,但它使用的是ctype。 这让我很害怕,但很管用:
B.pyx
from A cimport A, Callback
import ctypes
from libc.stdint cimport uintptr_t
cdef class B:
cdef A *c_self
cdef object callback
def __cinit__(self):
self.c_self = new A()
def __dealloc__(self):
del self.c_self
def set_callback(self, callback):
f = ctypes.CFUNCTYPE(None)(callback)
self.callback = f
cdef Callback c_callback = (<Callback*><uintptr_t>ctypes.addressof(f))[0]
self.c_self.set_callback(c_callback)
def call_callback(self):
self.c_self.call_callback()
def hello():
print("hello")
b = B()
b.set_callback(hello)
b.call_callback()
解决方案
函数指针没有任何空间来存储额外信息。因此,不可能在纯C中将可调用的Python转换为函数指针。类似地,cdef class
的cdef
函数必须存储实例的地址才能使用,这也是不可能的。
您有三个选项:
使用
ctypes
,如https://stackoverflow.com/a/34900829/4657412所示(答案的下半部分显示了如何操作)。ctypes
通过运行时代码生成来实现这一点(因此只能在它明确支持的处理器上工作)。在C++中使用
std::function
而不是函数指针。它们可以存储任意信息。您需要编写一个对象包装器(或者从其他地方重用一个),这样它就不完全是纯Cython了。请参见Pass a closure from Cython to C++。您尝试执行的操作可能更适合How to use a Cython cdef class member method in a native callback。使用回调类型为
的C类方案void (CallbackT)(/* any args */, void* user_data)
,注册地址:
在本例中,void register_callback(CallbackT func, void* user_data)
user_data
将是B
实例的地址(在设置地址之前,您需要确保它是Py_INCREF
ed,在取消设置地址之前,Py_DECREF
是ed)。Cython callback with class method提供了一个示例。
相关文章