首先,我想说我没有选择修改甚至查看c源代码,因此任何涉及修改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指针,但我想将这个值抽象为一个类。
答案 0 :(得分:4)
正如您可能已经想到的那样,调用中的问题
bot.event_set(VP.EVENT_OBJECT_CLICK, click)
实际上,第三个参数click
是一个 Python 函数对象,您将event_set
传递给vp_event_set
。唉vp_event_set
期待VPEventHandler
是 C 类型为void(*VPEventHandler)(VPInstance);
的函数指针
我想我会构建一个字典,它与一个VPInstance
(void *
指针作为整数)相关联,是一个PyEvent
类的实例,它应该包含函数click。使用它可以确保您需要在一个C函数上作为回调。
在foo.pxd
:
cdef class PyEvent(object):
cdef VPInstance instance
cdef object py_callback
在foo.pyx
中:
events = dict()
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)
我没有机会测试这个,所以我希望它或多或少有效。另外我建议在cython-users@googlegroups.com上询问,因为他们通常真的很有帮助,而且比我更专业。
答案 1 :(得分: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;
}
%}