繁体   English   中英

使用cython将回调函数从python传递到c

[英]Pass a callback function from python to c using cython

首先,我想说我没有修改甚至查看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指针,但我想将此值抽象为一个类。

您可能已经发现,通话中的问题

bot.event_set(VP.EVENT_OBJECT_CLICK, click)

事实上,第三个参数click为您传递一个Python函数对象event_setvp_event_set vp_event_set需要一个VPEventHandler ,它是类型为void(*VPEventHandler)(VPInstance);C函数指针void(*VPEventHandler)(VPInstance);

我想我将建立一个与VPInstancevoid *指针VPInstance转换为整数)相关联的字典,该字典应包含一些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,因为它们通常确实很有帮助,并且比我更专业。

最终,我对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;
}
%}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM