GLib is a utility library which is heavily used in GTK+ and most of GNOME. GLib's functions are named starting with g_ (such as g_strdup), GLib's typedefs for common types are just prefixed with a g (such as gint32), and GLib's structures are capitalized and start with G (such as GHashTable).
GLib provides some typedefs for portability, simplification of code, clarity of code and yet others just to keep consistent. The following table lists these typedefs. If the Equivalent, field is blank, there is no platform independent equivalent.
Table 2-1. GLib Typedefs
Name | Equivalent | Description |
---|---|---|
gint8 | 8bit wide signed integer | |
guint8 | 8bit wide unsigned integer | |
gint16 | 16bit wide signed integer | |
guint16 | 16bit wide unsigned integer | |
gint32 | 32bit wide signed integer | |
guint32 | 32bit wide unsigned integer | |
gint64 | 64bit wide signed integer (see note below) | |
guint64 | 64bit wide unsigned integer (see note below) | |
gchar | char | Standard character value |
guchar | unsigned char | Standard unsigned character value |
gshort | short | Standard short integer |
gushort | unsigned short | Standard unsigned short integer |
glong | long | Standard long integer |
gulong | unsigned long | Standard unsigned long integer |
gint | int | Standard integer |
guint | unsigned int | Standard unsigned integer |
gfloat | float | Standard float number type |
gdouble | double | Standard float number type |
gboolean | int | Type for storing TRUE/FALSE values |
gpointer | void * | Type for storing pointers to arbitrary objects |
gconstpointer | const void * | Type for storing pointers to arbitrary immutable objects |
It should be noted that gint64 and guint64 might not be available on all platforms. You can check for this in your code by checking to see if the macro G_HAVE_GINT64 is defined.
As you can see, some of the typedefs such as gint seem to have no other meaning in life then that of having a 'g' prefix and looking the same as the other typedefs. The logic behind this is to make the code look more consistent and clear. While it is no crime not to use these typedefs, you should really be consistent in your code. Some of the typedefs such as gboolean are only for improving code clarity and you could just as well use int to do exactly the same thing, but the former method clearly indicates that you are talking about a value that can only take TRUE or FALSE.
There are some functions that have different implementations across different systems or are not extremely safe, or don't exist at all on some systems, so GLib provides it's own implementations or wrappers that have a constant behavior and usually check their arguments.
Here are some of the more useful functions that fit this category. Note that the prototype is more of an informative one, as some of these might be macros in reality.
Table 2-2. Few GLib Portability Functions
Prototype | Description |
---|---|
gchar * g_strdup (const gchar *) | Returns a newly allocated string which is a copy of the argument, if the argument is NULL, NULL is returned |
gpointer g_malloc (int size) | Returns a newly region of memory with 'size' bytes |
void g_free (gpointer p) | Frees memory pointed to by 'p', and only returns if 'p' is NULL |
gint g_snprintf (gchar *string, gulong n, gchar const *format, ...) | Works just like sprintf by printing the arguments according to the 'format' into string, however it will only use 'n' bytes of the string and will thus truncate the result if it needed more. It returns the number of bytes actually printed into 'string' |
void g_usleep (gulong count) | Suspend execution for at least 'count' microseconds |
gint g_strcasecmp (const gchar *s1, const gchar *s2) | Compare string s1 and s2 in a case insensitive manner |
gint g_strncasecmp (const gchar *s1, const gchar *s2, guint n) | Compare the first n characters of string s1 and s2 in a case insensitive manner |
And there are also some utility functions and macros that are not really found in the normal c library. Here is a very short list of some of the more important and useful ones.
Table 2-3. Few GLib Utility Functions
Prototype | Description |
---|---|
g_new (type,count) | A macro which will allocate new memory for 'count' items of type 'type' and cast the result to 'type'. It is equivalent to '(type) g_malloc(count * sizeof(type))' |
g_new0 (type,count) | Same semantics as g_new, except that the returned memory will be set to all zeros. Note that you should not assume that setting the memory to zeros will zero out floating point types |
gchar * g_strconcat (const gchar *str, ...) | When passed any number of arguments of the type (const char *) and a NULL after the last argument, it will return a newly allocated string that results by concatenation of all the arguments. |
gchar * g_strdup_printf (const gchar *format, ...) | A printf like function that will return a newly allocated string with the result of the printf operation |
gchar * g_strstrip (gchar *string) | Will strip leading and trailing whitespace from the string. It will not allocate new memory, but will modify the original string and return a pointer to it. If you wish to allocate new memory use a construction such as: 'string2 = g_strstrip(g_strdup(string1));' |
There are many other useful methods in GLib, and I urge you to study GLib documentation and the GLib header file (glib.h), and you may be able to save a lot of time by not re-implementing some basic functionality.
Probably the best part of GLib are its containers. Here's a list of GLib's containers.
Table 2-4. Common GLib Containers
Name | Description |
---|---|
GList | Doubly linked list |
GSList | Singly linked list |
GHashTable | Hash table |
GCache | Cache |
GTree | Balanced binary tree |
GNode | n-ary tree |
GString | Dynamically sized string |
GArray | Dynamically sized array |
GPtrArray | Dynamically sized array of pointers |
GByteArray | Dynamically sized array of bytes (guint8) |
The easiest to use are GList's. The basic GList structure is just a single node of the linked list and you can put your data into the data pointer in the GList structure. To store a linked list you just store a pointer to the first node of the list. Here is the list of functions that operate on a GList. The functions usually take in a pointer and return the new pointer of the list, since the first node could now be a different one.
Table 2-5. Most Important GList Functions
Prototype | Description |
---|---|
GList* g_list_append (GList *list, gpointer data) | Append 'data' to a list. 'list' can be NULL to make a new list. |
GList* g_list_prepend (GList *list, gpointer data) | Prepend 'data' to a list. 'list' can be NULL to make a new list. |
GList* g_list_remove (GList *list, gpointer data) | Remove the node containing 'data' from the list. |
GList* g_list_find (GList *list, gpointer data) | Find the GList node that contains the 'data'. |
GList* g_list_next (GList *list) | A macro that returns a pointer to the next node. |
GList* g_list_previous (GList *list) | A macro that returns a pointer to the previous node. |
void g_list_free(GList *list) | Free the entire list. |
To access the data from a particular GList node You look at the data member in the GList structure. So code that would create a linked list of two elements which are strdup'ed strings, and later free that list and the strings would look like:
GList *list = NULL; /*the actual list pointer*/ GList *li; /*just a temporary pointer to a node used for iterating over the list*/ ... /*here we add two strings to the list*/ list = g_list_append(list,g_strdup("String 1")); list = g_list_append(list,g_strdup("String 2")); ... /*here we loop though the list, freeing all the strings and then we free the list itself*/ for(li = list; li!= NULL; li = g_list_next(li)) { char *string = li->data; g_free(string); } g_list_free(list); |
Another simple to use and useful container is the GString container. It's a dynamically sized string container for the times when you don't know how large the string you will need will be. Here's a list of the most important functions.
Table 2-6. Most Important GString Functions
Prototype | Description |
---|---|
GString* g_string_new (const gchar *init) | Create a new GString with initial value of 'init' |
void g_string_free (GString *string, int free_segment) | Free the GString structure and optionally also the string data segment |
GString* g_string_append (GString *string, const gchar *val) | Append 'val' to 'string' |
GString* g_string_prepend (GString *string, const gchar *val) | Prepend 'val' to 'string' |
void g_string_sprintf (GString *string, const gchar *format, ...) | A sprintf like function for GString |
void g_string_sprintfa (GString *string, const gchar *format, ...) | A sprintf like function for GString, but appends the string instead of overwriting it |
To access the string data for use as a char *, just access the str element of the GString structure. You can actually free the GString structure without freeing this data segment. This is useful if you want to create a normal C string. The following example is a function that takes an array of integers and sprintfs them into a string and returns a char *.
char * create_number_list(int array[], int array_len) { int i; /* the array iterator */ GString *string; /* the GString */ char *ret; /* the return value */ /* make a new GString that is empty */ string = g_string_new(""); /* iterate over the integer array */ for(i=0; i<array_len; i++) { /* append the number to the string in parenthesis */ g_string_sprintfa(string, "(%d)", array[i]); } /* setup the return value */ ret = string->str; /* free the GString structure, but not the data */ g_string_free(string,FALSE); /* return the string */ return ret; } |
Though less often used then GList's and GString's. The hash table container is a very useful one. Usually by a hash table one would mean an object (in GLib's terms a gpointer) would have a string key, by which we could recall the object at a later time. GLib takes this a step further, making the key a gpointer as well, and letting you provide a hashing and a comparison function yourself. While this makes GHashTable much more flexible, it can lead to some confusion with respect to memory allocation for the keys. Let's give some important functions and deal with the details later:
Table 2-7. Most Important GHashTable Functions
Prototype | Description |
---|---|
GHashTable* g_hash_table_new (GHashFunc hash_func, GCompareFunc key_compare_func) | Creates a new hash table using the specified hash function and comparison function |
void g_hash_table_destroy (GHashTable *hash_table) | Destroy the hash table and free memory. This does not however free neither the data, nor the keys, you have to do this yourself |
void g_hash_table_insert (GHashTable *hash_table, gpointer key, gpointer value) | Insert a new 'value' with a key of 'key'. |
void g_hash_table_remove (GHashTable *hash_table, gconstpointer key) | Remove the value with the key of 'key' from the table. Doesn't free neither the key nor the value. |
gpointer g_hash_table_lookup (GHashTable *hash_table, gconstpointer key) | Fetch the pointer of the value, with the key of 'key'. Returns NULL if it isn't found |
gboolean g_hash_table_lookup_extended (GHashTable *hash_table, gconstpointer lookup_key, gpointer *orig_key, gpointer *value) | Lookup the data with the key of 'value_key', store the original key pointer in 'orig_key' and the value in 'value'. Returns TRUE if the lookup was successful else it returns FALSE. You should use this function when removing an item to get rid of the original key in memory. |
void g_hash_table_foreach (GHashTable *hash_table, GHFunc func, gpointer user_data) | Run a function for each data stored in the hash table. The 'user_data' will be passed to the function as the last argument. The GHFunc prototype follows. |
void (*GHFunc) (gpointer key, gpointer value, gpointer user_data) | This is the function prototype that you will use for the function that is passed to g_hash_table_foreach. It gets passed the key, the value and the user_data specified in the g_hash_table_foreach call. |
guint g_str_hash (gconstpointer v) | A standard string hash function for string hash tables |
gint g_str_equal (gconstpointer v, gconstpointer v2) | A standard string compare function for string hash tables |
To create a hash table, you pass the hash and key compare functions to g_hash_table_new. There are standard functions defined for strings (g_str_hash and g_str_equal) and others. However if you pass NULL as the hash and compare functions, you will get a direct pointer hash, where pointers will be actually themselves used as keys.
The problem of memory allocation becomes apparent when we start using string hashes. GHashTable doesn't store the string, all it stores is a pointer. Therefore, when inserting a value into the hash, you have to create a new copy of the key for that value. This is an important thing to remember as otherwise things are not going to behave really nice for you. The other problem is how to then get rid of the key. If you do a g_hash_table_remove, you give as a key a string with the same contents as the original key, but not the same memory location. After then a pointer to the original key would be lost and unless you stored a pointer to it somewhere, you just created a memory leak. What you need to do instead is to do a g_hash_table_lookup_extended first to get both the value and the original key pointer and then do the g_hash_table_remove.
The following example will make a new string hash, insert a couple of strings into it, retrieve them, and then destroy the hash and the values stored in it:
/* function we use for freeing the keys and data in the hash before we destroy the hash */ static void free_key_value(gpointer key, gpointer value, gpointer user_data) { g_free(key); g_free(value); } ... /* somewhere else in the code */ GHashTable *ht; /* create a new hash table with strings as keys */ ht = g_hash_table_new(g_str_hash, g_str_equal); /* insert a couple of strings (say colors keyed by shape) */ g_hash_table_insert(ht, g_strdup("triangle"), g_strdup("green")); g_hash_table_insert(ht, g_strdup("square"), g_strdup("red")); g_hash_table_insert(ht, g_strdup("circle"), g_strdup("blue")); /* again, somewhere else in the code */ ... /* now here we wish to print out the color of a square */ char *color; /* get the color of a square */ color = g_hash_table_lookup(ht, "square"); printf("The color of a square is: %s\n",color); /* yet again somewhere else */ ... /* Now here we just want to destroy the hash table and free all the * memory associated with it. We use the free_key_value function and * have it run over all the values in the hash table. */ g_hash_foreach(ht, free_key_value, NULL); /* now we can destroy the actual hash table */ g_hash_table_destroy(ht); |
For more information look at the glib.h header file and at the documentation on the www.gtk.org web site.