2013-07-25 74 views
2

首先,我想說我沒有修改甚至查看c源代碼的選項,因此涉及修改c文件的任何內容都不會有幫助。使用cython將一個回調函數從python傳遞到c

在VP.h:

typedef enum VPEvent { 
    ... 
    EVENT_OBJECT_CLICK, 
    ... 
} 
... 
typedef void *VPInstance; 
typedef void(*VPEventHandler)(VPInstance); 
... 
VPSDK_API VPInstance vp_create(void); 
... 
VPSDK_API int vp_event_set(VPInstance instance, VPEvent eventname, VPEventHandler event); 
... 

在VP.pyx:

cdef extern from "VP.h": 
    ... 
    cdef enum VPEvent: 
    ... 
    VP_EVENT_OBJECT_CLICK, 
    ... 

    ... 
    ctypedef void *VPInstance 
    ctypedef void(*VPEventHandler)(VPInstance) 
    ... 
    VPInstance vp_create() 
    ... 
    int vp_event_set(VPInstance instance, VPEvent eventname, VPEventHandler event) 
    ... 

... 
EVENT_OBJECT_CLICK = VP_EVENT_OBJECT_CLICK 
... 

cdef class create: 
    cdef VPInstance instance 

    def __init__(self): 
    self.instance = vp_create() 
    ... 
    def event_set(self, eventname, event): 
    return vp_event_set(self.instance, eventname, event) 

我想有在Python什麼:

import VP 
... 
def click(bot): 
    bot.say("Someone clicked something!") 
... 
bot = VP.create() 
bot.event_set(VP.EVENT_OBJECT_CLICK, click) 

這是你會怎麼做在c:

#include <VP.h> 

void click(VPInstance instance) { 
    vp_say(instance, "Someone clicked something!"); 
} 

int main(int argc, char ** argv) { 
    ... 
    VPInstance instance; 
    instance = vp_create(); 
    ... 
    vp_event_set(instance, VP_EVENT_OBJECT_CLICK, click) 
} 

然而問題是,編譯VP.pyx當我得到

不能Python對象轉換爲「VPEventHandler」

同時,默認情況下,回調賦予了VPInstance指針,但我想將這個值抽象成一個類。

回答

3

您可能已經想通了,在通話

bot.event_set(VP.EVENT_OBJECT_CLICK, click) 

事實上這個問題,第三個參數click是你被傳遞到event_set一個vp_event_set Python的功能對象。唉vp_event_set期待一個VPEventHandler是類型的ç函數指針void(*VPEventHandler)(VPInstance);

我想我會建立一個字典關聯到VPInstancevoid *指針鑄造爲整數)一些PyEvent類的一個實例,它應該包含本身功能點擊。使用它你可以確保你需要一個C函數作爲回調。

foo.pxd

cdef class PyEvent(object): 
    cdef VPInstance instance 
    cdef object py_callback 

foo.pyx: 事件=字典()

cdef void EventCallBack(VPInstance instance): 
    PyEvent ev = <PyEvent> dict[events[<size_t> self.instance] 
    ev.py_callback(ev) 

cdef class PyEvent(object): 
    def __init__(self, click): 
     self.instance = vp_create() 
     self.py_callback = click 

    def event_set(self, eventname): 
     global events 
     events[<size_t> self.instance] = self 
     return vp_event_set(self.instance, eventname, EventCallBack) 

我沒有機會測試這一點,所以我希望它或多或少的作品。另外我會建議詢問[email protected],因爲他們通常真的很有幫助,而且比我更專業。

+0

謝謝版本!這是我見過的使用python對象使用指針處理的唯一Cython示例。即投到。 – Rebs

1

我最終通過採用與hivert建議的類似方法解決了這個問題,但是我決定切換到swig,因爲它專門爲語言綁定而構建。

這是一個下調的是我設置

int swig_event_set(VPInstance instance, VPEvent eventname, PyObject * event); 

%{ 
static PyObject * py_events[VP_HIGHEST_EVENT]; 
static void PythonEvent(VPInstance instance, int eventname) { 
    PyObject * func, * arglist; 
    PyObject * result; 

    func = py_events[eventname]; 
    arglist = Py_BuildValue("(O)", SWIG_NewPointerObj(SWIG_as_voidptr(instance), SWIGTYPE_p_void, 0)); 
    result = PyEval_CallObject(func, arglist); 
    Py_DECREF(arglist); 
    Py_XDECREF(result); 
} 

static void vp_event_object_click(VPInstance instance) { 
    PythonEvent(instance, VP_EVENT_OBJECT_CLICK); 
} 

int swig_event_set(VPInstance instance, VPEvent eventname, PyObject * event) { 
    Py_XDECREF(py_events[eventname]); /* Dispose of previous event callback */ 
    Py_XINCREF(event);     /* Add a reference to new event callback */ 
    py_events[eventname] = event;  /* Remember new event callback */ 

    switch(eventname) { 
    case VP_EVENT_OBJECT_CLICK: 
     return vp_event_set(instance, eventname, vp_event_object_click); 
     break; 
    case VP_HIGHEST_EVENT: 
     break; 
    } 

    return 1; 
} 
%} 
相關問題