从Python到C++的回调选项

luaexgnf  于 2021-09-08  发布在  Java
关注(0)|答案(1)|浏览(376)

你好,我试着用Cython调用C的一个Python用户定义的回调。但是,如果不在C侧或静态函数缓冲区进行更改,看起来是不可能的。那么,绑定propper回调(ctypes和cfunctype)是否只有一个选项?
赛顿0.29.23
a、 水电站:

  1. typedef void (*Callback) ();
  2. class A{
  3. Callback callback;
  4. public:
  5. A(){
  6. this->callback = nullptr;
  7. }
  8. void set_callback(Callback callback){
  9. this->callback = callback;
  10. }
  11. void call_callback(){
  12. this->callback();
  13. }
  14. };

a、 pxd:

  1. cdef extern from "A.hpp":
  2. ctypedef void (*Callback) ()
  3. cdef cppclass A:
  4. A() except +
  5. void set_callback(Callback callback)
  6. void call_callback()

b、 pyx

  1. from A cimport A, Callback
  2. cdef class B:
  3. cdef A *c_self
  4. cdef object callback
  5. def __cinit__(self):
  6. self.c_self = new A()
  7. def __dealloc__(self):
  8. del self.c_self
  9. cdef void callback_func(self) with gil:
  10. print("I'm here")
  11. self.callback()
  12. def set_callback(self, callback):
  13. self.callback = callback
  14. self.c_self.set_callback(<Callback>self.callback_func)
  15. def call_callback(self):
  16. self.c_self.call_callback()
  17. def print_():
  18. print("hello")
  19. b = B()
  20. b.set_callback(print)
  21. b.call_callback()

输出:

  1. I'm here
  2. [segmentation fault]

看起来像ctypes:获取c函数的实际地址是一个很好的解决方法,但它使用ctypes。这让我很害怕,但很管用:
b、 pyx

  1. from A cimport A, Callback
  2. import ctypes
  3. from libc.stdint cimport uintptr_t
  4. cdef class B:
  5. cdef A *c_self
  6. cdef object callback
  7. def __cinit__(self):
  8. self.c_self = new A()
  9. def __dealloc__(self):
  10. del self.c_self
  11. def set_callback(self, callback):
  12. f = ctypes.CFUNCTYPE(None)(callback)
  13. self.callback = f
  14. cdef Callback c_callback = (<Callback*><uintptr_t>ctypes.addressof(f))[0]
  15. self.c_self.set_callback(c_callback)
  16. def call_callback(self):
  17. self.c_self.call_callback()
  18. def hello():
  19. print("hello")
  20. b = B()
  21. b.set_callback(hello)
  22. b.call_callback()
hivapdat

hivapdat1#

函数指针没有任何空间来存储额外信息。因此,在纯c语言中,不可能将可调用的python转换为函数指针。同样地 cdef a的功能 cdef class 必须存储示例的地址才能使用,这也是不可能的。
您有三种选择:
使用 ctypes 与在ctypes中一样:获取c函数的实际地址。 ctypes 通过运行时代码生成来实现这一点(因此只在它明确支持的处理器上工作)。
使用 std::function 在C中代替函数指针。它们可以存储任意信息。您需要编写一个对象 Package 器(或者重用其他地方的 Package 器),这样它就不完全是cython了。请参见从Cython到C的闭包。如何在本机回调中使用cython cdef类成员方法可能更好地介绍了您要做的事情。
使用c类方案,其中回调类型为

  1. void (CallbackT)(/* any args */, void* user_data)

并在以下机构注册:

  1. void register_callback(CallbackT func, void* user_data)

在这种情况下 user_data 您的地址是什么 B 示例(您需要确保 Py_INCREF 在设置地址和 Py_DECREF 在取消地址设置之前已取消设置)。带有类方法的cython回调提供了一个示例。

相关问题