2011-03-09 92 views
8

我一直在使用Cython尝试与用C++编写的库进行接口。到目前为止,事情都非常好,我可以有效地使用库中的MOST函数。我唯一的问题在于实现回调。图书馆有一个看起来有点像这样4个函数定义:Cython - 实现回调

typedef void (*Function1)(const uint16_t *data, 
         unsigned width, unsigned height); 
void SetCallBack(Function1); 

因此,要实现他们,我想我会做这样的事情与用Cython:

ctypedef void (*Function1)(unsigned short *data, 
         unsigned width, unsigned height); 
cdef extern from "lib.hpp": 
    void SetCallBack(Function1) 

里面居然编译正确,但是,我不能为了我的生活而思考如何以回调的方式真正实现它。我第一次尝试创建只需调用,类似于你会怎么做它的其他功能,想出这样的功能:

def PySetCallBack(Func): 
    SetCallBack(Func) 

但给我的(预测的)错误:

“无法将Python对象转换为'Function1'”

所以是的,那就是我所在的位置。如果任何人有任何在Cython中设置回调的经验,我将非常感谢任何帮助。谢谢。

编辑: 按照你的意见,我创建了一个带有CDEF中间的功能,它看起来像这样:

cdef void cSetCallBack(Function1 function): 
    SetCallBack(function) 

这似乎已经得到了我......更紧密?得到一个不同的错误,现在至少:

error: invalid conversion from ‘void (*)(short unsigned int*, unsigned int, unsigned int)’ to ‘void (*)(const uint16_t*, unsigned int, unsigned int)’ 

现在,据我可以告诉这些类型是相同的,所以我想不通这是怎么回事。

EDIT2

ctypedef unsigned short uint16_t 

,并将它作为参数调用,但显然这实际上并没有得到任何接近,但只是把我身边: 通过宣布新的typedef修正问题一个侧轨,因为当试图调用该函数时,我得到了相同的“无法将Python对象重新转换为'Function1''错误。

所以,我几乎回到了我开始的地方。我现在唯一能做的事情就是明确地将python对象作为c函数进行转换,但是,说实话,我不知道该如何去做。

编辑第三: 好吧,解剖你的答案后,我终于得到它,和它的作品,所以万岁和诸如此类的东西。我落得这样做创建这样的功能:所以现在

cdef void cSetCallback(Function1 function): 
    SetCallback(function) 
cdef void callcallback(const_ushort *data, unsigned width, unsigned height): 
    global callbackfunc 
    callbackfunc(data,width,height) 
cSetCallback(callcallback) 
def PySetCallback(callbackFunc): 
    global callbackfunc 
    callbackfunc = callbackFunc 

唯一的问题是,它不能const_ushort *数据转换成Python对象,但是这完全是另外一个问题,所以我想这一个解决了,非常感谢。

+0

将'short unsigned int *'更改为'const short unsigned int *'。 – aschepler 2011-03-09 23:39:42

+1

据我所知,Cython不知道const是什么意思,并且每当我尝试使用它时,我都会得到“Const不是类型标识符”。 – Josiah 2011-03-10 02:05:17

回答

5

如果你可以修改库定义:

typedef void (*Function1)(const uint16_t *data, 
          unsigned width, unsigned height, 
          void *user_data); 
void SetCallBack(Function1, void*); 

相反,我怕你的运气了。如果你有void*,比定义一个函数调用带有正确参数的python可调用对象,并使用此函数和python可调用函数调用SetCallBack

如果你不能,但回调是全局的(看来是),你可以创建一个全局变量来存储python对象。比你再次创建一个函数来调用python对象,并通过它到SetCallBack和您的PySetCallback只会设置全局并确保正确的功能注册。

如果回调是特定于上下文的,但您无法将其传递给“用户数据”指针,那么我担心您在这里运气不佳。我知道python和C++,但不是cython,所以我不知道你是否可以在cython中创建函数,或者你是否必须用C++编写函数。

8

我最近一直处于这种情况,我不得不使用Cython将现有的C++库与Python进行接口,从而大量使用事件/回调。首先,包装C++回调类(基于'double(METHOD)(void)'原型)的原型,但它可能是模板化的,因为用Cython可以处理模板):

ALabCallBack.h:

#ifndef ALABCALLBACK_H_ 
#define ALABCALLBACK_H_ 


#include <iostream> 

using namespace std; 

namespace elps { 

//template < typename ReturnType, typename Parameter > 
class ALabCallBack { 
public: 

    typedef double (*Method)(void *param, void *user_data); 

    ALabCallBack(); 
    ALabCallBack(Method method, void *user_data); 
    virtual ~ALabCallBack(); 

    double cy_execute(void *parameter); 

    bool IsCythonCall() 
    { 
     return is_cy_call; 
    } 

protected: 

    bool is_cy_call; 

private: 

    //void *_param; 
    Method _method; 
    void *_user_data; 

}; 


} /* namespace elps */ 
#endif /* ALABCALLBACK_H_ */ 

ALabCallBack.cpp:

#include "ALabCallBack.h" 

namespace elps { 


ALabCallBack::ALabCallBack() { 
    is_cy_call = true; 
}; 

ALabCallBack::~ALabCallBack() { 
}; 

ALabCallBack::ALabCallBack(Method method, void *user_data) { 
    is_cy_call = true; 
    _method = method; 
    _user_data = user_data; 
}; 

double ALabCallBack::cy_execute(void *parameter) 
{ 
    return _method(parameter, _user_data); 
}; 


} /* namespace elps */ 

其中:

  • '回调' ::图案/转换器的方法火一个Python(=方法)选自C 对象的方法类型的相关信息

  • '方法' ::传递的有效方法由Python用户(=的user_data)

  • '参数' ::参数被传递给 '方法'

现在,我们需要实现.pyx文件...

我们的基础原型:

ctypedef double (*Method)(void *param, void *user_data) 

然后,我们提供C++类一个用Cython包装:

cdef extern from "../inc/ALabCallBack.h" namespace "elps" : 
    cdef cppclass ALabCallBack: 
     ALabCallBack(Method method, void *user_data) 
     double cy_execute(void *parameter) 

图案/转换器的方法将被用于平移Ç类型样机到一个Python对象调用:

cdef double callback(void *parameter, void *method): 
    return (<object>method)(<object>parameter) 

现在让我们将这个特性嵌入到Cython类中:

cdef class PyLabCallBack: 
    cdef ALabCallBack* thisptr 

    def __cinit__(self, method): 
     # 'callback' :: The pattern/converter method to fire a Python 
     #    object method from C typed infos 
     # 'method' :: The effective method passed by the Python user 
     self.thisptr = new ALabCallBack(callback, <void*>method) 

    def __dealloc__(self): 
     if self.thisptr: 
      del self.thisptr 

    cpdef double execute(self, parameter): 
     # 'parameter' :: The parameter to be passed to the 'method' 
     return self.thisptr.cy_execute(<void*>parameter) 

编辑:DEF执行=> cpdef双

就是这样:用于执行功能更好的打字。说它喜欢做这样的事情:

def func(obj): 
    print obj 
    obj.Test()  # Call to a specific method from class 'PyLabNode' 
    return obj.d_prop 

n = PyLabNode() # Custom class of my own 
cb = PyLabCallBack(func) 
print cb.execute(n) 

在Python隐式类型,我们可以访问相关的类作为参数传递的对象“OBJ”对象的属性到时候火回调。

它可以很容易地适用于纯粹的C实现。 请告诉我,如果你能看到任何可能的增强(在我的情况下,perfs是非常关键的,因为事件是集中发射)。