Al is DDJ's senior contributing editor. He can be contacted at [email protected].
There really isn't a platform named YAPP (that I know of). I made it up because it seems that I spend a lot of time evaluating C++ application framework class libraries for Linux GUI programming in search of the perfect one, and all I seem to do is yap about it. More about that later.
I'm leading this column off with a book report, something I rarely do, but this time it's important. I really want you to read Effective STL by Scott Meyers, Addison-Wesley, 2001; ISBN 0-201-74962-9. This is the third in a series of books by Scott that follow a common theme. The first two are Effective C++ and More Effective C++, both reviewed here in the past. Scott presents in his books a number of essays on how to use C++ and, in this new book, the STL. He calls his essays "Items." Each item addresses a specific issue with respect to some aspect of the book's purpose. Effective C++ has 50 items, More Effective C++ has 35, and Effective STL has 50.
Scott assumes you already have a working knowledge of C++ and the STL. Each item addresses a specific issue where you might come up with several ways to do something, or you might make specific assumptions about the consequence of some decision, such as which container to use for a particular application. Then Scott imparts the wisdom of his own experience and that of many of his colleagues with whom he collaborated to isolate and develop the items. Here's how you might do something. Here are several ways you might do it. Here's what you really ought to do. Here's why. Most items contain references to other items. It's difficult to get a handle on such a complex subject in such a concise manner, but Scott succeeded. The writing is clear and to the point. Veteran STL programmers will instantly recognize some of the issues and will be surprised by some others. There are things that just never occurred to you. Others that you thought you understood but really didn't.
I have not found a single item with which I disagree. I found several that changed how I use the STL and others that led me to use the STL in ways that had not occurred to me. If you program with STL, you need this book. If you don't, read the book anyway. It just might make you want to.
A Confusion of Jargons
To quote Strother Martin: "What we have here is failure to communicate." GUI programming jargon is hardly universal with no accepted standard, and it is frequently confusing. Let's try to clear up some of it.
Programmers for various GUI platforms use different terminology to describe the mechanism by which code executes as a result of an external stimulus. They also use other words to mean the things that a GUI displays.
An "event" is, in most parlances, the external thing that causes the program to respond. Mouse clicks and movements and keyboard presses are the most common events. Timer ticks and timeouts are among the others. This vague concept is called "event-driven" programming. The operating system or GUI platform translates events into what Windows programmers call "messages." Messages can beget other messages. A mouse click can generate a menu command, for example. Processing these messages is called "message-based" programming. To write a Windows program, you employ event-driven, message-based programming.
The use of the term "message" harks back to when Windows programmers coded in C. Windows has hundreds of defined messages that the code for a window can intercept and process. The term, however, collides with the same word that means "invocation of a method" in object-oriented programming (pure and otherwise). To further stir the broth, "method," in most OO languages, becomes "member function" in C++.
Every element displayed by Windows is called, not surprisingly, a "window." We've become accustomed to that term, but it's an odd analogy that mixes metaphors and rarely resembles the thing for which it is named. A button is a window; a list is a window; a menu is a window; a dialog is a window. Windows can have windows in a parent-child relationship. Nothing about all this resembles a real window, which does not disappear when you close it, which you can see through when you open it, which passively displays things beyond it, and which has no children or parents.
The code that executes for a window in response to a specific message has no name in traditional Windows C programming. All messages are processed as cases of a switch in a single "window processing" function. With the Windows API now wrapped in C++ classes, each message gets its own member function. There is no formal name for that kind of function.
To summarize: An event generates a message, which is processed by a member function in the name of a window.
A message is a "signal" in the KDE and Gnome development environments. But wait. A signal is something else in traditional UNIX programming. They had to know there would be confusion. I guess no other synonym for "message" presented itself; and they couldn't stoop to using Windows conventions.
Under *nix GUI programming, the code that executes in response to a signal is called a "slot," and the things that the GUIs display are called "widgets," a name inherited from OSF/Motif. "Widget" is a better name than window, because there is no real-world thing called a widget, which is a generic term for anything mechanical. (Perhaps GNOME should have used "gadget" instead, which means the same thing, since they both start with G.)
To summarize: An event generates a signal, which is processed by a slot in the name of a widget.
Now that we all understand the same words, we can communicate.
What's to Like?
Last month, I listed some of the things not to like about the QT class library, the base application framework for writing programs to run under the KDE desktop on Linux and other UNIX-like operating systems. QT is for other platforms, too, but my focus is on Linux these days. I'll summarize last month's criticisms here.
- No namespaces.
- Language extensions to implement slots.
- The metaobject compiler.
- An ill-organized and unnecessary RTTI mechanism.
- Custom rather than standard containers.
- Questionable C++ coding conventions.
See last month's column for details. I can put up with most of these shortcomings, although my teeth are wearing down from so much gritting. But this month, I add a complaint that makes programming with QT and the derived KDE class libraries unpalatable to this C++ programmer.
Take a close look at Example 1. What's wrong with this code? According to the conventions for QT programming, nothing is wrong, the program is correct. But according to me, something is seriously wrong with this program.
The program instantiates a QApplication object on the stack and a QLabel object on the heap. It sets the QLabel object as the main widget of the QApplication object, tells the QLabel object to show itself, and executes the QApplication object. The program exits when the QApplication::exec function returns. It's a typical GUI application.
But what's missing? Carefully notice that the program does not delete the QLabel object that it instantiated on the heap with the new operator. This means that the QApplication object must do the delete in its destructor to avoid memory leaks. This means that you must instantiate all widget objects on the heap.
But what if you don't? If you instantiate a widget on the stack or in external or static memory, the program fails when the parent object deletes the widget. No portable way exists for a program to determine whether something's address is in heap memory space, so the widget's parent is going to delete it no matter what its address is. You're in a heap of trouble.
If you instantiate the widget on the heap as you must but then delete it yourself, as you are conditioned by your proper upbringing to do, the program fails when the parent deletes the widget a second time. Another heap of trouble.
This convention is downright bad. It encourages bad coding practices by getting programmers accustomed to using new without delete as a routine practice. I can't accept, endorse, or use a class library that behaves this badly. That's a shame, because the KDE development environment shows promise for becoming a widely used platform. The bar is being lowered.
In my programming philosophy, a program balances the acquisition and disposal of system resources in such a way that the two parts are in view of the programmer wherever possible. If a function uses the new operator, that function should use the delete operator on the same pointer sometime before the function returns to its caller; see Example 2. That's simply elementary C++ style. If you program that way, you'll make fewer memory-management mistakes. If an object's pointer is a data member, every delete is accompanied by an assignment to the pointer of a zero value or by another new assignment so that an extra delete somewhere has no ill effects. Consider Example 3. If addbar is never called, or if it's called many times, heap management stays well balanced and safe.
You don't have to write code this way, and sometimes you can't. But an architecture that requires you to make heap allocations that you do not delete is fundamentally unsound. Why should I even have to preach about this? Because some very influential programming environments are encouraging programmers to do the wrong thing, that's why, and something needs to be said about it.
I expect mail on this matter. I expect some programmers to get squarely in my face about it, arguing that the technique works and that some large number of programs have been successfully developed and deployed with this approach, somehow validating such deplorable practices despite their obvious shortcomings. Examples of other libraries that do similar things will be offered to prove me wrong. (The same thing happened a few years ago when I said not to code delete this;.) It shall be argued that these practices can't possibly be shortcomings at all since so many programmers accept and embrace them.
A lot of people are still driving on Firestone tires, too.
Gnome's Not KDE
I said last month that programmers will probably prefer the look-and-feel of KDE for their applications over that of GNOME, which mainly was me saying that's what I want. But my subsequent lament about KDE class libraries sent me looking at the GNOME development tools, which gave me a nice surprise. When you run a GNOME application on a KDE desktop, the GNOME application looks like a KDE application. Conversely, KDE applications look like GNOME when run on the GNOME desktop. Apparently, the two platforms use common run-time library calling conventions to display widgets. The desktop environment determines the look and feel more than the application's own code does.
If you are looking for one single, outstanding validation of the open-source development model, this is it. With no profits to lose and no trade secrets to compromise, projects are encouraged to cross-pollinate their technologies, making all products better and advancing the state of technology across the board. You won't find much evidence of this cooperation among the mailing lists associated with both desktops. Mostly what you find is fierce competition and chauvinism. But separate yourself from all that, and the result is positive.
GNOME is based on component object technology. It employs CORBA-like technology, which was originally called ORBit and was almost replaced by something named Bonobo. I don't know much about either architecture, but apparently they are core to the GNOME philosophy. Much controversy surrounds these libraries and what should be included in the next release of GNOME. Disagreements on what should be released caused one of the primary GNOME maintainers to step down in protest. The surrounding flak made us casual onlookers believe that the project was in jeopardy, particularly considering the overwhelming success of GNOME's major competitor, KDE. I wouldn't worry, though. These open-source projects are informally staffed by volunteers. Anyone who has ever been active in a PTA, the JayCees, or on a condominium board of officers knows that volunteers get a lot more passionate about their assignments than do paid employees. Maybe it's because losing the job has no negative financial consequences and they can blow off steam and even stomp out whenever they want. Besides, there are always a bunch of observers and underlings who secretly want the position and all the (questionable) esteem and respect that they think goes with it. There's always someone eager to take over in a volunteer organization.
I got another surprise when I looked at GNOME programming tools. My first exposure was to the C API, which does not interest me. Several readers turned me on to Gtk-- (http://gtkmm.sourceforge.net/), which is a C++ wrapper around the GTK API, upon which GNOME is built, and, guess what? Most of my objections to the KDE library are gone. Gtk-- uses namespaces. It implements slots without using preprocessor macro language extensions or a metaobject compiler. It has no funny custom RTTI feature that you have to use. It does not require you to instantiate widget objects with the new operator that it will sometime later delete for you.
Gtk-- adds to the jargon confusion by calling its slots "signal handlers." Gtk-- objects can emit signals by using special objects called "signalers," which you connect to signal handler "callback functions."
Gtk-- comes with an HTML tutorial still a work in progress which I recommend if only for the occasional touches of humor that had me laughing out loud in several places.
I installed Gtk-- from source code by using the new Gcc 3.0 compiler release. It builds without errors, and the example programs in the tutorial work without any problems. Well, almost. When I use the command lines in the text of the tutorial examples to compile, they do not work properly. But when I use the Makefiles that accompany the example source code, everything runs without a hitch.
Gnome-- (also available at http://gtkmm.sourceforge.net/) is an extension of Gtk-- intended, as nearly as I can tell, to support more of GNOME's object technology and to add such things as MDI support to the application classes. I can't really say for sure because the project is fairly new and without much in the way of documentation yet. It would not compile with the gcc 3.0 compiler, and when I retreated to Red Hat's 2.96, it could not find certain required header files to continue. My guess is that the maintainers have something installed that I do not, although I did install the prerequisite libraries that were identified. I'll wait for this one to become more ready for wide usage before diving in.
The RedHat Inti Class Library
Another possibility for a GNOME class library is Inti from RedHat (http://sources.redhat.com/inti/). At first glance, and that's all I've given it so far, Inti seems to be even more of what I am looking for. It employs namespaces, uses STL containers, and avoids the kludge that QT uses to implement events, allowing you to choose between connecting signals and slots as Gtk-- does and using virtual functions. But there's a major problem. According to its preliminary documentation, Inti uses that same awful convention that QT uses, requiring you to implement some objects on the heap and never delete them. They say it's to support reference counted objects, but I don't get it. Unless I've missed something, Gtk-- did not have to do that.
If the Inti developers are reading this, listen up. That's a bad thing to do. Don't do it. You are still in the preliminary stages of your project. There's still time to do the right thing and correct that error. If it's the only way you can support reference counting, then do without reference counting. Its benefits aren't worth the price.
Because as long as Inti has that characteristic, folks, that dog won't hunt and I won't be using it.