Come On! Why must everything be derived?

by Ben

I am trying to get some experience building graphical UIs using C++ after hours at work (and also to build a tool that would be useful there as well). I started looking into the various frameworks, and the truth is that there isn’t any magical silver bullet for C++. There is no Swing++, so to speak.

Anyway, I really like the idea of using the Glade tool to build the UI graphically and have it specified in an XML file which would then be read at runtime to construct the actual UI. I did a few toy examples in C to see how it’s done, and I was thoroughly impressed, especially with the signal autoconnect facility in libglade. Connecting callback functions is one of the many tedious things that really make it a chore to build a UI.

After doing that for a while, I thought I’d try the libglademm (and gtkmm/glibmm) libraries so I could benefit from the consistent API and proper object orientation and hierarchy for the various widgets. All very nice. If you want to create a custom widget, you don’t have to bend over backwards to do it; you just inherit from a stock widget and change as necessary, using the base class’ implementation where possible. Here’s what Debian’s Roger Leigh had to say about it:

The C++ code using Gtkmm is slightly more complex than the code using
Glade. However, the benefits of type and signal safety, encapsulation
of complexity and the ability to re-use code through the derivation of new
widgets make Gtkmm and libglademm an even better choice. Although it
is possible to write perfectly good code in C, Gtkmm gives the programmer
security through compiler type checking that plain GTK+ cannot offer. In
addition, improved code organisation is possible, because inheritance allows
encapsulation.

Then, after libglademm kindly constructs your widget objects for you, you just connect your signals. How hard can it be, right? After all, in libglade, all you have to do is this:

#include
#include

void some_signal_handler_func(GtkWidget *widget, gpointer user_data) {
/* do something useful here */
}

int main(int argc, char *argv[]) {
GladeXML *xml;

gtk_init(&argc, &argv);
glade_init();

/* load the interface */
xml = glade_xml_new("filename-for-interface", NULL);
/* connect the signals in the interface */
glade_xml_signal_autoconnect(xml);
/* start the event loop */
gtk_main();
return 0;
}

This is just a stock example from the libglade reference documentation, of course, but it illustrates nicely how much ugly crapwork you don’t have to do because glade does it for you. That ‘glade_xml_signal_autoconnect’ function is pure gold, buddy, let me tell you.

Anyway, I looked for something similar in libglademm, but there isn’t. There isn’t a Gnome::Glade::Xml::signal_autoconnect because, according to Leigh,

You need to do the connection “by hand” in your constructor, for
example. It’s not possible to connect to C++ object methods
automatically like you can with C functions.

It’s certainly not impossible. It’s especially challenging if you’re going for the most abstract form of portability, but who needs to do that? At any rate, that has no bearing on the fact that gtkmm expects you to derive every widget you use, if you want to use signals at all (and which widgets don’t fall into that category? GtkLabel maybe? Even then you sometimes want something to happen when you click a label). I am completely dismayed by this. Every object has a default signal handler…but it’s protected. What for? I just want to hook up whatever the default signal handlers are. If the defaults don’t do anything, then why aren’t they pure virtual? If they do something, then why can’t they just be exposed so I can avoid creating a new class for every widget I want to hook signals to?

The interface I’m working on has a top-level window with a number of nested widgets down to a pretty deep level (7 or 8 or so). Each level contains multiple widgets. If I want to hook signals to every one of them, everything I’ve read about it to date suggests that I’m going to have to derive tens of new classes, just so I can call their default signal handlers. How poor is that? The code and image size bloat are astonishing, and with all the big virtual tables that have to be constructed, the performance hit is surely unnecessarily monstrous.

*sigh*

Maybe someone who knows gtkmm on a much deeper level than me will comment here and set me straight. I’ll keep posting about this though, because despite its shortcomings, I don’t know that there is anything better for C++ out there right now. Nothing as portable as GTK+, anyway.