GLib is a utility library for C that makes programming in C much more enjoyable. There are and always have been other such libraries, but none so popular and so well focused, so consistent and full of features. Before learning about GLib through GTK+/GNOME I relied primarily on C. And, being a C programmer, I was jealous of languages such as Perl and C++ with STL and their nice containers and consistent data storage functionality. The C standard library unfortunately has a large number of inconsistent and low-level functions that work differently on different platforms or are implemented only on certain platforms.
GLib is here to answer these problems. With respect to C, it addresses three fundamental problematic issues C users often face:
- Data containers
- Portability
- Utility
GLib's most prominent and powerful aspect is its containers, although it is also extremely useful in writing portable code. GLib does not require you to restrict code to small subsets of functionality that work on all target platforms, nor, when you use GLib, do you find yourself resorting to prayer as a method of ensuring satisfactory implementation of your functions on other platforms. It also provides utility functions more consistent than the standard C library, though designed in a similar spirit. Beyond all this, GLib touts the more exotic features of a simple lexical analyzer and a main loop functionality for event-driven applications.
The set of typedefs in GLib cover a wide range. Some are designed to reduce typing, some are typedefs for portability, some increase code clarity or naming consistency. To reduce typing, we have the typedefs for unsigned types. In these cases, instead of unsigned int, you type guint. This holds for all integer types, including the char type.
Portability typedefs address the problem of working with integers of specific sizes. A signed 16-bit integer, for example, is gint16; an unsigned integer of 8 bits is guint8. The sizes are 8, 16, 32, and 64 if the machine/compiler supports 64-bit integers. Be sure to check that the G_HAVE_INT64 macro is defined when using these.
GLib provides two basic typedefs that increase code clarity. These are the gpointer, which is a void *, and the gboolean, which is a typedef to an int. The latter increases code readability and allows for automatic code scanners to figure out the true/false nature of the value. As a matter of taste rather than functionality, GLib prefixes all the basic types with a "g", such as "gint". I personally don't use these, but some people find they provide increased consistency with the previous types.
Let's start with the most basic component, memory allocation. The standard malloc and free are OK, but not perfect. A free will crash, for example, if passed a NULL, and malloc may return NULL. Neglecting to check the return value may result in some unexpected application behavior. GLib defines equivalents to g_malloc and g_free. g_malloc is guaranteed to return a pointer to new memory. If it can't allocate more, the program will abort. This is preferable to getting a segfault later due to an unhandled NULL pointer. The g_free function can be passed a NULL, in which case it does nothing and makes for cleaner functions if you have objects with a lot of pointers that need to be freed.
While g_malloc is nice, g_new and g_new0 are much more operable. The latter two are nice, simple macros that take both the allocated object's type and the designated number of types. The g_new0 macro also sets the memory to all zeros. The nice aspect of this function is that it is easier to find errors using noninitialized values if you know there will be all zeros rather than random garbage. Let's look at an example of allocating a new array of 20 integers and then freeing it later.
int *array = g_new0(int, 20); ... g_free(array); |
You always find yourself working with C strings. The way C handles strings is both a blessing and a curse. With GLib's string utility functions you can get a handle on the bad parts. Let's start with some functions that modify the string you pass it. To convert to upper and lower case, call g_strup or g_strdown and pass the string as an argument. You might also want to get rid of leading or trailing (or both) whitespace. g_strchug strips the leading spaces of a string, g_strchomp strips trailing spaces, and g_strstrip (which is a macro actually) does both. These 3 functions actually return their argument. Don't be fooled by that -- they are not returning a new string. Returning the argument just makes the functions easy to chain together. For example, imagine reading a file and needing to strip all leading whitespace and make each line upper case:
FILE *fp = fopen("some-file.foo", "r");
char buf[256];
|
Let's look at some functions for allocating new strings. The g_strdup function is just a regular strdup function implemented using g_malloc. It duplicates a string and returns a pointer to the newly allocated string. You might want to duplicate at most a certain number of characters, in which case you should use g_strndup (which also takes an integer argument with that number). Another function is g_strconcat to which you pass an arbitrary number of arguments that are strings, followed by a NULL. It concatenates the strings and returns a newly-allocated memory with that string. For example, to write a function that appends some directory name and an extension to a string:
static void
foo(char *string)
{
char *file;
file = g_strconcat("/some/path/", string, ".txt", NULL);
...
g_free(file);
} |
There are also a couple of functions that effectively replace sprintf, which is not very safe. The first is g_snprintf, which is a portable implementation of the snprintf function. It takes the buffer as the first and the size of the buffer as the second argument. Otherwise it behaves exactly like sprintf, but it will never overrun the buffer. Sometimes, however, you don't know how much buffer is going to be enough and sometimes you want to allocate a new string to be printed to. In these cases you can use g_strdup_printf, which takes only the format string and its arguments and returns a newly-allocated string with the printed buffer.
char foo[256]; char *bar; |
There are, of course, many other GLib utility functions. See Resources for GLib documentation if you want to see the whole list.
Now that we're past the basics, let's delve into some of the containers. Most containers have one thing in common: the data they store is just a void pointer. This gives you greater flexibility, but you do not have any compiler-enforced type safety. Either make sure you're always using a single container for a single type, and never overload a container, or use some kind of runtime type checking mechanism. I have yet to have a bug in my code because of wrong types in a GLib container, so it doesn't seem like a big problem. One problem with using a void pointer is that it's hard to use the primitive types. However, since a pointer takes up as much space as an int on just about any platform you can think of (and, more importantly, on every platform to which GLib is ported), you could just store an integer instead of the pointer. GLib in fact defines macros that will correctly cast an int to a gpointer, GINT_TO_POINTER, and a gpointer to an int, GPOINTER_TO_INT. Use these macros so that your code is portable across different platforms without unnecessary warnings. For example:
gpointer foo = GINT_TO_POINTER(50); ... int bar = GPOINTER_TO_INT(foo); |
Linked lists are by far the most used (and perhaps abused) containers in GLib. The structural names for these are GList and GSList. GList is a doubly-linked list implementation and GSList is a singly-linked list implementation. They are very similar in function and most of the time you can just use GList, since the space penalty is very small for the extra pointer. The GSList has a couple of functions missing, but the rest are just the GList functions. Instead of starting with g_list_, GSList functions start with g_slist_.
GList *list = NULL; /* our list pointer */ GList *li; /* an iterator variable */ |
g_list_foreach(list, (GFunc)g_free, NULL); g_list_free(list); |
GList *list = NULL; char *foo = "foo"; char *bar = "bar"; |
Using g_string_insert_c
GString *gstring = g_string_new("foobar");
g_string_insert_c(gstring, 3, '-');
/* now gstring->str should be "foo-bar" */ |
Other things you might want to do include erasing a part of the string. This is done with g_string_erase, which (in addition to the GString pointer) takes an integer with the starting position and the length of the sector you want to erase.
GString *gstring = g_string_new("fooBLAHbar"); |
Now here comes the really neat function -- a sprintf-like function for GStrings. It comes in two varieties, g_string_sprintf, which acts like sprintf, overwriting the string, and g_string_sprintfa, which doesn't overwrite the string but rather appends to it. Appending is very useful for building, say, lists of things. Consider trying to build a comma-separated list of numbers from 1 to 10:
int i; GString *gstring = g_string_new(NULL); |
There are also functions to make a string all upper case or lower case (g_string_up and g_string_down), to truncate a string to a certain number of characters (g_string_truncate -- the number of characters is the second argument to the function), and to assign a new C string to a GString (g_string_assign).
GString *gstring = g_string_new("fooBAR"); |
GLib has a lot more to cover than I have space in this article. If you'd be interested in an article that discusses the hashes, the trees, relations, the lexical scanner, the main loop, file descriptor io, and so on, let me know!
- Consult the GLib reference manual.
- See a site devoted to GLib shared libraries for the Palm Pilot.
- Read last month's column on glade and libglade.
- A GNOME application programming tutorial is available.
- Visit the GNOME home page.
- Visit the home page of the GTK+ library.
- Try a GTK+ tutorial.
- Read the tutorial on how to make GNOME Panel applets.
- Learn the latest from the GNU project home page.
- Visit RHAD Labs at RedHat, where a lot of GNOME/GTK+ development goes on.
- Look into GIMP, where GTK+ originated.
- Find more things to do with Linux and GNOME at the author's Web site.

George (Jiri or Jirka in Czech) Lebl was born in Prague, Czechoslovakia, and now lives in San Diego, California, where he is trying to finish his degree. After several years using non-UNIX operating systems, he started using UNIX and became a UNIX bigot about four years ago, and a Free Software bigot about two years ago. He joined the GNOME project in the fall of 1997, and finally became a C bigot as well. And, most importantly, he's a VI user. He can be reached at jirka@5z.com.
Comments (Undergoing maintenance)





