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.