Monday 19 October 2009

Piggyback

Usually when I'm wrapping a C++ class which requires me to callback into BlitzMax, I will subclass it, and override the specific methods to make those callbacks.
But sometimes, that's just not possible, because the object we want to have access to those callbacks has been created by something else - i.e. not by us!

It's always been a bit of a conundrum for me when I've come up against a class like this, as to what to do. Fortunately, I've recently conjured up a reasonably elegant solution which also happens to fit rather well into Qt's Signal/Slot model.

Essentially what I'm doing is piggybacking onto the object that I want, with a subclassed QObject of my own. We intercept the returned object, do a quick check to see if we are already piggybacking it, and if not, we attach ourselves to it, and pass it on.
During the attach phase, we plug into the object's destruction method, which we will use to detach and kill our own object. So in essence, once we are piggybacking, the object we've attached to takes ownership of our object. Fire-and-forget, if you will :-)

As is always the case, it is probably better to explain all this with an example.

For this example, we'll take a look at the QAction wrapper, which also happens to be the reference implementation.
Our wrapper class has a static method ('link') for checking if the object is already attached :

void MaxQAction::link(QAction * a) {

BBObject * handle = qfind(a);

if (handle == &bbNullObject) {

MaxQAction * action = new MaxQAction(a);

}

}


If we don't find the object in our internal map, we make the attachment. The MaxQAction() constructor creates a BlitzMax QAction object instance, and binds it to the original object. It then connects all the object's signals to our wrapper class slots.

When we receive a potentially new QAction, we simply call 'link', and the attaching is handled silently in the background :

QAction * action = group->addAction(a);

MaxQAction::link(action);

return action;


Most importantly, all this is done behind the scenes, so that all you need to do is write your BlitzMax code, and it will all "just work".