2011-03-09 132 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是非常關鍵的,因爲事件是集中發射)。