The observer pattern appears frequently in my C++ project, which I now want to expose to the Python interpreter via Cython bindings. I tried to construct a minimal example illustrating the situation. A Spectacle accepts any object derived from the abstract base class Observer, such as an Onlooker. When we call Spectacle::event(), each registered observer is notified.
This is the content of the file ObserverPattern.h:
class Spectacle {
private:
std::vector<Observer*> observers;
public:
Spectacle() {};
virtual ~Spectacle() {};
virtual void registerObserver(Observer* observer) {
this->observers.push_back(observer);
}
virtual void event() {
std::cout << "event triggered" << std::endl;
for (Observer* observer : this->observers) {
observer->onEvent();
}
}
};
class Observer {
public:
Observer() {};
virtual ~Observer() {};
virtual void onEvent() = 0;
};
class Onlooker : public Observer {
public:
Onlooker() {};
virtual ~Onlooker() {};
virtual void onEvent() {
std::cout << "event observed" << std::endl;
}
};
And this is the content of my .pyx file, containing the bindings:
cdef extern from "ObserverPattern.h":
cdef cppclass _Spectacle "Spectacle":
_Spectacle() except +
void registerObserver(_Observer* observer)
void event()
cdef extern from "ObserverPattern.h":
cdef cppclass _Observer "Observer":
_Observer() except +
void onEvent()
cdef extern from "ObserverPattern.h":
cdef cppclass _Onlooker "Onlooker":
_Onlooker() except +
void onEvent()
cdef class Spectacle:
cdef _Spectacle _this
def event(self):
self._this.event()
def registerObserver(self, Observer observer):
self._this.registerObserver(observer._this)
cdef class Observer:
cdef _Observer* _this # must be a pointer because _Observer has pure virtual method
cdef class Onlooker(Observer):
pass # what should be the class body?
This does compile, but segfaults when event() is called and the observers are notified:
>>> spec = CythonMinimal.Spectacle()
>>> look = CythonMinimal.Onlooker()
>>> spec.registerObserver(look)
>>> spec.event()
event triggered
Segmentation fault: 11
What is the problem here and how could a fix look like?
Your problem is essentially "implement a C++ interface in Python".
The only portable way to do this is to write an actual C++ class
that will call back into Python.
Cython has undocumented experimental_cpp_class_def option that allows to
create C++ classes using Cython syntax. It's not pretty (IMO), but it works
for many scenarios.
Here is how you could implement Observer that delegates to the provided
Python callable:
from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
cdef cppclass ObserverImpl(_Observer):
PyObject* callback
__init__(object callback): # constructor. "this" argument is implicit.
Py_INCREF(callback)
this.callback = <PyObject*>callback
__dealloc__(): # destructor
Py_DECREF(<object>this.callback)
void onEvent():
(<object>this.callback)() # exceptions will be ignored
And that's how you could use it:
def registerObserver(self, callback not None): # user passes any Python callable
self._this.registerObserver(new ObserverImpl(callback))
C++ objects, just like C structures, can't hold Cython-managed object
references. That's why you have to use PyObject* field and manage reference
counting yourself. Inside methods you can, of course, cast to and use any Cython feature.
Another tricky moment is exception propagation. onEvent() method, being defined in C++, can't propagate Python exceptions. Cython will simply ignore exceptions it can't propagate. If you want to do better, catch them yourself and store somewhere for later inspection or rethrow as C++ exception. (I think it's impossible to throw C++ exceptions in Cython syntax, but you can call an external throwing helper function.)
If your observer has more than one method, then callback would be a Python class and instead of calling it directly you would call its methods, like (<object>this.callback).onEvent().
Obviously, ObserverImpl can also be coded directly in C++. Py_INCREF, Py_DECREF and PyObject_Call/PyObject_CallMethod are the only Python APIs necessary.
Related
I have an observer (or "listener") pattern implemented in my code as such:
struct EntityListener
{
public:
virtual void entityModified(Entity& e) = 0;
};
class Entity
{
public:
Entity();
void setListener(EntityListener* listener);
private:
EntityListener* m_listener;
};
Now, this works in C++; the Entity class calls the entityModified() method whenever it needs. Now, I'd like to transfer some of the functionality to Lua, and among those function points is this listener callback. The entities are now created from the Lua scripts. The question is, how do I achieve the listener functionality in Lua?
For example, the Lua script currently does something like this:
function initializeEntity()
-- The entity object is actually created in C++ by the helper
Entity = Helper.createEntity()
-- Here I'd like to hook a Lua function as the Entity's listener
end
One possible solution is to have a LuaListener class in your C++ code that contains a "pointer" to the Lua function, and a Lua-specific setListener function that is called from the Lua script that takes a Lua function as argument, and creates a LuaListener instance and passes that to the actual C++ setListener.
So the Lua code would look something like
function onModified(entity)
-- ...
end
function initializeEntity()
entity = Helper.createEntity()
entity.setListener(onModified)
end
And the C++ code would look something like (pseudoish-code only):
class LuaListener : public EntityListener
{
private:
lua_State* state;
std::string funcName;
public:
void entityModified(Entity& e)
{
// Call function `funcName` in `state`, passing `e` as argument
}
};
class LuaEntity : public Entity
{
public:
void setListenerLua(state, funcName, ...)
{
Entity::setListener(new LuaListener(state, funcName, ...));
}
};
How can I register call back on JNI from android app? My requirement is, I want to make a JNI call from Android application and want to register a call back, so that I can get call back on java application from JNI.
Thanks
First, check out Swig. It wraps C++ in Java and also has a "director" fcility that makes it easier to call C++ methods from Java.
With raw JINI, you cannot do this directly. Although the RegisterNatives() call can be made to bind a native method, it cannot be changed. If you want to call a C function by pointer, you will need to do this in two steps. I am glossing over a lot, because JNI is incredibly verbose and tedious. The basic trick is, wrap the C function pointer in a java long.
First declare a java class:
public class Callback {
public Callback(long cMethodPointer) { this.cMethod = cMethod; }
public void doCallback() { callCMethod(cMethod); }
public static native void callCMethod(long cMethod);
public long cMethod;
}
Run javah on that class file, and you'll get a stub header generated that looks like:
JNIEXPORT void JNICALL Java_Callback_callCMethod
(JNIEnv *, jclass, jlong);
Implement that method in a DLL/so:
typedef void (*FP)();
JNIEXPORT void JNICALL Java_Callback_callCMethod
(JNIEnv *, jclass, jlong pointer) {
((FP)(void*)pointer)();
}
and call System.loadLibrary() on that DLL/so in your Java main() method.
Finally, from your C/C++ code, you will need to create an instance of the Callback object via JNI, and pass the pointer to an actual method to it:
void MyFunction() { ... }
void Register() {
jclass cls = env->FindClass("Callback");
jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
jvalue arg;
arg.j = (jlong)(void*)MyFunction;
jobject callback = env->NewObjectA(confCls, mid, &arg);
}
So this gives you a brand new Callback object that points to your C function! But then, how do you do anything with that? Well, you have to pass the Callback object to Java via JNI (step omitted), so that your Java code has the Callback object from which to call your C method:
public class Foo {
public Callback callback;
public void doSomeStuff() {
...
callback.doCallback();
}
}
Can someone suggest a way to manipulate c++ objects with Cython, when
the c++ instance of one class is expected to feed the constructor of another wrapped
class as described below?
Please look at the note on the pyx file for the class PySession, which
takes a python PyConfigParams object as argument and then needs to
extract values from it in order to construct a c++ ConfigParams
object. the ConfigParams object is then used to feed the constructor
of Session.
It would be ideal to have a procedure which would allow me to "inject"
the ConfigParams c++ object wrapped by the PyConfigParams object
directly into the constructor of Session, without having to dismantle
it first and then building a new c++ object to feed the constructor.
This works, of course. However, it is a cumbersome, sort of brutal way to implement this solution, not to mention unreliable.
I am aware of PyCapsule, however it may require to touch the c++ headers, which is something I can not do.
Related to this, but a different question is: what if I need a wrapped
class (let's say here PySession) to simulate the behavior of the C++
api by returning a ConfigParams instance? would I need to do the
reverse and dismantle the c++ object to build a Python PyConfigParams
which would then be returned to the Python user in the Python world?
Any suggestions are very welcome!
Thank you!
Let's suppose I have two c++ classes named ConfigParams and Session.
An instance of ConfigParams is used to feed the constructor of the
Session class:
C++ classes
ConfigParams class
// ConfigParams.h
#include <iostream>
using namespace std;
class ConfigParams
{
int parameter1;
public:
ConfigParams(int par1) { this->parameter1 = par1;}
int getPar1() { return this->parameter1; }
};
Session class
// Session.h
#include <iostream>
using namespace std;
#include "configparams.h"
class Session
{
int sessionX;
public:
Session(ConfigParams parameters) { this->sessionX = parameters.getPar1(); }
void doSomething();
};
void Session::doSomething()
{
cout << "Session parameters set as: " << endl;
cout << "X = " << this->sessionX << endl;
}
Cython pyx and pxd files for the above classes:
PyConfigParams
# configparams.pxd
cdef extern from "configparams.h":
cppclass ConfigParams:
ConfigParams(int par1)
int getPar1()
# configparams.pyx
cdef class PyConfigParams:
cdef ConfigParams* thisptr
def __cinit__(self, i):
self.thisptr = new ConfigParams(<int> i)
def getPar1(self):
return self.thisptr.getPar1()
PySession class
# session.pxd
from configparams cimport *
cdef extern from "session.h":
cdef cppclass Session:
Session(ConfigParams parameters)
void doSomething()
# session.pyx
cdef class PySession:
cdef Session* thisptr
def __cinit__(self, pars):
# Note that here I have to extract the values
# from the pars (python PyConfigParams object)
# in order to build a c++ ConfigParams object
# which feeds the c ++ constructor of Session.
cdef ConfigParams* cpppargsptr = new ConfigParams(<int> pars.getPar1())
self.thisptr = new Session(cpppargsptr[0])
def doSomething(self):
self.thisptr.doSomething()
Solution:
Forward declare PyConfigParams in the configparams.pxd module (so it can be invoked from the session.pyx module)
# configparams.pxd
cdef extern from "configparams.h":
cppclass ConfigParams:
ConfigParams(int par1)
int getPar1()
cdef class PyConfigParams:
cdef ConfigParams* thisptr
Import PyConfigParams in the session.pyx module, and cast the argument for the constuctor, this will grant access to the PyConfigParams pointer to the c++ object, which will need to be dereferenced.
# session.pyx
from configparams cimport PyConfigParams
from cython.operator cimport dereference as deref
cdef class PySession:
cdef Session* thisptr
def __cinit__(self, PyConfigParams pars):
self.thisptr = new Session(deref(pars.thisptr))
def doSomething(self):
self.thisptr.doSomething()
I'm porting a Python extension module written in C++ from Boost.Python to SWIG.
The C++ code defines an abstract class X with a static factory method
class X {
public:
static X* create(const char* descr);
...
};
The factory method returns a pointer to an instance of some derived class.
With Boost.Python you can wrap the C++ class X in a Python class X that has an
__init__(self, descr)
method that calls X::create. In fact, it is done as follows:
namespace bp = boost::python;
bp::class_<X>("X", boost::no_init)
.def("__init__", bp::make_constructor(&X::create))
...
Is there a way of doing the same thing with SWIG?
As suggested, it is better to use __new__ for controlling how a class is created. In SWIG, you should create an interface (.i) file that looks as follows:
%extend X {
static X * __new__(const char *desc) { return create(desc); }
};
I am developing a server-client application in which the client calls a server's API which gives a Python interface for user input. It means the client interface and server interface is written in Python whereas the socket code is in C++.
On the server side:-
I have a class, Test, in C++ and this class is inherited in Python named TestPython using director feature of SWIG.
Also I have an exception class MyException in C++.
Now a function of TestPython class throws MyException() from Python code.
I want to handle exception thrown from Python in C++ code using SWIG.
Below is code snippet:
C++ Code-
class MyException
{
public:
string errMsg;
MyException();
MyException(string);
~MyException();
};
class Test
{
int value;
public:
void TestException(int val);
Test(int);
};
Python Code -
class TestPython(Test):
def __init__(self):
Test.__init__(self)
def TestException(self,val):
if val > 20:
throw MyException("MyException : Value Exceeded !!!")
else:
print "Value passed = ",val
Now, if the TestException() function is called, it should throw MyException. I want to handle this MyException() exception in my C++ code.
So can anyone suggest my how to do that, I mean what should I write in my *.i(interface) file to handle this.
The above TestException() written in Python is called by the client, so I have to notify the client if any exception is thrown by the server.
To do this you basically need to write a %feature("director:except") that can handle a Python exception and re-throw it as a C++ one. Here's a small but complete example:
Suppose we have the following header file we wish to wrap:
#include <iostream>
#include <exception>
class MyException : public std::exception {
};
class AnotherException : public std::exception {
};
class Callback {
public:
virtual ~Callback() { std::cout << "~Callback()" << std:: endl; }
virtual void run() { std::cout << "Callback::run()" << std::endl; }
};
inline void call(Callback *callback) { if (callback) callback->run(); }
And this Python code that uses it:
import example
class PyCallback(example.Callback):
def __init__(self):
example.Callback.__init__(self)
def run(self):
print("PyCallback.run()")
raise example.MyException()
callback = PyCallback()
example.call(callback)
We can define the following SWIG interface file:
%module(directors="1") example
%{
#include "example.h"
%}
%include "std_string.i"
%include "std_except.i"
%include "pyabc.i"
// Python requires that anything we raise inherits from this
%pythonabc(MyException, Exception);
%feature("director:except") {
PyObject *etype = $error;
if (etype != NULL) {
PyObject *obj, *trace;
PyErr_Fetch(&etype, &obj, &trace);
Py_DecRef(etype);
Py_DecRef(trace);
// Not too sure if I need to call Py_DecRef for obj
void *ptr;
int res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_MyException, 0);
if (SWIG_IsOK(res) && ptr) {
MyException *e = reinterpret_cast< MyException * >(ptr);
// Throw by pointer (Yucky!)
throw e;
}
res = SWIG_ConvertPtr(obj, &ptr, SWIGTYPE_p_AnotherException, 0);
if (SWIG_IsOK(res) && ptr) {
AnotherException *e = reinterpret_cast< AnotherException * >(ptr);
throw e;
}
throw Swig::DirectorMethodException();
}
}
%feature("director") Callback;
%include "example.h"
Which handles an error from a director call, looks to see if it was one of our MyException instances and then re-throws the pointer if it was. If you have multiple types of exception being thrown then you will probably need to use PyErr_ExceptionMatches to work out what type it is first.
We could throw also by value or reference using:
// Throw by value (after a copy!)
MyException temp = *e;
if (SWIG_IsNewObj(res))
delete e;
throw temp;
instead, but note that if you threw a subclass of MyException in Python this would fall foul of the object slicing problem.
I'm not quite sure if the code is 100% correct - in particular I think the reference counting is correct, but I could be wrong.
Note: In order to make this example work (%pythonabc wouldn't work otherwise) I had to call SWIG with -py3. This in turn meant I had to upgrade to SWIG 2.0, because my installed copy of Python 3.2 had removed some deprecated functions from the C-API that SWIG 1.3.40 called.