 | Level: Introductory Daniel Robbins (drobbins@gentoo.org), President/CEO, Gentoo Technologies, Inc.
01 Jun 2002 In this second installment of the newly relaunched "GNOMEnclature" column, relative GNOME newbie and "Common threads" columnist Daniel Robbins takes a look at the new Glib object system from a new GNOME developer's perspective. By the end of this article, you'll know enough about GNOME to decide if you want to try it for yourself (even if you are a stalwart C fan).
OOP programming in C?
In this article, we're going to take a look at the Glib object system,
also called "GObject", which up until recently used to be part of GTK+.
But before taking a look at the new object system in Glib 2.0, we need to
address a more fundamental issue -- what exactly is an "object system" and
why does it exist? After all, isn't C a non-object oriented language? It
is possible to write object-oriented programs in C, or must one use C++
instead? The answer is that it is possible to write object-oriented
programs in C. However, because the concept of object isn't a part
of the C language specification, an external library is required to
provide this support. In this article, we use the term "object system" to
describe a library that provides the foundation required for OOP
programming, and Glib is an example of such a library. Glib provides a C
implementation of classes, inheritance, reference counting, signals,
interfaces, and object properties. By using Glib, C programmers can write
object-oriented programs with ease. So, it is possible to write object-oriented programs in C.
However, you may be wondering why the GTK+ developers didn't just go ahead
and use C++ instead. Without covering every possible explanation, here
are a few reasons why it makes sense to have an object system for C. For
one, there are many developers who simply prefer C to C++. In other
situations, using a C++ compiler may not be an option due to project or
platform constraints. Whatever the reason, having an object system for C
opens up OOP programming (and GNOME programming, in particular) to a wider
number of potential developers, and for that we're grateful. C++ wrappers
That being said, all those C++ fanatics out there shouldn't be worried --
you can write GNOME programs in C++ too. Since C++ is a superset of the C
language, you can easily combine C-style Glib/GTK+ code with your existing
C++ projects. Alternatively, you can use a Glib/GTK+ C++ wrapper
instead. A Glib/GTK+ C++ wrapper will allow you to interact with Glib
objects using native C++ classes and objects.
Looking at GObject
Okay, so now you know what an object system is and what it's used for. Now,
we'll take a look inside the Glib object system and explore some
foundational GObject programming concepts. Up until recently, the GObject
system was implemented in the GTK+ 1.2.x library. Now, with the imminent
release of GTK+ 2, the object system has been moved down one layer to the
new Glib 2 library. Thanks to this move, the Glib object system is now
completely independent of GUI-related trappings. This is great news for
those of us who would like to use classes and objects in non GTK+-based
programs such as console-based applications and tools. Now, let's see what the Glib object system (also called "GObject")
looks like in comparison to the object system of C++ and the Java language. First,
let's do a syntax comparison. In C++, you'd invoke a method via an object
pointer as follows:
object->function(arg_a, arg_b);
Using a C++ object reference, you'd type:
object.function (arg_a, arg_b);
The Java language only has references, not pointers. The Java method call syntax is
identical to a C++ referenced object method call:
object.function (arg_a, arg_b);
In contrast, GObject uses standard C function call syntax. The object
pointer is passed as the first argument to the function. Also note that the
function name is prefixed with the class name in order to prevent namespace
collisions:
classname_function (object, arg_a, arg_b);
You may be wondering exactly how calling a GObject method
differs from calling a simple C function. Well, from the perspective of
the C programming language itself, there is no difference. As far as your
C compiler is concerned, it is simply calling a function, and the first
argument of that function happens to be a pointer to a standard C
structure. So your C compiler doesn't even know that we are writing an
object-oriented program. However, don't let this fact fool you into
thinking that GObject OOP programming is just a bunch of hokey
technojargon for good old-fashioned C programming. GObject does do
a lot of stuff behind the scenes, allowing you to create subclasses of
existing classes, interfaces to classes (which we will discuss
later in this article) and more. Yet all this OOP functionality has been
designed to be fully compatible with standard C programming
constructs. Here's another important thing to know about GObject method calls.
When passing the object to a class function (when you're invoking a
method), you need to cast the object to match the expected type of
the function you're calling. For example, if you have a GtkButton object
that you want to pass to a function that takes a GtkWidget argument, you
would write:
gtk_widget_show (GTK_WIDGET (button));
GObject interfaces
In addition to the many improvements to GObject, Glib 2 introduces an OOP
concept called "interfaces." To understand what interfaces are, let's
look at an example. Let's say that we wanted to create a Pegasus object for an
application. Since a pegasus is a mythological winged horse, the natural
OOP thing to do would be to create a new Pegasus class that uses the Horse
class as its parent. Then, we could add the necessary code to our new
class to support "wings." In this case, creating Pegasus as a subclass of
Horse makes sense because it expresses a relationship between the
two classes. However, let's say we had a variety of unrelated classes, such as
Horse, Car, and House, and we wanted to make it possible for all of them to
talk. This new ability has nothing to do with their relationship
to one another -- in fact, these three classes are not related in any way
that is meaningful to us. Yet we want all of them to support new
abilities related to talking. What would we do? Interfaces provide a solution to this problem, allowing us to add
common capabilities to disparate classes. So going back to the example
above, we could simply write a "Talk" interface for the Horse, Car, and
House classes. Suddenly, our three unrelated classes are "talk-enabled"
and are able to work with new talk-related functions that we create. And
these new talk-related functions happily interact with our objects by
using the "Talk" interface themselves. So, thanks to interfaces, our
three unrelated classes now speak the same language. In Glib, you can create any number of interfaces for a class. So, if
we created a Talk interface for our House class, we could define a say()
function as follows:
void say (Talk *mytalk, const char *myphrase);
Then, we could call the say() function, casting our myhouse variable to the "Talk" interface:
say (TALK(myhouse), "hello there!");
A more down-to-earth, authentic example of interfaces can be found in
the GtkEditable interface in GTK+ 2 (see Resources for a link). Both the text widget and the
entry widget implement this interface.
GObject signals
Generally, event-driven GUI programs consist of one main loop. In this
loop, the program continually waits for new messages to be sent from the X
server. These messages, called events, are interpreted by the program,
and allow the program to respond to the user selecting a menu item,
clicking on a button, and so on. Signals are a lot like events, except that they connect objects to one
another. They allow objects to automatically "react" to changes in the
state of another object without requiring an explicit event loop. The
signal of one object simply needs to be connected to the method of
another. Then, when the first object "emits" the signal (due to an
internal change in state), the second object "catches" this change and
reacts appopriately. No event loop is needed because signals are
implemented using callbacks -- the emission of a signal simply results in
a C function being called. Signals are the efficient and flexible glue
that tie the objects in your program together. In fact, if you look at the previous
GNOMEnclature column, you'll see that we had quite a few example code
lines that looked like this:
g_signal_connect (G_OBJECT (window), "destroy", gtk_main_quit, NULL);
In the above code snippet, we connect the "destroy" signal of
our main window object to the gtk_main_quit() function. Thanks to
g_signal_connect(), when our window emits a "destroy" signal (when the
window is closed), our program will automatically quit. If you've ever
done any event-driven programming in the past, I think you'll find signals
to be a refreshing change from the common practice of setting up an
explicit event processing loop. So, we've seen how to connect a signal to a standard C function. But
how would we connect this same signal to the method of a particular
object? Easy -- simply use the following template:
g_signal_connect (G_OBJECT (window), "destroy", classname_function, object);
Simply replace classname_function with the method to be called, and
replace object with the object pointer upon which the method should
operate. To learn more about signals, check out the Resources section of this article for some good
links. However, note that Glib and GTK+ 2.0 bring some changes to the area of
signals. While the underlying concepts haven't changed, the signal system
has been moved from GTK+ 2.0 to Glib. This has the handy side-effect of
making it possible to use signals in non-GUI applications. If you are
writing new GNOME 2.0 code, you should use Glib's new GSignal class; GTK+
2.0 signals are still around, but simply consist of a "wrapper" that uses
Glib's GSignal behind the scenes. Despite all the changes, Glib signals (prefixed by g_signal_) are
generally very similar to their GTK+ counterparts (which are prefixed by
gtk_signal_). However, if you've created or are planning to create your
own custom signals, you should read the GObject GSignal reference
documentation (see Resources) to catch up
on some of the changes that may apply to you. Conclusion
Now that we've taken a look at GObject fundamentals, you should be
equipped with the necessary fundamental concepts to begin your GNOME
programming career. I'll see you in the next "GNOMEnclature" column, as we
continue our preparation for the GNOME 2 platform. See you then!
Resources - To learn more about signals, check out the GTK+ 1.2 tutorial,
particularly the Theory of Signals and Callbacks.
- For a much more in-depth treatment, check out the online version of
GTK+/GNOME Application Development
, by Havoc Pennington (New Riders Publishing).
- Check out the
GObject GSignal reference documentation.
- To learn more about the GTK+ 2 API, be sure to read the online GTK+2 Reference Manual
. In particular, you can see how the GtkEntry
and GtkText
widgets both implement the GtkEditable
interface.
- You'll find FAQs, discussion forums, and kindred GNOME-developing
spirits at the GNOME Developer's
Site and GNOME Web site.
- Read the
first "Getting ready for GNOME 2" article (developerWorks, January 2002).
- The GTK+ homepage is a good
place to start for all things GTK+-related.
- If you're interested in GNOME's new text handling capabilities,
be sure to visit the Pango project homepage.
- You can also learn more about Pango in The
Pango connection: Part 1 (developerWorks, March 2001) and The
Pango connection: Part 2 (developerWorks, April 2001).
- To learn more about accessibility under GNOME, visit the GNOME Accessibility
Project Web page.
- Find more Linux articles in the developerWorks Linux zone.
About the author  | |  |
Residing in Albuquerque, New Mexico, Daniel Robbins is the President/CEO
of Gentoo Technologies, Inc., the creator of Gentoo Linux, an advanced Linux for the
PC, and the Portage system, a next-generation ports system for
Linux. He has also served as a contributing author for the Macmillan
books Caldera OpenLinux Unleashed,
SuSE Linux Unleashed, and Samba
Unleashed. Daniel has been involved with computers in some
fashion since the second grade, when he was first exposed to the Logo
programming language as well as a potentially dangerous dose of Pac Man.
This probably explains why he has since served as a Lead Graphic Artist at
SONY Electronic Publishing/Psygnosis. Daniel enjoys spending time
with his wife, Mary, and their daughter, Hadassah. You can contact
Daniel at drobbins@gentoo.org.
|
Rate this page
|  |