Wrapping C++ class with callback methods

414 views
Skip to first unread message

Jesterscript

unread,
Jul 27, 2010, 10:27:16 PM7/27/10
to cython-users
Hello all, here is the problem:

I'm trying to wrap openFrameworks (openframeworks.cc) with Cython,
but, there is a class ofBaseApp that has some callback methods to be
overriden by deriving that class. Supose I have:

ofBaseApp::setup()
ofBaseApp::update()
ofBaseApp::draw()
ofBaseApp::mousePressed(int x, int y)

Now, I'm trying to wrap that class but, Is there any way (and I really
mean ANY, even if it needs some helper code to add) to 'capture' those
callbacks from Cython/Python world? The idea is that when the library
calls automatically this methods in a loop for a given instance of
ofBaseApp after some previous initialization, I need to hook those
callbacks from Cython/Python to execute python code, and get any
parameter value passed from the C++ side (as in the mousePressed
callback for example). So, is that posible?

Thanks in advance and I hope I have been clear enough.
Sorry for my poor english ;)

Robert Bradshaw

unread,
Jul 27, 2010, 11:20:05 PM7/27/10
to cython...@googlegroups.com

I think what you will have to do is create a stub class whose
overridden methods call some C function pointers. You can then
initialize and use this class from Cython.

- Robert

Pablo Andres Arrobbio

unread,
Jul 28, 2010, 9:03:10 AM7/28/10
to cython...@googlegroups.com
Thank you for your quick answer, but, could you please elaborate a little on that idea? What do you mean when you refer to C function pointers? Do you mean to call some of the auto generated C funcs by Cython? Is there any simple example I could follow?

Thanks

2010/7/28 Robert Bradshaw <robe...@math.washington.edu>

Robert Bradshaw

unread,
Jul 28, 2010, 2:16:58 PM7/28/10
to cython...@googlegroups.com
On Wed, Jul 28, 2010 at 6:03 AM, Pablo Andres Arrobbio
<pa.a...@gmail.com> wrote:
> Thank you for your quick answer, but, could you please elaborate a little on
> that idea? What do you mean when you refer to C function pointers? Do you
> mean to call some of the auto generated C funcs by Cython? Is there any
> simple example I could follow?

Sure. Warning, I have not even tried to compile the code below, but it
should be enough to get you started.

--------- wrapper.cc -----------

class CallbackWrapper: public BaseClass {
int (*my_func_ptr)(double, void*);
public:
CallbackWrapper(int (*f)(double, void*)) {
this->my_func_ptr = f;
}
int callback_stub(double x, void* data) {
return this->my_func_ptr(x, data);
}
};

-------- module.pyx ----------

cdef extern from "wrapper.cpp":
cdef cpp class CallbackWrapper:
CallbackWrapper(int (*f)(double void*))


cdef int my_cython_callback(double x, void* data):
[do callback stuff]

def registerCallback():
cdef CallbackWrapper *callback_object = new
CallbackWrapper(&my_cython_callback);
[register callback_object]

> Thanks
>
> 2010/7/28 Robert Bradshaw <robe...@math.washington.edu>
>>

Robert Bradshaw

unread,
Jul 28, 2010, 2:17:53 PM7/28/10
to cython...@googlegroups.com
On Wed, Jul 28, 2010 at 11:16 AM, Robert Bradshaw
<robe...@math.washington.edu> wrote:
> On Wed, Jul 28, 2010 at 6:03 AM, Pablo Andres Arrobbio
> <pa.a...@gmail.com> wrote:
>> Thank you for your quick answer, but, could you please elaborate a little on
>> that idea? What do you mean when you refer to C function pointers? Do you
>> mean to call some of the auto generated C funcs by Cython? Is there any
>> simple example I could follow?
>
> Sure. Warning, I have not even tried to compile the code below, but it
> should be enough to get you started.

And one more word of warning, you may need to worry about capturing
the GIL if you do anything with Python objects, if your callback is
being invoked from another thread.

- Robert

Pablo Andres Arrobbio

unread,
Jul 30, 2010, 6:56:01 PM7/30/10
to cython...@googlegroups.com
Thanks Robert, I've followed your example as a guide, but I get an error when trying to compile this:

----- ofBaseAppStub.h -----

#ifndef __OFBASEAPP_STUB_H__
#define __OFBASEAPP_STUB_H__

#include <ofBaseApp.h>

typedef void (*void_callback0_t)(void);

class ofBaseAppStub : public ofBaseApp
{   
public:

    ofBaseAppStub(void_callback0_t setup_cback);
    ~ofBaseAppStub();
   
    void setup();

private:
 
    void_callback0_t m_setup_cback;
};

----- ofBaseAppStub.cpp -----

#include <ofBaseAppStub.h>

ofBaseAppStub::ofBaseAppStub(void_callback0_t setup_cback)
{
    m_setup_cback = setup_cback;
}

ofBaseAppStub::~ofBaseAppStub()
{

}

void ofBaseAppStub::setup()
{
    this->m_setup_cback();
}

----- ofBaseAppStub.pxd -----

cdef extern from 'ofBaseAppStub.h':
    ctypedef void (*void_callback0_t)()
    ctypedef struct c_ofBaseAppStub 'ofBaseAppStub':
        pass
       
    ## Constructor.
    c_ofBaseAppStub *new_ofBaseAppStub 'new ofBaseAppStub' (void_callback0_t setup_cback)
    ## Destructor.
    void del_ofBaseAppStub 'delete' (c_ofBaseAppStub *ptr)

----- ofBaseApp.pyx -----

cimport ofBaseAppStub

cdef class oFBaseApp:
    cdef ofBaseAppStub.c_ofBaseAppStub *thisptr
   
    cdef void setup(void):
        print "setup from python!!"
        print "setup from python!!"
        print "setup from python!!"
        print "setup from python!!"
        print "setup from python!!"

    def __cinit__(self):
        self.thisptr = ofBaseAppStub.new_ofBaseAppStub(&self.setup)  # <-- Error comes from this line...

    def __dealloc__(self):
        ofBaseAppStub.del_ofBaseAppStub(self.thisptr)

----------------------------------------

I get this compile time error:

ofBaseApp.pyx:20:49: Cannot assign type 'void (*)(oFBaseApp)' to 'ofBaseAppStub.void_callback0_t'

Now, where is the type problem? And why 'setup' in ofBaseApp.pyx is reported as being of type 'void (*)(oFBaseApp)' if it is in fact of type 'void (*)(void)'?
Could you please help me with this? Thank you.


2010/7/28 Robert Bradshaw <robe...@math.washington.edu>

Robert Bradshaw

unread,
Jul 30, 2010, 7:05:09 PM7/30/10
to cython...@googlegroups.com

What you're really writing is

> cdef class oFBaseApp:
> cdef ofBaseAppStub.c_ofBaseAppStub *thisptr
>

> cdef void setup(self):
> print "setup from python!!"

which makes the problem clearer. Member functions always take a "self"
argument. What you might need to do is pass self (you may have to
declare it as a PyObject* in the C++ side, a oFBaseApp on the Cython
side, or do some casting) to your C++ class, which would then store it
an invoke it passing the "self" back as the first parameter. Be sure
to incref that object in your C++ constructor, and decref it in its
destructor (or otherwise make sure that self doesn't go away too
soon).

- Robert

Pablo Andres Arrobbio

unread,
Jul 31, 2010, 3:29:24 PM7/31/10
to cython...@googlegroups.com
Sorry but I don't know Python internals very much, and I don't understand how to adapt the code. Could you give me an example to follow? Thanks again.

2010/7/30 Robert Bradshaw <robe...@math.washington.edu>
Reply all
Reply to author
Forward
0 new messages