Cython - callback implementation

I worked with Cython trying to interact with a library written in C ++. So far, everything is going well, and I can effectively use the MOST functions in the library. My only problem is implementing callbacks. The library has 4 function definitions that look something like this:

typedef void (*Function1)(const uint16_t *data, unsigned width, unsigned height); void SetCallBack(Function1); 

So, to implement them, I decided that with cython I would do something like this:

 ctypedef void (*Function1)(unsigned short *data, unsigned width, unsigned height); cdef extern from "lib.hpp": void SetCallBack(Function1) 

What really compiles correctly, however, for my life I cannot think of how to really implement this in such a way that the callback works. At first, I tried to create a function that would just call it, just like you would for any other function, by inventing the following:

 def PySetCallBack(Func): SetCallBack(Func) 

but this gives me a (predictable) error:

"Cannot convert Python object to" Function1 "

so yes I'm here. If anyone has experience setting up callbacks in Cython, I would really appreciate any help. Thanks.

Edit : Following your advice, I created an intermediate cdef function that looks like this:

 cdef void cSetCallBack(Function1 function): SetCallBack(function) 

It seems to bother me ... Closer? At least get another error:

 error: invalid conversion from 'void (*)(short unsigned int*, unsigned int, unsigned int)' to 'void (*)(const uint16_t*, unsigned int, unsigned int)' 

Now, as far as I can tell, these types are identical, so I can not understand what is happening.

Edit2 : Fixed this problem by declaring a new typedef:

 ctypedef unsigned short uint16_t 

and using this as an argument to call, but apparently it didn’t quite come nearer, it just took me to the side track, because when I try to call this function I get the same "Cannot convert Python object to Error" Function1 " again.

So, I almost returned to where I started. The only thing I can do now is to explicitly indicate that the python object comes in as a c function like this, but to be honest, I have no idea how I would do it.

Edit third : Okay, after analyzing your answer, I finally get it, and it works, so cheers and much more. What I ended up creating a function like this:

 cdef void cSetCallback(Function1 function): SetCallback(function) cdef void callcallback(const_ushort *data, unsigned width, unsigned height): global callbackfunc callbackfunc(data,width,height) cSetCallback(callcallback) def PySetCallback(callbackFunc): global callbackfunc callbackfunc = callbackFunc 

So, the only problem is that it cannot convert const_ushort * data to a python object, but this is another problem completely, so I think this problem is solved. Many thanks.

+8
c ++ python cython
source share
2 answers

If you can change the library to determine:

 typedef void (*Function1)(const uint16_t *data, unsigned width, unsigned height, void *user_data); void SetCallBack(Function1, void*); 

instead, I'm afraid you're out of luck. If you have void* , than you define a function that calls an object called by python with the correct arguments and SetCallBack with that function and called python.

If you cannot, but the callback is global (it looks like), you can create a global variable to hold the python object. Than you will again create a function to call the python object and pass it to SetCallBack , and your PySetCallback simply establish globality and ensure that the proper function is registered.

If the callback is context-specific, but you don’t have a way to pass it a "user data" pointer, I’m afraid you are out of luck here.

I know python and C ++, but not cython, so I don't know if you can create a function in cython, or you have to write in C ++.

+5
source share

Recently, I was in a situation where I also had to interact with an existing C ++ library with Python using Cython, intensively using events / callbacks. It was not easy to find sources about this, and I would like to collect all of this here:

First of all, a C ++ callback class based on the prototype 'double (METHOD) (void)', but it could have been planned since Cython can handle patterns):

ALabCallBack.h:

 #ifndef ALABCALLBACK_H_ #define ALABCALLBACK_H_ #include <iostream> using namespace std; namespace elps { //template < typename ReturnType, typename Parameter > class ALabCallBack { public: typedef double (*Method)(void *param, void *user_data); ALabCallBack(); ALabCallBack(Method method, void *user_data); virtual ~ALabCallBack(); double cy_execute(void *parameter); bool IsCythonCall() { return is_cy_call; } protected: bool is_cy_call; private: //void *_param; Method _method; void *_user_data; }; } /* namespace elps */ #endif /* ALABCALLBACK_H_ */ 

ALabCallBack.cpp:

 #include "ALabCallBack.h" namespace elps { ALabCallBack::ALabCallBack() { is_cy_call = true; }; ALabCallBack::~ALabCallBack() { }; ALabCallBack::ALabCallBack(Method method, void *user_data) { is_cy_call = true; _method = method; _user_data = user_data; }; double ALabCallBack::cy_execute(void *parameter) { return _method(parameter, _user_data); }; } /* namespace elps */ 

Where:

  • 'callback' :: Template / converter method for starting Python (= Method) object method from C printed information

  • 'method' :: Efficient method passed by the Python user (= user_data)

  • 'parameter' :: The parameter to pass to the method

Now we need to implement the .pyx file ...

Our basic prototype:

 ctypedef double (*Method)(void *param, void *user_data) 

Then we provide the Cython shell for the C ++ class:

 cdef extern from "../inc/ALabCallBack.h" namespace "elps" : cdef cppclass ALabCallBack: ALabCallBack(Method method, void *user_data) double cy_execute(void *parameter) 

The template / converter method that will be used to translate the C-typed prototype into a Python object call:

 cdef double callback(void *parameter, void *method): return (<object>method)(<object>parameter) 

Now paste these functions into the Cython class:

 cdef class PyLabCallBack: cdef ALabCallBack* thisptr def __cinit__(self, method): # 'callback' :: The pattern/converter method to fire a Python # object method from C typed infos # 'method' :: The effective method passed by the Python user self.thisptr = new ALabCallBack(callback, <void*>method) def __dealloc__(self): if self.thisptr: del self.thisptr cpdef double execute(self, parameter): # 'parameter' :: The parameter to be passed to the 'method' return self.thisptr.cy_execute(<void*>parameter) 

Edit: it is better to type for the execute function: def execute => cpdef double

What is it. Name it how to do something like this:

 def func(obj): print obj obj.Test() # Call to a specific method from class 'PyLabNode' return obj.d_prop n = PyLabNode() # Custom class of my own cb = PyLabCallBack(func) print cb.execute(n) 

As an implicit python input, we can access the properties of the obj object associated with the class of the object passed as an argument when the time comes to call the callback.

It can be easily adapted for a clean implementation of C. Please tell me if you can see any possible improvement for this (in my case, the performances are very important as events are intensely activated).

+8
source share

All Articles