How to use a Cython cdef class member method in a native callback

Viewed 1305

I have a C++ library that is heavily callback driven. The callbacks are registered as std::function instances.

A callback registration function might look something like:

void register(std::function<void(int)> callback);

I can register plain cdef functions by making libcpp.functional.function objects. Say I have this callback:

cdef void my_callback(int n):
    # do something interesting with n

I can register it succesfully:

cdef function[void(int)]* f_my_callback = new function[void(int)](my_callback)
register(deref(f_my_callback))

The problem is that I want to register cdef class methods as the callback. Let's say I have a class:

cdef class MyClass:

    cdef function[void(int)]* f_callback
    py_callback = lambda x: None

    # some more init/implementation

    def __init__(self):
        # ** this is where the problem is **
        self.f_callback = new function[void(int)](self.member_callback)
        register(deref(self.f_callback))

    cdef void member_callback(self, int n) with gil:
        self.py_callback(n)

    def register_py(self, py_callback):
        self.py_callback = py_callback

the self.f_callback = new function[void(int)](self.member_callback) line doesn't work because the function[T] constructor is seeing the MyClass parameter (self). In regular python, doing self.member_callback is basically equivalent to partially applying self to MyClass.member_callback, so I thought that this would be fine, but it is not.

I made a typedef:

ctypedef void (*member_type)(int)

Then if I cast, I can get it to compile:

self.f_callback = new function[void(int)](<member_type>self.member_callback)

But when callbacks actually come in, everything is broken. Doing anything with self results in a segfault.

What is the 'right' way to pass a cython class member method as a C/C++ callback?

EDIT:

Just to clarify, this question is specifically about passing member methods (i.e. function with self as the first argument) to C++ functions expecting a parameter of type std::function.

If I decorate the member_callback with @staticmethod and remove all references to self it all works as expected. Unfortunately, the method needs to have access to self to correctly execute the logic for the class instance.

2 Answers
Related