Copyright © 2008 Gergely Polonkai
This work is licenced under the Creative Commons Attribution-Share Alike 3.0 Unported License. To view a copy of this licence, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
A few weeks ago I got a new task to create a server-side software. After hours of thinking and planning, I have decided to get back to my childhood friend, GLib. This little boy, however, has gone mature since we last met.
After a few hours of re-learning, I found out that GLib doesn't have a real tutorial, like GTK has. My first thought was to write one, but after creating the skeleton of this document, I decided to make a bigger thing: a Gnome developers' tutorial.
The Gnome libraries can work together greatly. GLib for some basic programming; this is the base of the other libraries. Then we go up step by step from GLib, through GDK and GTK up to the highest levels like GnomeUI and friends. Most of these libraries are written to be highly portable, and besides their source code, many binaries exist for many different platforms.
All through this book I will talk about portability many times. I will try to write only about libraries and functions that are known to be available on more platforms. However, it is possible that some useful libraries don't exist on them. In those cases, I will say it in that part's preamble.
Also, in the examples I will try to use only the mentioned
libraries' functions, trying to avoid calls to other libraries. E.g. I
will use GLib's g_printf()
instead of the standard I/O function printf(), which
is well known, but required a library called stdc. Although this library
is available on almost every platforms, I will try not to use it this
time. This can be confusing sometimes, but you have been warned.
GLib is a powerful tool in the good hands. It gives you many possibilities: it works so well in file and network handling and thread execution, and so on. But trust me. You haven't heard everything yet.
In this part, I will show you some examples on using GLib's features. There will be some snippets which could be done much faster with standard C stuff, but most of those times you sacrifice portability on the altar of fastness. So always think before you decide: GLib is highly portable as it is available on many systems nowadays.
This tutorial is mostly based on the original GLib API documentation. As of the time of writing, it can be accessed at http://library.gnome.org/devel/glib/stable/.
The examples were written and tested with GLib version 2.6.15 (the most recent stable version as of writing), under a Gentoo Linux. I'm writing them in ViM, so although I will double check, it is possible that I will leave some ViM-specific stuff in the examples, like ViM modelines.
Usually, this is the heart of a GLib program. An event loop manages all the given sources of events. These events can come for various sources like file descriptors or sockets, or even timeouts. Or, if you want to, you can create your own event source, even with a new hardware driver, or whatever you want.
If you are creating a threaded application, you will likely have many independent sources in many threads. In this case, you will have to use contexts, called GMainContexts. A GMainContext can run only in one thread, but sources can belong to more than one contexts (thus threads).
Event sources also have a priority. By default, it's
G_PRIORITY_DEFAULT, which is defined as 0. All
priorities below 0 are higher. If more events occur in a given time, the
one with higher priority will be processed first. There are also special
events, which can be called idle events. Their priority number is a
positive value, thus they are processed when there are no events exist
with default (or higher) priority.
The event loops are represented by the GMainLoop data
type. It can be created with a call to
g_main_loop_new(). After it is created, it can be
started with a call to g_main_loop_run(). This will
continuously check for new events from each assigned sources and
dispatch them, or, if no events are available, run the idle functions.
If an event handler decides, it can call
g_main_loop_quit(), which will send a special event
to the main loop. This event will be processed immediately, and will
make the loop to end, and g_main_loop_run() to
return.
There are times, when multiple event loops need to be exist. A
typical case is when a GTK window opens a
dialog box, which is set to modal. In this case, the main window's loop
will wait for the dialog's main event loop to end.
GTK, however, contains wrapper functions for
this purpose, like gtk_main() and
gtk_main_quit().
Besides events, you can also register idle functions. If your application has to do some work what has very low priority (e.g refining graphics or save some backups or user information), it can be done with an idle function. These functions run only when no events exist in the event queue.
Timeout functions work in a bit different way. After the given
time, a new event gets into the event loop, with a default priority.
This event will generate a call to the specified function. After the
function call, the timer restarts, but only if the timeout function
returned TRUE. Otherwise, this timer gets
destroyed, and will generate events no more.
Example 1-1. Using main loops and timeout functions (01_main_loop_timeout.c)
#include <glib.h>
#define COUNT_UNTIL 2
#define TIMEOUT 2
static int counter;
GMainLoop *main_loop;
gboolean
timeout_func(gpointer data)
{
counter++;
g_printf("Running for the %d. time.\n", counter);
if (counter == COUNT_UNTIL)
{
g_main_loop_quit(main_loop);
return FALSE;
}
return TRUE;
}
int
main(int argc, char **argv)
{
counter = 0;
main_loop = g_main_loop_new(NULL, FALSE);
g_timeout_add_seconds(TIMEOUT, timeout_func, NULL);
g_main_loop_run(main_loop);
return 0;
}After compiling and running this example, you will see that (in
the ideal case) timeout_func() will be called
every two seconds, exactly two times. Let's get through this program,
line by line.
In main(), I simply initialize the
counter variable to indicate that the current loop
number is 0. Then I create the main loop object with
g_main_loop_new(). The first parameter is the
context, where the event loop will receive its events. As I passed
NULL, it will be set to the default context. In a
more complicated environment, e.g when using threads, it can be
something totally different. The second parameter is
FALSE, which indicates that this loop is currently
not running. In some special cases a main loop can be initialized with
a running state if you set this parameter to
TRUE.
When the main loop is created, I add a new timeout function. In
every TIMEOUT seconds, the function
timeout_func() will be called with the user
parameter of NULL.
Now that the main loop is ready, and we set up every event
sources, it's time to get this loop running. This is done in the next
line, with the call to g_main_loop_run(). It gets
only one parameter: the variable which contains the event loop
"object".
When our main event loop will end, the program gets to the last
line, which will make main() to return 0,
indicating that everything went fine. But how will this infinite loop
end?
timeout_func() will do this work. First of
all, it increases the counter variable, then it
prints out the number of the current call, and check the
counter variable. If it reached
COUNT_UNTIL, it calls
g_main_loop_quit(), which will tell the running
event loop main_loop to finish running. In this
case, the loop will wait for every running event handlers to return,
and when they do, the call to g_main_loop_run()
in main() will return. Also, to be so called good
programmers, we return FALSE to indicate that we
don't need this timeout function any more, so it can be removed from
the event loop's sources.
![]() | This strictness is not really needed in this example. As we
already said our event loop to stop running, it will never call
|
At the end of timeout_func() I return TRUE.
This will tell the timeout handler that we still need this function to
be called after the specified time (defined in seconds with the
TIMEOUT macro) elapses.
GLib implements its own memory handling
functions on top of the standard C methods. Although in general they are
the same, GLib extends the standard way
heavily. For example, when you try to allocate memory, and it fails (e.g
not enough memory or such), they terminate the program. This means that
you don't have to check if the memory allocation has succeeded. There
might be cases though when you want to try allocating new memory, but
you don't want your program to be terminated upon failure. Fot this
cases, there are the g_try_* functions. And, for
the best part: usually when you allocate memory, you will want to zero
that space. There are the functions with a zero at the end of their
names, which exactly do this (like g_malloc0(),
which allocates and initializes some memory with nul-bytes).
Besides these basic functions, with GLib you can work with equal-sized chunks of memory, which is very efficient on memory usage. Also, you can use caches, which can be used to share resources while conserving memory.
GLib has recreated the standard memory allocation procedures. Their functions use the standard ones as a backend, but they are extended heavily. They can terminate your application when they cannot allocate memory, or fill the allocated space with zeros. Also, there are some macros to ease the allocation of memory "arrays", like many int *'s. It's good to see this written, but how does it work?
Example 2-1. Allocating memory, the basic way (02_memory_allocation.c)
#include <glib.h>
int
main(int argc, char **argv)
{
gchar *string;
gsize huge_size = 0xffffffff;
string = g_new(gchar, 10);
/* string is not initialized, it points to a random area now */
string[0] = 'H';
string[1] = '!';
string[2] = '\0';
g_printf("%s\n", string);
g_free(string);
string = (gchar *)g_malloc0(10);
/* string is now initilazized with nul-bytes */
string[0] = 'H';
string[1] = '!';
g_printf("%s\n", string);
g_free(string);
/* The next allocate should fail */
string = g_try_malloc(huge_size);
if (string == NULL)
g_printf("Failed to allocate %lu bytes of memory.\n", huge_size);
else
g_free(string);
return 0;
}This little program demostrates the three main aspects of memory allocation. In the first part, I allocate a gchar pointer with 10 bytes of memory. g_new doesn't initialize the allocated memory, so the string I put in it by characters (H and !) has to be suffixed with a nul-byte.
Slices are small chunks of memory with equal sizes. Their use over the standard or GLib'ized malloc() family is wise if you need many chunks of memory with a small size, because the allocator is optimized. This way you application will consume much less physical memory. Although memory is not a real problem nowadays, it can also speed up your application. In pre-2.8 GLib they were called memory chunks, which had several disadvantages. So called memory chunks are now deprecated, and slices should be used instead.
Caches allow sharing of complex data structures. This can come handy if you need to share bigger resources.
Lists are useful tools to store and manage identical data. This data can be simple, like a plain string, or even a very complicated data structure. Storing this kind of data can be done several ways. Which one you should use depends on the goal.
Singly linked lists are the most effective if memory comes into your mind. In this case the internal structure contains only one pointer which points to the next element. This make searching a bit slow, as you need to start from the first element to iterate through the whole list. It uses the slice allocator, thus being very effective on memory usage.
The doubly linked list is the improved version of the singly linked one. In this case, the structure also contains a pointer to the previous value. This can speed up searching, but because of the two internal pointers it consumes a bit more memory. However, it also uses the slice allocator, so the memory usage of this list can still be small.
Queues are special doubly linked lists. Queues store both the starting and ending element of the list, so it is easy to use them both as a FIFO or LIFO queue. Using this type of list is much slower than the former two, as it always have to update its internal structure, which contains a pointer of the starting and ending element, so as the count of elements.
Sequences are the other type of special lists. The API suggests it's some sort of a list, but internally it is stored as a binary tree. This makes sequences highly sortable.
Hash tables are what their name suggests: tables. They are actually key-value pairs, making them good for storing data like configuration items and such.
Balanced binary trees can be looked at as they are hash tables optimized for searching and traversing in order.
This chapter tells you about utility functions to handle strings in many-many ways. You will learn how to allocate, copy, duplicate and format strings, how to check if a character is of a given type, convert between character sets, encode them in Base64 format, generate checksums, and much more.
There is a library called libxml2, which is developed in parallel to GLib. It can be used for XML parsing (either), but there are times when you need to parse a very small XML chunk. For this, GLib gave us this very simple XML parser.
Although the filesystem can be accessed with GIO (predecessor of Gnome-VFS), sometimes this small subset of GLib functions is more than enough.
Although most C implementations and I/O handling libraries implement this functionality well, there might be times when you want to write very portable programs which can work on all the platforms (well, at least on platforms where GLib itself works), or when you need some events to be generated from file descriptors.
For these times, GLib created IOChannels. These are some object-like stuff, which can integrate themselves in the event oriented self of GLib fine. That's because IOChannels have a little cousin, called watches. With watches, you can add a new event source to the main event loop's context, which can generate events when the associated file descriptor can be read or written. Also, IOChannels can be read and written with GLib's own functions.
One of GLib's strongest points is thread handling. With it, you can easily create single and simple threads for a small work, just like huge thread pools which can interact and can work concurrently. The former is an easy task with the already existing libraries, like Linux' pthread. The latter, however, can be a long, error-prone and complicated task. But with GLib, it's very easy to achieve such setup. Within them, you can set up your own communication mechanism, but GLib implements a well-working solution of asynchronous communication.
The following chapter is to emulate some old POSIX functions on Windows™. Naturally, they work only on Windows™ systems, so if you are programming only for Unices, you can safely skip this chapter. Also, if you want (almost) full POSIX compatibility on Windows™, you should try the CygWin project instead.
While using GLib, you will meet many new types. Some of them will be more than important, others will be forgotten long before you finish reading this document.
GLib defines a bunch of types. Some of them are non standard to C (like gboolean); others are highly portable, and they mean exactly the same on every different OS's and architectures (like int8, which is 8 bits long on every system); many exist in parallel to C to ease coding (like gpointer); and for last but not least there are types that mean exactly the same like in C, and they just unify the GLib type names (like gchar, which is just the same like C's char).
gboolean is a standard boolean type. It can get the value of TRUE or FALSE.
![]() | For the restless, gboolean is a simple integer
type, where |
gpointer is a typeless pointer. It's just an alias of void *. Easier than the original, and it looks better. gconstpointer is the constant version of it (const void *).
They are just aliases to C's standard types: char,
unsigned char, int, unsigned
int, short, unsigned short,
long and unsigned long. They only exists to
unify the type names. Their ranges can be checked with constants:
g* types' range is between G_MINx and
G_MAXx while gu* types' range is
between 0 and G_MAXUx. (The original
GLib documentation doesn't mention these
range constants for gchar and guchar
however).
These are the types I call highly-portables. They always have the given length (8, 16, 32 and 64 bits respectively).
![]() | In GLib versions before
2.0 64bit support was not necessary.
Thus, there was a macro called |
Synonyms of C's float and double
types. Their range is between -G_MAXx and
G_MAXx.
gsize corresponds to the original C99's size_t type. It is an integer value which has enough space to store a pointer. Thus it's a guint32 on 32 bit systems, and a guint64 on 64 bit systems. gssize is the signed version of gsize (a bit confusing naming convention after the variables like guint)
GLib has many useful macros which can be used at compile-time decisions and integration. With them, you can easily get the OS the compiler is running on, the memory page size, the directory separator, and so on. Below you can find a list of them.
These macros are defined only on the specific OS's
(Win32, BeOS,
UNIX respectively).
G_OS_WIN32 and G_OS_UNIX covers
more systems, like G_OS_WIN32 is defined on most
current Windows™ systems (as I know, from
Windows 95™ to Windows
Vista™), while G_OS_UNIX is defined on
all UNIX and UNIX-like systems, e.g BSD and Linux.
These contain the directory separator of the running OS. It's /
in Unices and \ in Windows™.
G_DIR_SEPARATOR holds it in a char
('/') while G_DIR_SEPARATOR_S holds it
in a string ("/").
These contain the search path separator character of the running
OS, just like G_DIR_SEPARATOR for the directories.
It's usually a : in Unices and a ; in
Windows™.
TRUE and FALSE hold the
two values of a gboolean type. NULL is
the standard NULL pointer, which is usually: (void
*)(0).
These macros return the smaller or greater value from
a and b, respectively.
Ensures that x is between
low and high. If
x is less than low, returns
low; if x is greater than
high, returns high; if
x is between low and
high, returns x. If
low is greater than high, the
result is undefined (will be low or
high, depending on the three values).
It is wise to install pkg-config with GLib. pkg-config is a very handy tool, which can help you in compiling GLib programs (and many others, like GTK and friends, and even GnuTLS and gSASL have pkg-config data available on most systems).
pkg-config will give you many compiler parameters for you, like the include directories and libraries to be linked with your new executable. E.g on my system, pkg-config --cflags --libs glib-2.0 will return -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -lglib-2.0, which tells my compiler to look for include files in /usr/include/glib-2.0 and /usr/lib/glib-2.0/include and link in the library called glib-2.0.
The examples below use gcc in a bourne shell-like environment. If you are running on another systems, the same can be achieved by running pkg-config with the parameters (the whole command between the backticks), and copying their output to the compiler's parameters.
Example C-1. Compiling single-source GLib programs with pkg-config
gcc `pkg-config --cflags --libs glib-2.0` -o executable source.c
Or if you have multiple source files, you can use the following example. Note that source3.c doesn't need GLib, maybe it doesn't contain GLib-related code ;)