Archive for Programming

Checking for if the app is a GUI app

#ifdef QT_GUI_LIB
# include <QApplication>
#endif

inline bool is_gui_app()
{
#ifdef QT_GUI_LIB
        return (QApplication::type() != QApplication::Tty);
#else
        return false;
#endif
}

This is enough if you don’t plan to actually touch the Qt GUI API. Otherwise, you’ll have to drag that QT_GUI_LIB ifdef all over your code.

(I’m going to use this to determine whether or not I should set up the Windows console control handler.)

Comments

More on Process Termination

Alright, after a lot of research, I’ve concluded that cross-platform process termination is a tricky beast. It is not possible to have a clean and balanced solution for all platforms, but at the end of this post you’ll find a decent guideline.

On Windows, at the lowest level, a process can only be terminated abruptly, with no chance for the process to cleanup. In other words, it is like Unix’s kill -9. To have a clean termination, the process has to be hinted somehow, and this differs depending on the situation. A GUI app can have a WM_CLOSE event sent to its window. A console app cannot receive WM_CLOSE, so Windows has a separate facility (via SetConsoleCtrlHandler) that the app can use for determining if the parent console window is being closed, or if Ctrl-C has been pressed. What’s interesting about these events, particularly WM_CLOSE, is that they don’t explicitly mean a process should end. For example, Psi, with the system tray active, will never quit from a WM_CLOSE. If the app doesn’t have a GUI window or a console, then all you can do is terminate the process. This is why the Windows Task Manager has two listings from which to end tasks/processes. One sends WM_CLOSE (and, in the case of a console, the proper notification is relayed to the app running inside it), the other outright kills the process.

On Unix (which includes Mac OS X), there is SIGTERM, and it works for any process, console or GUI. The SIGTERM signal is a request to terminate, giving the app the opportunity to perform last minute tasks.

Now, in addition to process termination, there is the notion of “window system shutdown”, which is supported on Windows, X11, and Mac. This is a very important topic, because, at least on Windows and X11, handling of the window system shutdown is generally required for a clean exit (I say “generally”, because on X11 you have a second chance, I’ll explain next). As I already discussed, there is no mechanism to be friendly terminated on Windows. And so it should not be a surprise that Windows uses another hint here, WM_ENDSESSION, to tell you that the system is shutting down. In fact, it first uses a different hint, WM_QUERYENDSESSION, to give your app a chance to perform user interaction (e.g. “Save this Document?”) and possibly reject the shutdown.

All three window systems have varying features. X11 is the most advanced here, and actually has two phases, plus a mechanism for storing session information (like window sizes/positions). I suspect Windows and Mac don’t have advanced session management because they are not remote and can therefore just suspend/hibernate (not to mention that suspending an X server is a rather recent thing). The reason you must handle these shutdown events is because Windows has no other way of terminating your app in a friendly way, and with X11 your app probably won’t get a SIGTERM when the server shuts down. On Mac, I don’t know what happens. Therefore, because I don’t have another answer for you, you must handle the shutdown event on Mac. :)

Now, you might be wondering about why you don’t get a SIGTERM on unix when X11 goes down. Well, think about it, the X server (graphic display) and X client (your app) don’t need to be in the same place. Losing the X server simply means losing a socket. This doesn’t rule out the possibility of some smart window manager sending SIGTERM to local apps, but you can’t rely on this. If you handle nothing else, handle this “X display is gone” scenario, because it should cover all shutdown scenarios. If you lose a remote X connection, I assure you that you won’t get to do the nice X11 window system shutdown procedure (including the session management goodness, sorry). This scenario is what I was referring to about the “second chance”.

What does this all come down to?

- If you are a GUI app, handle window system shutdown (including “display is gone”).

- If you are on Unix, handle signals. You should do this even if you are a GUI app, simply because you can.

- If you are a Windows console app, handle the Windows console events, which incidentally also include window system shutdown events.

- If you are a Windows GUI app, don’t handle console events, that’s just confusing.

Comments (3)

Process Termination Handling

I just threw together some code for handling when a process is requested to terminate. The motive is so I can quit a console application cleanly. With GUI apps, detecting when the user wishes to exit is a trivial task, since any GUI toolkit worth anything will tell you when a window closes (via an ‘X’ button or ALT-F4 or whatever). However, console apps, especially those that run indefinitely, and barring those that have interactive UIs such as curses or prompts, are commonly exited by using Ctrl-C. Fortunately, tracking this kind of event wasn’t too bad to implement. Windows has a handler function specifically for console breaking, and Unix has signals (I trap SIGINT, SIGHUP, and SIGTERM, per the glibc manual).

The JDNS commandline tool has an argument to specify how long it should run, which is just plain silly. And my network interface monitoring test program had a similar option too… Until tonight! Now I can just hit ctrl-C and the app will quit. Clean exits are essential when valgrinding, and so this will make my life much easier.

And now that I’ve thought about it some more, this code may also be useful in GUI apps like Psi, for when a system shutdown occurs. However, I think the Windows code I have is for console only, so that will have to be resolved before it is useful there..

Comments

Q_GLOBAL_STATIC

Creating C++ classes in global space is generally a bad idea, because the order of construction is undefined. On top of that, constructors require execution, thus slowing down your program startup time. Enter Q_GLOBAL_STATIC. It works like this:

Q_GLOBAL_STATIC(Foo, get_foo)

Write this in your global space, and it will define a function called get_foo() that essentially does “new Foo” (if it has not done so already) and returns it. Now, instead of using the object directly, you use the function. This puts you in control of when your global objects are created, and gives you lazy-loading to boot. An atomic integer operation is used for thread-safe construction. In addition, a static template class “wrapper” is defined for your type, so that your object will be properly destructed on exit, just like a normal global type. Granted, this is a slight bit of overhead, but probably less than any object you’d use it with. There’s even Q_GLOBAL_STATIC_WITH_ARGS().

You probably shouldn’t be using globals anyway, but sometimes you need them. Q_GLOBAL_STATIC seems particularly useful with QMutex, the bare-minimum to get you off the ground for creating thread-safe singletons.

Comments (2)

Network Interface Detection

Right now I’m working on detecting when network interfaces come and go, so that, for example, Psi can go online when your network connection becomes active. There is a nice debate as to what constitutes a network connection, but I think for our purposes I’ve decided this will be a non-loopback interface bound to a gateway. This code will be part of IrisNet, which is the new network subsystem for Iris.

Time for platform-specific coding fun again.

Comments (5)

moveToThread ( zero )

You can’t delete a QObject directly unless you do it from the thread it lives in. However, you can use object.moveToThread(0) to disassociate the object from any thread, allowing it to be deleted from anywhere. The drawback is that you can’t use queued signals/slots, but for the kind of object you would do this with it is probably not an issue.

QCA uses this trick to disassociate backend provider QObjects from threads so that they can be cleaned up from any thread. This is mainly for the public key front-end objects which are not QObjects, and so the user has an expectation that they can be copied between threads.

Comments

Ideas for Ambrosia server

Here are some basic ideas for a server project, with working title “Ambrosia”. Like my qnet notes, these are just quick jots that need to be better organized someday.

  • gateway:
      a gateway to xmpp-core clients and/or servers
      accept xmpp-client or xmpp-server connections
      make outgoing xmpp-server connections if necessary
      handle authentication (using local tls/sasl system configuration)
      accept connections from services for administration/routing
      if a user is unknown, ask a service for a see-other-host value
  • service protocol:
      mutual-auth encrypted channel from service to gateway
      a service can:
        handle administration of the gateway
        register any number of domain/type pairs to handle
        reply to a see-other-host request by the gateway
      packets sent from the service are routed out the gateway
      packets received from a remote client or domain is delivered to the
        service
      if there are multiple services registered with the same domain name,
        the gateway picks one (round robin load balance, or whatever) ?
      stanzas should be chunked so that large stanzas don't block the channel
      acking of all stanzas
  • how it will probably work:
      launch the gateway process.  it needs no configuration.
      configure and launch the IM service.  it connects to the gateway, and
        registers its domain for client/server.
      a client connects to the gateway, the gateway authenticates it.
      the client sends a message addressed to someone at another domain.
      the gateway routes it to the IM service.  the IM service processes
        it, and routes it back to the gateway for outbound deliver.
      the gateway connects to the other domain and delivers it.
  • why this design is significant:
      the gateway system is scalable, in that you can run as many as you
        want.  the single IM service can connect to multiple gateways.
      the gateway handles all authentication, meaning that the services
        have less to think about.
      any number of services can share a gateway
      any number of domains can share a gateway (virtual domains)
      gateway shouldn't need to be configured, making administration easier
      the same gateway software can be used from simple internal servers
        all the way to massive public servers
  • what this design doesn't cover:
      xmpp-im.  this is purely about the gateway.  handling IM is another
        topic entirely, and scalability of that is an even harder problem.
        all this proposal does is isolate the edge work into a reusable
        component.

Comments

Ideas on a network protocol framework (qnet?)

Cutestuff/network has some good stuff, but even so, I find myself writing a lot of the same code over and over. Qt offers a nice cross-platform network API, and cutestuff/network draws from this, but it doesn’t really take it to the next level. After looking at Python Twisted, it seems to me that maybe a similar application protocol framework would be very useful. This should be fairly easy to implement in Qt, since most of what Twisted does is already present (such as async, events, and crypto [see qca]). We just need a few base classes to do some housekeeping. Some ideas below:

(Apologies for the incomprehensibility of the items. I’ve mainly transcribed what I had in my personal to-do list, and I plan to tidy up the text as time goes on.)

  • Make ServSock work with BSockets instead of OS socket ids. If the underlying socket closes before it is tended to, the object should still remain in the queue so that the app can take action if it wants to. Don’t activate the BSocket until there is a chance to return to the event loop, so that the user can connect signals. SocksServer is a good example of what should be done, but it’s not perfect.
  • Give BSocket and ServSock better names.
  • Get rid of ByteStream::error(int). It should have no argument, and the error should be read via a member function. This is better than that stupid enum ErrCustom crap. Also, there should be a specific connectionFailed() signal instead of piggybacking this on error(). The reason is that when trying to share these functions among different kinds of bytestreams, it gets annoying.
  • BSocket should not emit readyRead() continuously even if the underlying QSocket does. I’ve never tested this, but the QSocket docs seem to say that this “might” happen. Joy.
  • Signals should never be emitted as the result of calling a function without going back to the event loop. Examples: connectToHost, setSocket. This is not a universal rule though, as I’m sure fiddling with certain widgets do cause immediate signals. What is the criteria? I hate vagueness. *sigh*
  • Should objects emit many signals before returning to the event loop? Examples: connected() followed by readyRead(), or a loop of stanzaWritten() signals.
  • There should be universal byte queue tracking code, possibly in BSocket, ByteStream, or someplace else, so that parent classes don’t have to do the same exact byte tracking code all day.
  • What about having a “blocking” or background close() operation, so that you can close and forget about a connection even if there is stuff left to write? I see three possibilities: nothing (current behavior), background (send remaining data even if the BSocket front-end is deleted), background with blocking (same as background, but if the application tries to exit, block the shutdown until the data is written).
  • Streams should have a flag (”bool isIncoming() const” ?) so that it is easy to determine if a connection is inbound or outbound.
  • Maybe there should be a generic client/server base to be used for anything that does the “Client *Server::takeClient()” pattern (see SocksServer, S5B, etc). This abstraction should not necessarily be limited to just tcp sockets, but anything involving a client/server relationship (like jabber file transfer).
  • Functions for pulling items out of an array, so that I don’t have to use memcpy or htons, etc.
  • Generic interface to security and authentication. Protocols that use TLS, SASL, or other form of auth, could have the same members and signals (for example, dealing with certificates or prompting for authentication), such that controller or dialog code could easily be reused with other protocols.
  • And of course, using security should be easy from the protocol-developer perspective as well. TLS and SASL security layers should be easy to apply to any existing stream (work on this has taken place in qca2 and also iris/xmpp-core/securestream, but there’s room for improvement).
  • Getting all this solved would make me very happy. I like being happy.

Comments

Signal-Safety

Table of Contents


The problem

At some time or another, you might have come across a Qt-based class that cannot be deleted as the result of a signal. Indeed, many classes that are part of Qt have this problem. This is most common with error-handling cases. Consider the following example:

void MyClass::slot_fooError(int err)
{
        delete foo;
        foo = 0;

        QMessageBox::information(0, tr("Error"), tr("Foo error %1").arg(err));
}

Looks sensible enough, yes? However, let us look at Foo’s code:

void Foo::doStuff()
{
        ++x;
        if(x == 3) {
                emit error(ErrReachedThree);
                x = 0; // reset
        }
}

If you don’t see the problem, then you haven’t been programming with Qt very long. ;-) Here’s what happens: doStuff() invokes slot_fooError(), which deletes foo. This causes the members of foo to be freed. Upon returning from slot_fooError(), doStuff() will now try to set ‘x’ (a member of foo) to zero. Of course, this is invalid memory now, and the rest is history (as well as your program instance).

Trolltech will tell you that you can’t assume a class is deletable as the result of a signal, and you should use deleteLater() to work around this. Fine, we will do this:

void MyClass::slot_fooError(int err)
{
        foo->deleteLater();
        foo = 0;

        QMessageBox::information(0, tr("Error"), tr("Foo error %1").arg(err));
}

Of course, the problem remains. Do you see it? QMessageBox::information() invokes a sub-eventloop, which allows full event processing to take place from this one point in the call stack. This way the messagebox can be dragged around, clicked, etc, and we still have not left slot_fooError(). This is nice, except that now the deleteLater() will go into effect, and so foo will be deleted as soon as this sub-eventloop begins. In other words, because of sub-eventloops, deleteLater() doesn’t buy us anything. You can cause similar trouble with qApp->processEvents().

void MyClass::slot_fooError(int err)
{
        foo->deleteLater();
        foo = 0;

        myWidget->show();
        qApp->processEvents(); // make the show() happen NOW
}

Obviously this is not usable. Will reordering it help us?

void MyClass::slot_fooError(int err)
{
        QMessageBox::information(0, tr("Error"), tr("Foo error %1").arg(err));

        foo->deleteLater();
        foo = 0;
}

This, in theory, should work. You could probably debate all day about whether or not the state of MyClass should be reset before or after the QMessageBox. I’d argue that it should happen before, just for cleanliness sake, but I digress. Now, is this reordering of code the master solution? For the purpose of QMessageBox, it’s probably fine.

However, consider a situation where your class would rather emit a signal:

void MyClass::slot_fooError(int err)
{
        emit fooFailed();

        foo->deleteLater();
        foo = 0;
}

This simply passes the buck upwards. If the ‘owner’ of the MyClass instance decides to delete that instance, then it will have to take care that it happens after the fooFailed() signal completes. Otherwise, if MyClass is deleted before that, then the program will crash when foo->deleteLater() is called, as foo would be invalid at that point.


The solution

Rather than pile deleteLater()’s on top of each other, amassing a bunch of QTimers for the purposes of hacky workarounds, I propose that classes always be deletable as the result of a signal. I say that the problem is actually in Foo, so let’s fix it:

void Foo::doStuff()
{
        ++x;
        if(x == 3) {
                x = 0; // reset before emitting
                emit error(ErrReachedThree);
        }
}

Easy enough, just reorder the code so the resetting happens before the signal. Now we can do this:

void MyClass::slot_fooError(int err)
{
        delete foo;
        foo = 0;

        QMessageBox::information(0, tr("Error"), tr("Foo error %1").arg(err));
}

and this:

void MyClass::slot_fooError(int err)
{
        delete foo;
        foo = 0;

        myWidget->show();
        qApp->processEvents(); // make the show() happen NOW
}

without any worry. Foo will be completely gone at the ‘delete foo’ line, with no worries of it creeping up later (aside from the fact that doStuff() is harmlessly on the call stack).

The data stack is also a safe place to store data. Consider this variation:

void SomeClass::doIt()
{
        NumList tmp = nums; // nums is a member of SomeClass
        emit listFull(tmp);
        tmp.removeEvens();
        emit listOddsOnly(tmp);
}

The ‘owner’ of SomeClass can delete the object as the result of either of these signals, because no members are accessed after they emit. The above example may not appear very useful, but keep it in mind, as this stack trick will come into play in the later sections.


Accessing member data after emitting

Now, what if you need to access members after emitting a signal? What if you can’t put your signal emit at the end? Consider a socket handling class:

void MySocket::dns_result()
{
        d->address = d->dns->resultAddress();
        emit hostFound();
        d->socket->connect(d->address);
}

Obviously this will crash if MySocket is deleted as a result of emitting hostFound(). How can we fix it? Well, one variation is this:

void MySocket::dns_result()
{
        d->address = d->dns->resultAddress();
        d->socket->connect(d->address);
        emit hostFound();
}

This looks suboptimal, as we should probably emit hostFound() before we start connecting, but the above code could be appropriate, depending on what you’re trying to do. It certainly solves the problem, and is clean.

However, there are some dirty ways. Let’s look at them. First, via QTimer.

void MySocket::dns_result()
{
        d->address = d->dns->resultAddress();
        QTimer::singleShot(0, this, SLOT(slot_tryConnect()));
        emit hostFound();
}

void MySocket::slot_tryConnect()
{
        d->socket->connect(d->address);
}

This will cause the signal to be emitted prior to performing the followup steps, which might be a more appropriate sequencing for your purposes.

And the other way, via QGuardedPtr:

void MySocket::dns_result()
{
        d->address = d->dns->resultAddress();
        QGuardedPtr self = this;
        emit hostFound();
        if(!self)
                return;
        d->socket->connect(d->address);
}

This method ensures that we don’t proceed to any code after hostFound() if our class dies during the emit.

Now, both of these are decent solutions and seem to achieve the same thing. However, they do have slightly different behavior. The former uses QTimer, meaning that if the parent invokes a sub-eventloop (such as QMessageBox), then the follow-up code will occur immediately. With the latter, the follow-up code won’t happen until the signal is completed, regardless of any sub-eventloops. This means that the eventloops would have to complete (QMessageBoxes would have to be closed), before the code would proceed. This can cause great problems with any code that should be processed even when an exec()’d dialog is open, such as network or process-handling.


QTimer vs QGuardedPtr

It seems we have two choices then. Use QTimer so our follow-up code can run alongside an exec()’d dialog, or use QGuardedPtr and avoid sub-eventloops completely. I vote for the latter, and I’ll explain it in one word: sanity.

Using QTimer for signal-safety is tricky. You have to split your function into two parts (making one of them a slot) everytime you add a signal. This can be especially annoying if you are adding a signal to some existing code at a later time.

QGuardedPtr, on the other hand, is easy enough to add to any code. You don’t have to consider changing your class code-flow into parts just to add a new signal. In fact, the process is so straightforward that you can make a macro:

#define SAFESIGNAL(a) { QGuardedPtr self = this; a; if(!self) return; }

void MySocket::dns_result()
{
        d->address = d->dns->resultAddress();
        SAFESIGNAL(hostFound());
        d->socket->connect(d->address);
}

The drawback of not using QTimer is that your code will not advance if the developer uses QDialog::exec(). My solution? The developer should not use QDialog::exec(). :) Besides, this advancement is just a side-effect of using QTimer. If you were coding without any signal-safety in mind, then you’d just emit your signals wherever you darn please, and you’d still get stuck in an exec().

The conclusion? Code well, and avoid QDialog::exec(). :-)


Working with ‘legacy’ classes

Now, you might ask: “What if I’m writing code that depends on a class that does not follow these guidelines?” The answer is that it is possible to work around these cases with some nasty hacks. Consider the following example using QSocket, which we assume does not follow our guidelines:

void MyProtocol::MyProtocol()
{
        [...]
        qsock = new QSocket;
        connect(qsock, SIGNAL(bytesWritten(int)), SLOT(qsock_bytesWritten(int)));
        [...]
}

void MyProtocol::~MyProtocol()
{
        [ ... ]
        delete qsock;
        [ ... ]
}

void MyProtocol::qsock_bytesWritten(int x)
{
        bool packetWritten = [ ... packet-size processing involving 'x' ... ];
        if(packetWritten)
                emit packetSent();
}

This looks harmless at first. After all, you’re emitting the signal at the end of that function, with no further access to members, right? Yes, this is true, but you don’t know what is going on within QSocket. It might try to access its own members after this call, which would be unsafe if we are deleted during our signal.

Fine, we switch to deleteLater(),

void MyProtocol::~MyProtocol()
{
        [ ... ]
        qsock->deleteLater();
        [ ... ]
}

Of course, this doesn’t solve the QMessageBox problem. To completely fix this for good, we must use the stack:

void MyProtocol::MyProtocol()
{
        [...]
        inQSockSignal = false;
        qsock = new QSocket;
        connect(qsock, SIGNAL(bytesWritten(int)), SLOT(qsock_bytesWritten(int)));
        [...]
}

void MyProtocol::~MyProtocol()
{
        [ ... ]
        // don't delete the qsocket if we're in one of its signals
        if(!inQSockSignal)
                qsock->deleteLater();
        [ ... ]
}

void MyProtocol::qsock_bytesWritten(int x)
{
        bool packetWritten = [ ... packet-size processing involving 'x' ... ];
        if(packetWritten) {
                inQSockSignal = true;
                QSocket *tmp = qsock;
                QGuardedPtr self = this;
                emit packetSent();
                if(!self) {
                        // if we got deleted here, then the qsocket is still alive.  delete it now
                        tmp->deleteLater();
                        return;
                }
                inQSockSignal = false;
        }
}

Tricky, eh? ;-)


cutestuff’s SafeDelete

In order to make the previous concept easier to follow, I’ve created the SafeDelete class, which can be found in my cutestuff/util collection. Just two files are needed (safedelete.h and safedelete.cpp). It works like this:

void MyProtocol::MyProtocol()
{
        [...]
        sd = new SafeDelete; // make one of these
        qsock = new QSocket;
        connect(qsock, SIGNAL(bytesWritten(int)), SLOT(qsock_bytesWritten(int)));
        [...]
}

void MyProtocol::~MyProtocol()
{
        [ ... ]
        sd->deleteLater(d->qsock); // go through SafeDelete instead of direct
        [ ... ]
        delete sd;
}

void MyProtocol::qsock_bytesWritten(int x)
{
        SafeDeleteLock s(sd); // lock the function

        bool packetWritten = [ ... packet-size processing involving 'x' ... ];
        if(packetWritten)
                emit packetSent();
}

Basically, SafeDelete will normally call deleteLater() on the passed object, unless there is an active SafeDeleteLock on it. If there is a SafeDeleteLock, then SafeDelete simply queues up the delete requests. If the SafeDeleteLock is destroyed, then the delete requests are processed. So far so good. Now, if the SafeDelete is destroyed, then the destructor moves the delete queue into the SafeDeleteLock, which is on the stack! This way, MyProtocol can safely be deleted. When the SafeDeleteLock is destructed, the delete requests are processed.

Note: In the above example, SafeDeleteLock is created on the stack, so that it is destroyed automatically when the function ends. This is similar in nature to how QMutexLocker works.

Happy coding!

Comments

Crossplatform Thoughts on Psi/Mac and the Lowest Common Denominator

I’ve been thinking about the issues of crossplatform programming lately now that I want to improve Psi/Mac. The program feels alien to MacOS X for two reasons: 1) widgets do not match those of native applications, both in looks and in behavior, and 2) the organization of the application does not make sense when compared to one that is native.

Native widgets are a hot issue. Qt works by emulating the widget look & behavior of the platform. Under Windows, this is very easy to do, since the widgets are quite basic. Also, the notion of a “native widget” under Windows is less defined. Many applications get away with having their own widgets, and so the Windows look and feel has never been drastically changed. Even Windows XP, which supports theming, does so by grafting a simple look & feel system atop the usual widgets. Qt supports XP themes quite well. Under X11, there is no such thing as a standard widget. I suppose you could say that would be Motif, and Qt does a fine job of emulating it (even though I can’t stand it), but these days people run all sorts of fancy KDE desktops using advanced styles like Keramik. However, KDE is Qt-native, so this isn’t really any trouble for Qt. Instead of Qt trying to figure out how to look like the desktop, the desktop is telling Qt how to look. Try Qt under GNOME, and we lose all sense of native look and feel. The Mac is by far the most troublesome style to emulate, not just because of complexity (it is complex), but also because it is so high-level that Apple can do things like change the TabWidget look and all apps conform to it. Qt/Mac on Panther (OS X 10.3) shows the older TabWidget style, emphasizing this problem.

Why does Qt not use native widgets? Trolltech will tell you it is more powerful this way. Certainly it is, you can do whatever you want with your Qt widgets, like drawing a line across a button or making your own freaky QScrollBar clone that has extra gadgets on it or whatever. Because the Qt style system breaks down high level widgets into separate parts for rendering, you could actually invent a widget that does not exist on Mac, but still has an Aqua look and feel. However, in many cases, this is overkill. Yes, custom widgets are handy, but do we often need custom widgets that look derived from native widgets? I say no. Consider the ‘BusyWidget’ from Psi, which just shows the logo as a bunch of spinning panels. This is a custom widget. It does not exist on any platform, it is something I wrote. However, it is not something impossible to write without Qt. I could easily make it with Objective-C using native Cocoa and it would look exactly the same. We do not want to rule out custom widgets. What we do want to rule out are customized variations of native widgets. We can trade this flexibility for true native look and feel, which is essentially what the wxWindows toolkit does. So why not just use wxWindows? Well, having native widget wrapping is only part of this battle. Qt is a more ideal framework in my opinion. It is almost like a platform of its own, and even defines extensions to the C++ language like signals, slots, objects, rtti, etc. It turns C++ into a wannabe Objective-C / Java. Whether this is a good idea or not is left up to the reader, but I consider it very powerful to be able to do these extras while retaining the use of C++.

Now, at some point, someone is bound to bring up the issue of the Lowest Common Denominator. This is generally what crossplatform applications have problems with, the rule being that if you can’t do something everywhere, then you just don’t do it. Personally I think this is a stupid limitation. Crossplatform applications should take advantage of platform-specific functionality whenever possible, otherwise the value of the underlying platform is lost. With Qt, we are not stuck in a box. We are natively compiled C++, not confined in a VM like Java, which means we can do whatever we please on each platform. The goal with crossplatform code then, in my opinion, is to have highly portable code, but not necessarily “write once run anywhere” code. We want to share as much code as we can to avoid redundancy, but beyond that we want to write code that is specific to each platform in question.

Consider the text-entry widget on Mac. It has built in on-the-fly spell checking. This is a great move by Apple, and it reminds me of KDE. Instead of simple widgets like on Windows where apps have to supply most of the functionality, apps under Mac can use highly advanced widgets that have a lot of functionality on their own already, and can be upgraded through the OS easily. This allows for more power, consistency, and rapid development of apps. How can we use this widget with Qt? Well, we can’t. This is because QTextEdit, like all Qt widgets, is not a native widget. Could Qt be altered to use the native Cocoa widget? Presumably. At what loss? The ability to do things we would never want, like drawing all over it with QPainter. I think it is clear that in the majority of cases, we would rather use the native widget. Then, of course, we also need a way to access the properties of the native widget so that we can do further platform-specific functions to it. On the Mac, we would like to enable the spell check mode (if we have to do that), but this function would not exist on other platforms. How do you access Cocoa widgets from C++ ? Good question, perhaps it is not easily possible. wxWindows probably does not support this. Qt does not support native widgets, so this is not possible either. So what is the answer, write the whole GUI in Cocoa?

No! Well, sort of. In the end it will all be Cocoa, whether we are creating widgets via Objective-C widgets or using them through a wrapper. However, it is simply not efficient to have to recode everything in Objective-C. Just because you want to port to another platform should not mean rewriting all of your code. The point I made earlier was that emphasis should be placed on code reusability. Some may say that abstracting the GUI completely out of an app is the logical thing to do. In some cases, I agree. If you are porting to a PDA, sure. If you are porting to Mac, you’d probably want to do this to some extent. Licq, the unix ICQ client, has a completely abstracted GUI system, and there are both Qt and Gtk versions. However, there is a ton of redundancy here.

I can appreciate the need to rework a GUI for a particular platform. When you consider the Mac, the GUI design guidelines are drastically different from Windows. Configuration dialogs generally take effect on-the-fly. There are no “OK” or “Apply” buttons. Apps use a global menubar at the top of the screen. Some apps use that weird “brushed metal” look. It is sensible then, that one might want to recreate their GUI completely for Mac. However, consider all of the various dialogs that we have. Do they all need to be recreated on Mac? And if so, do the entire dialogs need adjustment or only partly? For instance, some dialogs might be perfectly fine in their current state, aside from the ordering and alignment of the buttons at the base of the dialog. It would make more sense to reuse the code for this dialog, but to change the button positioning to match the Mac. Why code the use of a scrollbar in Objective-C when you’ve already done it in C++? It’s not like a scrollbar is only available on certain platforms. The solution, then, is to have generic bindings to all native widgets, and also platform-specific bindings so that extra functionality can be used if necessary. Qt is in a great position to do this, and even have fallback to emulated widgets if we really really wanted to make a frankenstein widget with native look. However, for some dialogs it might be more optimal to recode them completely, and this could be done with some Objective-C code wrapped in a C/C++ library. Psi has a ton of backend code, from the XMPP library to all of the internal contact management and accounting. It makes sense to be able to reuse as much as possible when making a MacOS X Jabber client, instead of starting from scratch. However, it should be used in such a way that it is not at the loss of native look, behavior, and functionality.

This, I think, sums up all of my thoughts about cross-platform programming. Rule of the day: code should be as portable and reusable as possible.

Comments (1)

Next entries »

Bad Behavior has blocked 809 access attempts in the last 7 days.