This article is more than 1 year old
Cross platform development for Windows and Mac OS X
Killing two birds with one stone
Hands on I’ve been fascinated by cross-platform programming for more years than I care to remember, and my interest has recently been sharpened by the acquisition of a number of Apple Macs – both Intel and PPC (PowerPC). This article focuses primarily on some technical aspects of Qt, Trolltech’s cross-platform C++ toolkit which, as you may know, is the architectural core behind the KDE desktop on Linux. At the end, I show how easy it is to create a simple application without writing a line of code.
Making a MOC-ery Of It All…
One of the most interesting aspects of Qt is the signal-slot mechanism. This connects an event such as a button press, mouse-click etc., to a consumer of that event. Assuming that you’re familiar with C# (and by now, you jolly well should be!) you’ll know that the VS.NET form designer connects an event to its consumer by doing something like this:
this.OKButton.Click += new System.EventHandler(this.OKButton_Click);
Here, anytime the OKButton
is clicked, the OKButton_Click
method will be called. Under the hood, this is accomplished with C#’s delegate mechanism, which provides a type-safe link between the event consumer and producer. .NET delegates are also multicast, hence the +=
operator in the code above; one event can be “broadcast” to multiple recipients.
All fine and dandy, but not hugely portable unless you’re using the Mono C# compiler (more on that here). Qt’s creators wanted to be able to compile code using any reasonably modern, bog-standard C++ compiler. Accordingly, they created moc. Moc is a pre-processor which converts the special C++ “superset” used by Qt into plain-vanilla C++.
Note: If this seems odd, then think back to 1983 and the advent of C++. Stroustrup’s original Cfront pre-processor effectively translated C++ into straight C, making the new language available on any platform with a decent C compiler.
To get some idea of what moc does, (that’s Meta-Object Compiler, by the way) take a look at the following class declaration taken from Trolltech’s documentation:
class MyClass : public QObject { Q_OBJECT public: MyClass(QObject *parent = 0); ~MyClass(); signals: void mySignal(); public slots: void mySlot(); };
The signals
part of the class declaration identifies any signals that can be raised by the class. In this case, we have one signal; mySignal
. The QLCDNumber
class for example (one of the many user-interface gadgets in Qt) will emit an overflow
signal if it’s asked to display a too-large number. You can thus think of signals as being somewhat like exceptions. However, a raised signal will do nothing if it’s not connected to a slot, unlike the way in which exceptions “percolate up” the calling stack until they find a handler. A signal is also very lightweight, requiring no special run-time support from the C++ library. Most important of all, the resulting standard C++ is completely portable.
Most of the grungy-looking stuff you can see is provided by the all-important Q_OBJECT
macro, which must be the first thing in the class declaration. The class must inherit (directly or indirectly) from QObject,
but you’ve probably already figured that out. In Figure 1 (the moc output screenshot), look in particular at the declaration of MyClass:mySignal.
This calls QMetaObject::activate
to send the signal to a connected slot. The other side of the coin is that an incoming signal maps down to a call on MyClass::qt_metacall
which results in mySlot
being invoked.
Note: Well, actually, it’s a tad more complex than that. You’ll notice that MyClass::qt_metacall
can also invoke mySignal
. That’s because another class might potentially want to raise your signal/s. If you don’t fancy someone else raising your signals (and who would?), you can always mark them private in the class declaration. Similarly, MyClass::qt_metacall
is also used to change property values. (Qt supports properties which can be browsed at design-time.)