如何在本机回调中使用Cython cdef类成员方法 我有一个C++回放库,它被重写驱动。回调被注册为std::function实例
回调注册函数可能类似于:如何在本机回调中使用Cython cdef类成员方法 我有一个C++回放库,它被重写驱动。回调被注册为std::function实例,c++,cython,C++,Cython,回调注册函数可能类似于: void register(std::function<void(int)> callback); register(function_wrapper[specific_type].make_std_function(<cb_type>self.member_callback, <void*>self)) 我可以成功注册它: cdef function[void(int)]* f_my_callback = new functio
void register(std::function<void(int)> callback);
register(function_wrapper[specific_type].make_std_function(<cb_type>self.member_callback, <void*>self))
我可以成功注册它:
cdef function[void(int)]* f_my_callback = new function[void(int)](my_callback)
register(deref(f_my_callback))
问题是我想注册cdef类
方法作为回调。假设我有一门课:
cdef class MyClass:
cdef function[void(int)]* f_callback
py_callback = lambda x: None
# some more init/implementation
def __init__(self):
# ** this is where the problem is **
self.f_callback = new function[void(int)](self.member_callback)
register(deref(self.f_callback))
cdef void member_callback(self, int n) with gil:
self.py_callback(n)
def register_py(self, py_callback):
self.py_callback = py_callback
self.f\u callback=new function[void(int)](self.member\u callback)
行不工作,因为function[t]
构造函数看到了MyClass
参数(self
)。在常规python中,执行self.member\u回调
基本上等同于将self
部分应用于MyClass.member\u回调
,因此我认为这很好,但事实并非如此
我做了一个typedef:
ctypedef void (*member_type)(int)
然后,如果我强制转换,我可以让它编译:
self.f_callback = new function[void(int)](<member_type>self.member_callback)
self.f_回调=新函数[void(int)](self.member_回调)
但当回调真正到来时,一切都被破坏了。使用self
执行任何操作都会导致SEGFULT
将cython类成员方法作为C/C++回调传递的“正确”方法是什么
编辑:
只是澄清,这个问题是关于将强>成员方法< /强>(即函数为<代码>自我>代码>作为第一个参数),C++函数希望得到一个类型的参数:代码>代码::函数
如果我用
@staticmethod
装饰成员回调
,并删除对self
的所有引用,那么一切都会正常工作。不幸的是,该方法需要访问self
,才能正确执行类实例的逻辑。std::function
可以接受一系列参数。在其最简单的形式中,它使用一个直接映射到其模板类型的函数指针,而这就是Cython包装器真正要处理的全部内容。为了包装一个C++成员函数,你通常使用的是代码> STD::MyIOFFORY或PyObject*
,然后自己处理引用。不幸的是,Cython目前无法为您生成包装,因此您需要自己编写一个holder。本质上,您需要一个处理引用计数的可调用对象
在中,我展示了两种创建这种包装器的方法(其中一种是手动的,第二种是通过使用boostpython节省了工作量,后者已经实现了非常类似的东西)。我在那里展示的方案将适用于任何匹配相关签名的Python可调用。尽管我用一个标准的Python模块级函数对其进行了说明,但它对于成员函数同样适用,因为instance.memberfunction
生成了一个绑定成员函数,这是一个Python可调用函数,同样适用
对于您的问题,唯一的区别是您使用的是cdef
成员函数,而不是def
成员函数。这看起来应该可以工作,但没有(Cython的最新版本尝试自动转换为Python可调用,但由于运行时错误而失败)。您可以创建一个lambda函数作为一个非常简单的包装器。从速度的角度来看,这稍微有点不理想,但具有使用现有代码的优势
您需要对前面的答案进行如下修改:
- 如果您试图使用手动
版本,请更改参数类型以匹配您的签名(即将PyObjectWrapper
更改为int、string&
)。对于boost版本,您不必这样做int
void寄存器(std::函数回调)的包装器代码>需要:
取决于您使用的版本。这是关于Cython的签名,但是由于这两个对象都是可调用C++对象,C++编译器自动将其转换为<代码> STD::函数< /> >cdef extern from "whatever.hpp": void register(PyObjWrapper) # or void register(bpo)
。
- 调用
asregister
或register(PyObjWrapper(lambda x:self.member\u回调(x)))
register(get\u as\u bpo(lambda x:self.member\u回调(x))
可以创建专门针对使用
cdef
功能的更高效版本。它将基于一个非常类似于PyObjWrapper
的对象。然而,我不愿意在没有明确理由的情况下这样做,因为我已经编写的代码可以工作。我相信@DavidW的答案对于所问问题的简化版本是正确的
为了清晰起见,我提出了一个问题的简化版本,但据我所知,我的问题的真实版本需要一个稍微不同的方法
具体地说,在我的简化版本中,我指出将使用int
调用回调。如果是这种情况,我可以设想提供一个可调用的python(boostPython或PyObjWrapper
版本)
但是,回调实际上是通过std::shared_ptr
调用的,其中T
是库中的模板类。cdef
回调需要对该实例执行一些操作,并最终调用一个python可调用实例。据我所知,没有一种方法可以调用一个带有C++对象的Python函数,比如<代码> STD::SyrdypTr< /Cord>
@戴维德的回答很有帮助,它让我找到了一个解决这种情况的方法。我得了C+
// wrapper.hpp
#include <memory>
#include <functional>
#include "my_cpp_project.hpp"
template<typename T>
using cy_callback = void (*)(void*, std::shared_ptr<my_cpp_class<T>>);
template<typename T>
class function_wrapper {
public:
static
std::function<void(std::shared_ptr<my_cpp_class<T>>)>
make_std_function(cy_callback<T> callback, void* c_self)
{
std::function<void(std::shared_ptr<my_cpp_class<T>>)>
wrapper = [=](std::shared_ptr<my_cpp_class<T>> sample) -> void
{
callback(c_self, sample);
};
return wrapper;
}
};
cdef extern from "wrapper.hpp":
cdef cppclass function_wrapper[T]:
ctypedef void (*cy_callback) (void*, shared_ptr[my_cpp_class[T]])
@staticmethod
function[void(shared_ptr[my_cpp_class[T]])] make_std_function(
cy_callback, void*)
cdef void member_callback(self, shared_ptr[my_cpp_class[specific_type]] sample) with gil:
# do stuff with the sample ...
self.python_cb(sample_related_stuff)
register(function_wrapper[specific_type].make_std_function(<cb_type>self.member_callback, <void*>self))
ctypedef void (*cb_type)(void*, shared_ptr[my_cpp_class[specific_type]])