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.