Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在本机回调中使用Cython cdef类成员方法 我有一个C++回放库,它被重写驱动。回调被注册为std::function实例_C++_Cython - Fatal编程技术网

如何在本机回调中使用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或 STD::MeMyFunsiRef>代码>,以获取相关类实例的副本或引用(在现代C++中,你也可以选择lambda函数,但这确实给了你相同的选项)。 将其转换为Python/Cython时,您需要为正在调用其成员的对象保留一个
PyObject*
,然后自己处理引用。不幸的是,Cython目前无法为您生成包装,因此您需要自己编写一个holder。本质上,您需要一个处理引用计数的可调用对象

在中,我展示了两种创建这种包装器的方法(其中一种是手动的,第二种是通过使用boostpython节省了工作量,后者已经实现了非常类似的东西)。我在那里展示的方案将适用于任何匹配相关签名的Python可调用。尽管我用一个标准的Python模块级函数对其进行了说明,但它对于成员函数同样适用,因为
instance.memberfunction
生成了一个绑定成员函数,这是一个Python可调用函数,同样适用

对于您的问题,唯一的区别是您使用的是
cdef
成员函数,而不是
def
成员函数。这看起来应该可以工作,但没有(Cython的最新版本尝试自动转换为Python可调用,但由于运行时错误而失败)。您可以创建一个lambda函数作为一个非常简单的包装器。从速度的角度来看,这稍微有点不理想,但具有使用现有代码的优势

您需要对前面的答案进行如下修改:

  • 如果您试图使用手动
    PyObjectWrapper
    版本,请更改参数类型以匹配您的签名(即将
    int、string&
    更改为
    int
    )。对于boost版本,您不必这样做

  • void寄存器(std::函数回调)的包装器需要:

    cdef extern from "whatever.hpp":
      void register(PyObjWrapper)
      # or
      void register(bpo)
    
    取决于您使用的版本。这是关于Cython的签名,但是由于这两个对象都是可调用C++对象,C++编译器自动将其转换为<代码> STD::函数< /> >

  • 调用
    register
    as
    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]])