Come On! Why must everything be derived?

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.

5 Comments

  • May 7, 2007 - 12:24 am | Permalink

    > gtkmm expects you to derive every widget you use, if you want to use signals at all

    This isn’t true, and it would be silly:
    http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/ch03s04.html

  • May 7, 2007 - 7:45 am | Permalink

    Ok, so I exaggerated. I know you can hook up any public function you want.

    The problem is that I don’t want to write signal handlers for every widget, and AFAIK, you can’t use the default signal handlers unless you derive your own widgets because the defaults were made protected.

  • May 8, 2007 - 6:57 pm | Permalink

    Err, yes, you can’t override virtual methods without deriving a class. That’s C++. I can’t imagine how it could be different.

  • May 14, 2007 - 12:04 pm | Permalink

    What I was looking to do was merely connect the defaults from outside the class. Something like [code lang="cpp"]
    Gtk::Window win.destroy().connect(sigc::mem_fun(&win, Gtk::Window::on_destroy(), false))
    [/code]

    I have since discovered that the defaults are connected by default, which addresses my primary headache. It’s still a PITA to derive new classes to write new signal handlers (especially if you’re using glade, as I am). Like you say, I suppose there’s not really much they could have done to make it much better. Perhaps my beef should really be with the Glade tool.

  • May 15, 2007 - 7:31 am | Permalink

    I don’t understand how something would be the default if it was not used by default. This is not different than in GTK+.

    Also, you don’t need to handle a destroy function to cause an object to be destroyed. gtkmm uses normal C++ memory management. Maybe this is what’s confusing you.

    I strongly suggest that you look at the gtkmm book and its examples.

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>