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

Met*_*ark 4 c python callback 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);
...
Run Code Online (Sandbox Code Playgroud)

在 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)
Run Code Online (Sandbox Code Playgroud)

我想要在Python中拥有什么:

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

这就是你在 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)
}
Run Code Online (Sandbox Code Playgroud)

然而问题是,当编译 VP.pyx 时,我得到

无法将 Python 对象转换为“VPEventHandler”

同样,默认情况下回调会被赋予一个 VPInstance 指针,但我想将此值抽象到一个类中。

hiv*_*ert 5

正如您可能已经想到的,通话中的问题

bot.event_set(VP.EVENT_OBJECT_CLICK, click)
Run Code Online (Sandbox Code Playgroud)

事实上,第三个参数click是一个Python函数对象,您将其传递event_setvp_event_set. 唉,vp_event_set期待的VPEventHandler是一个类型为C 的函数指针void(*VPEventHandler)(VPInstance);

VPInstance我想我会构建一个与某个类的实例(void *指针转换为整数)关联的字典,PyEvent该类的实例本身应该包含函数单击。使用它可以确保您需要一个 C 函数作为回调。

foo.pxd

cdef class PyEvent(object):
     cdef VPInstance instance
     cdef object py_callback
Run Code Online (Sandbox Code Playgroud)

foo.pyx:事件= 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)
Run Code Online (Sandbox Code Playgroud)

我没有机会测试这个,所以我希望它或多或少有效。另外,我建议您通过 cython-users@googlegroups.com 询问,因为他们通常非常有帮助,而且比我更专业。