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*?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With