Showing posts with label slots. Show all posts
Showing posts with label slots. Show all posts

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".


Thursday, 6 August 2009

Slots & Signals for BlitzMax

One of the very cool features of Qt is its Signal/Slot mechanism. It's also known as the Observer Pattern, in the Design Patterns book.
A signal is a function which is called as the result of some kind of event or action. For example, the click of a push button. A slot is a function which you may connect to a signal, so that whenever the signal is actioned, the slot function is called.

This is all well, good and easy to implement in C++ as part of Qt, because there are build tools which are there to generate glue framework code for you.
But of course, having to run extra utilities when trying to build your BlitzMax application wouldn't be very useful. One should simply be able to knock some code together and run it.

After mulling over this obvious problem for a while, I came up with what I think is quite a clever system which works in a functionally similar way to the C++ code....
On the low-level side, every C++ signal needs to be connected to a BlitzMax utility function.
On the BlitzMax side, the base QObject holds a list of all connections to any of its own signals. Instead of connecting a callback function pointer (like we would with wxMax), we rather pass in the name of the method to connect the signal to. Using reflection we can map the method name to the actual method which we will later call.
When the signal is actioned, every method (slot) which has connected to the signal is called in turn.
It all works seamlessly :-)

The sliders example is a very good demonstration of the system in action, and even shows the ability to create your own BlitzMax-based signal method.

Although we aren't really using the Qt signal/slot framework properly - since the real signal and slot connections are hard-coded into the modules - it functions in exactly the same way, which is really all we need to worry about ;-)