In my last post, I kinda whined about the awkwardness of the Gtkmm API. At least, it seemed that way at the time. I toyed with libglade and Gtk+ for a while first and everything seemed very straightforward and easy.
Gtkmm and libglademm are different. They seemed awkward at first, but after further investigation, I like their designs much better, and the advantages presented by C++ seem obvious. One of my primary objections was that it didn’t seem that I could hook up the default signal handlers because they were made protected members of the widget classes. At the time, it seemed to me that the only way that the default signal handlers could be used at all was by deriving classes and overriding the handlers (which are virtual, by the way). I have since discovered that the default handlers are connected by default. Also, the “slotting” mechanism used for signals — whereby signal handlers can be chained together and all executed on a single event — is pretty nifty.
Speaking strictly of Gtkmm, this doesn’t seem so bad, but I was using glade to put my UI skeleton together, and I very much liked the idea of dynamically building your UI from an XML file at program startup, rather than coding all the tedium that goes along with UI development. I also prefer C++ to C whenever possible (actually, I prefer C++ to any other language whenever remotely appropriate), which is how I got here in the first place. Anyway, glade-2 — in my opinion — isn’t very good at dealing with “custom widgets”, which includes widgets that are derived from and substantially the same as the stock widgets. One caveat I should include here is that I haven’t really gone through this whole process with glade-3 yet. Maybe it’s better. I don’t know.
Offhand, I’d say one thing that could be better in glade-2 is if, once you placed some stock widget, you could edit the classname and let glade-2 assume that the user-entered classname is the name of a class that derives from the stock widget you originally placed. Then, maybe it could support all the properties of the original widget, but actually use your derived widget at runtime when the UI is built. This would be pretty much what I want, because thus far, the only use I have for writing custom widgets is to override the default signal handlers.
Tags: C++, glade, gtk, programming
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.
Ryan is disillusioned with C++. The analogy of C++-as-a-person-whom-I’m-going-to-divorce is kinda lame, but his criticisms are typical. I normally don’t get in blog wars like this, but for some reason, I’m feeling defensive tonight.
What this means is that he thinks C++ behaves inconsistently. Firstly, so does any other language that has more than one compiler. Java may be an exception, but there aren’t many java VMs other than Sun’s – and even then, there are probably differences. The C++ standard is clear about expected behavior in almost all conceivable scenarios, and it spells out where the behavior isn’t well-defined. I think his complaint here ought to be with the “compiler friends” he hangs out with. He could also find a portable compiler that tries to comply with the standards (such as gcc/mingw or the intel compiler, if he’s on x86. VS2005 does a respectable job of implementing the standard, but then, it’s not portable in any sense.
Yes, so C++ doesn’t have usable reflection capabilities. That was an intentional design decision made to prevent the runtime overhead involved – remember, C++ was designed as a tool for creating high-performance software. Languages and VMs that support real reflection are aren’t. Repeat after me: C++ isn’t the tool you should use in a project that requires reflection. Languages that support reflection aren’t the ones you should use in a project that require high performance.
He says he has to use a “null-terminated blah blah blah” – which you should never have to do in C++. And what’s so bloated about std::string? I find it to be quite nice, actually. Non-standard third-party implementations need not apply. std::string works just fine.
std::vector<int> anyone? Also, if std::vector<std::vector<int> > doesn’t blow up your skirt, then typedef it. Sheesh.
Where to start? Firstly, C++ having templates is still more than Java can say. Generics != templates. The syntax is a little unseemly at first, but it’s an acquired taste – like beer.
Firstly, header dependency resolution isn’t a characteristic of C++. The pre-processor does that. Secondly, if your code is so complicated that wrapping all your headers in
#ifndef _BLAH_
#define _BLAH_
// definitions
#endif
doesn’t work for you, then your design is broken. Don’t blame C++.
Design patterns are just the outflow of a few decades worth of industrial experience. People are starting to retire en masse, so they thought it might be a good idea to write some things down. Why is that a bad idea, and what does that have anything to do with C++? No language can obviate the benefits of having formal design patterns. Not even our current rock-star, Ruby.
More generally, what I want say, and what many expert programmers will say until they’re blue in the face and what many other programmers simply won’t listen to is that languages are just tools. They’re all designed for something, and that something isn’t always the same. Actually, it usually isn’t. Use a tool that’s appropriate for the job. C++ happens to be designed for a very broad and general application domain. That’s a large reason for why it’s so widely used. I don’t know that I even know of any other languages that have such a large application domain. Even Java, C#, and Lisp/Scheme aren’t as general-purpose as C++ is. The point is use the tool appropriate for the job. The fact that C++ is a combo compound mitre saw/router/nailgun doesn’t mean it should be used to paint a wall. Likewise, you shouldn’t use a paintbrush to try and make furniture. Ruby does not do everything Perl does with none of the downside. Perl has its strengths, and Ruby has its. Let it be at that.
Just to address Ryan’s specific comments: the reason certain languages are widely used and “safe” is because of their large application domain (and other reasons as well, many of which ultimately spring from this primary reason). Businesses don’t want to invest in many niche tools when they can support good general tools for the same or less.
Also, I don’t necessarily disagree with his insistence that we shouldn’t be monolingual (picking up new programming languages should be something any good programmer can do quickly), but I also think that it’s good to be able to fall back on something as an expert.
Powered by ScribeFire.