I have been working with Cython in an attempt to interface with a library written in c++. So far things are going pretty good, and I can effectively use MOST functions within th
I've recently been in the situation where I also had to interface an existing C++ library with Python using Cython, making an intensive use of events/callbacks. It was not that easy to find sources about this and I would like to put all of this together here :
First of all, the wrapping C++ callback class (based on 'double (METHOD)(void)' prototype, but it could have been templatized, since Cython can handle templates) :
ALabCallBack.h :
#ifndef ALABCALLBACK_H_
#define ALABCALLBACK_H_
#include
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' :: The pattern/converter method to fire a Python (=Method) object method from C typed infos
'method' :: The effective method passed by the Python user (=user_data)
'parameter' :: The parameter to be passed to the 'method'
Now, we need to implement the .pyx file...
Our base prototype :
ctypedef double (*Method)(void *param, void *user_data)
Then, we provide a Cython wrapper 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 pattern/converter method to be used for translating C typed prototype to a Python object call :
cdef double callback(void *parameter, void *method):
return (
Now let's embed this features in a 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, 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(parameter)
Edit : Better typing for execute function : def execute => cpdef double
That's it. Call it like doing something like that :
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 python is implicitly typed, we can access the properties of the 'obj' object related to the class of the object passed as argument when the time comes to fire the callback.
It can be quite easily adapted for pure C implementation. Please, tell me if you can see any possible enhancement for this (in my case, perfs are very crucial since events are fired intensively).