Cython callback works correctly for function, but

2019-04-06 10:24发布

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*?

1条回答
唯我独甜
2楼-- · 2019-04-06 11:03

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.

查看更多
登录 后发表回答