4

First off I want to say that I do not have the option to modify or even view the c source code so anything that involves modifying the c file will not be helpful.

In 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);
...

In 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)

What I want to have In Python:

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

This is how you would do it in 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)
}

However the problem is that when compiling VP.pyx I get

Cannot convert Python object to 'VPEventHandler'

As well, by default the callback is given a VPInstance pointer but I want to abstract this value into a class.

2 Answers 2

5

As you probably figured out, the problem in the call

bot.event_set(VP.EVENT_OBJECT_CLICK, click)

Indeed, the third argument click is a Python function object which you are passing in event_set to vp_event_set. Alas vp_event_set is expecting a VPEventHandler that is a C function pointer of type void(*VPEventHandler)(VPInstance);

I think I would build a dictionary associating to a VPInstance (void * pointer casted as integer) an instance of some PyEvent class which should contains itself the function click. Using that you can ensure that you need on one C function as callback.

In foo.pxd:

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

In 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)

I don't have a chance to test this, so I hope it more or less works. Also I would recommend asking on [email protected] as they are usually really helpful and more expert than me.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! This is the only Cython example I've seen that handles using pointers with python objects. Ie cast to <size_t>.
1

I eventually solved this by taking a similar approach to what hivert suggested, however I decided to switch to swig as it is specifically built for language bindings.

This is a trimmed down version of what I setup

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;
}
%}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.