Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cython callback works correctly for function, but not for bound method

I am implementing a cython-based interface to a C++ library. I implemented a callback system that works with normal functions but fails strangely when passing in bound methods. Here is a portion of my cython code:

cdef extern from "VolumeHapticTool.h":
    cdef cppclass HDButtonEvent:
        bool isPressed()
        unsigned int getButtonId()
        Vec3[float] getPosition()

    ctypedef void (*HDButtonCallback)(HDButtonEvent, void *)

cdef extern from "Scene.h":
    cdef cppclass Scene:
        Scene()
        void setDrillButtonCallback( HDButtonCallback, void*)

cdef void pyHDButtonCallback(HDButtonEvent e, void *user_data):
    print <object>user_data
    (<object>user_data)( (e.isPressed(), e.getButtonId(), topy_vec3f(e.getPosition())) )

cdef class pyScene:
    cdef Scene * m_scene
    def __init__(self):
        self.m_scene = new Scene()

    def __del__(self):
        del self.m_scene

    def setDrillButtonCallback(self, func):
        print func
        self.m_scene.setDrillButtonCallback(pyHDButtonCallback, <void*>func)

And here is how I'm trying to call it:

class RenderCanvas(GLCanvas):
    def __init__(self, parent):
        self.scene = cybraincase.pyScene()
        self.scene.setDrillButtonCallback(self.OnDrillButtonPress)

    def OnDrillButtonPress(self, event):
        print event 

When this code is run initially, this is printed: <bound method RenderCanvas.OnDrillButtonPress of <UI.RenderCanvas; proxy of <Swig Object of type 'wxGLCanvas *' at 0x42b70a8> >>

That seems right. The problem is when the callback is triggered, this is printed: <bound method Shell.readline of <wx.py.shell.Shell; proxy of <Swig Object of type 'wxStyledTextCtrl *' at 0x3a12348> >>

A totally different bound method is being called. However, when I make OnDrillButtonPress a static method, <function OnDrillButtonPress at 0x042FC570> is printed both on initialization and triggering calls.

Is there an incompatibility with saving bound methods as void*?

like image 557
tkerwin Avatar asked Sep 06 '11 22:09

tkerwin


1 Answers

Thanks to comments by Stefan Behnel and Mark Florisson on the cython discussion group, the problem is that the reference to my bound method was going out of scope and getting garbage collected.

The solution was to do this:

cdef class pyScene:
    cdef Scene * m_scene
    cdef object drill_button_func

    def setDrillButtonCallback(self, func):
        self.m_scene.setDrillButtonCallback(pyHDButtonCallback, <void*>func)
        self.drill_button_func = func

By keeping a reference to the bound method in the class, it doesn't get cleaned up until it stops being used.

like image 138
tkerwin Avatar answered Oct 20 '22 02:10

tkerwin