1.4 Basic Utilities


1.4 Basic Utilities

GLib has a number of utilities that simplify everyday interaction with the C programming language and the system that your program runs on. For functions dealing with GLib data structures, see Section 1.5.

1.4.1 Memory Management

If you employ GLib's memory management routines, you can save yourself some headaches ” GLib provides additional error checking and diagnostic functionality. As in C, there isn't much to learn; the table on the next page provides a reference for the C programmer.

Instead of malloc() , realloc , and free() , you can use g_malloc() , g_realloc() , and g_free() ; they operate in an identical fashion. To allocate memory and zero out any previous content, use g_malloc0() . Note that its syntax is like malloc , not calloc () .

The advantage of these functions over those in the standard C library is the built-in error handling. If a problem occurs during runtime, g_error() can step in to examine it (see Section 1.4.6). With the usual C library, you might be faced with a core dump if you fail to check the return codes carefully every time you allocate memory.

GLib Function

Corresponding C Function

gpointer g_malloc(gulong n_bytes)

void *malloc(size_t size ) with error handling

gpointer g_malloc0(gulong n_bytes)

like malloc() , but initializes memory as in calloc()

gpointer g_try_malloc(gulong n_bytes)

like malloc() without error checking

gpointer g_realloc(gpointer mem, gulong n_bytes)

void *realloc(void *ptr, size_t size) with error checking

gpointer g_try_realloc(gpointer mem, gulong n_bytes)

realloc() without error checking

void g_free(gpointer mem)

void free(void *ptr)

Note  

If you have some special reason for inspecting the return code by hand, you can do so with the GLib functions g_try_malloc() and g_try_realloc() , which work just like their C counterparts ” that is, they return NULL upon failure. You could use this in a place where the allocated memory isn't critical (for example, in extra buffers meant to improve performance) or when you're running some sort of probe.

Naturally, if you choose to override GLib's protection mechanism, you need to know exactly what you're doing. For most applications, the normal functions like g_malloc() can save you a lot of code, frustration, and time.

Normal practice dictates that you do not specify the requested memory block size for functions like malloc() and g_malloc() as a concrete number. Instead, you make the compiler or runtime system figure it out from a type size multiple, usually with sizeof() . To make the data types agree, you must apply a cast to the malloc() return value. All of this makes for a mess of parentheses and stars, so GLib offers the macros g_new() , g_new0() , and g_renew() , as demonstrated in this code fragment:

 typedef struct _footype footype; footype *my_data; /* Allocate space for three footype structures (long version) */ my_data = (footype *) g_malloc(sizeof(footype)*3); /* The abbreviated version using g_new */ my_data = g_new(footype, 3); /* To initialize the memory to 0, use g_new0 */ my_data = g_new0(footype, 3); /* Expand this block of memory to four structures (long version) */ my_data = (footype *) g_realloc(my_data, sizeof(footype)*4); /* Shorter version */ my_data = g_renew(my_data, 4); 

You can clearly see how g_new() abbreviates g_malloc() and g_renew() is a short form of g_recalloc() in this fragment. In addition, g_new0() is a brief form for invoking g_malloc0() .

Warning  

Remember that you need to use a type with g_new() , just as you would with sizeof() . Something like b = g_new(a, 1) (where a is a variable) yields a compilation error. It will be an ugly error because g_new() is a macro.

Memory Chunks

GUI applications tend to repeatedly allocate memory in identically sized blocks ( atoms ). Furthermore, there are relatively few kinds of atoms. GLib uses a mechanism called memory chunks ( GMemChunk ) to provide applications with atoms. A chunk consists of several atoms; its block size is the total byte length of the component atoms. Therefore, the block size must be a multiple of the atom size.

Here is an example of how to use g_mem_chunk_new() to request a new memory chunk:

 GMemChunk my_chunk; my_chunk = g_mem_chunk_new("My Chunk",         /* name */                            42,                 /* atom size */                            42*16,              /* block size */                            G_ALLOC_AND_FREE);  /* access mode */ 

The g_mem_chunk_new() function has four arguments: a name for the memory chunk that you can use for diagnostics, the size of each atom (here, 42), the overall block size (it's easiest to write this as a multiple of the atom size), and the access mode (see below). The return value is a pointer to the new GMemChunk structure.

Note  

A GMemChunk isn't a data structure. It's a management system for memory fragments that can contain data structures.

The access mode (or type) gives you control over how to create and deallocate the atoms. There are two modes:

  • G_ALLOC_AND_FREE allows you to return individual atoms to the memory pool at any time.

  • G_ALLOC_ONLY permits deallocation of atoms only when you dispose of the entire memory chunk. This mode is more efficient than G_ALLOC_AND_FREE .

Here is how an example of how to allocate and free memory atoms in the chunk created in the preceding example:

 gchar *data[50000]; gint i; /* allocate 40,000 atoms */ for(i = 0; i < 40000; i++) {   data[i] = g_mem_chunk_alloc(my_chunk); } /* allocate 10,000 more atoms and initialize them */ for(i = 40000; i < 50000; i++) {   data[i] = g_mem_chunk_alloc0(my_chunk); } /* free one atom */ g_mem_chunk_free(my_chunk, data[42]); 

Here, g_mem_chunk_alloc() and g_mem_chunk_alloc0() make the individual atoms available. They work like g_malloc() and g_malloc0() , returning a pointer to the atom's memory, but they take a GMemChunk structure as the argument instead of a size specification. The g_mem_chunk_free() function takes a pointer to an individual atom, returning it to the unallocated pool of memory.

Warning  

Remember that you can use g_mem_chunk_free() only on atoms of a memory chunk that was created with the G_ALLOC_AND_FREE access mode. In addition, never use g_free() to free an atom ” this will inevitably lead to a segmentation fault, because one of the memory chunk deallocation functions will cause a double free() .

Several functions clean and dispose of atoms in memory chunks, working on an entire memory chunk at once. Here is an example of how to use these functions:

 /* free up any unused atoms */ g_mem_chunk_clean(my_chunk); /* free all unused atoms in all memory chunks */ g_blow_chunks(); /* deallocate all atoms in a chunk */ g_mem_chunk_reset(my_chunk); /* deallocate a memory chunk */ g_mem_chunk_destroy(my_chunk); 
  • g_mem_chunk_clean( chunk ) examines chunk and deallocates any unused memory. This procedure gives you some manual control over the underlying memory management. The g_mem_chunk_free() function doesn't necessarily deallocate an atom's memory immediately; GLib does this when convenient or necessary. g_mem_chunk_clean() forces immediate deallocation.

  • g_blow_chunks() runs g_mem_chunk_clean() on all outstanding memory chunks in your program.

  • g_mem_chunk_reset( chunk ) frees all atoms in chunk , including those in use. Be careful when using this function, because you might have a few lingering pointers to previously allocated atoms.

  • g_mem_chunk_destroy( chunk ) deallocates all atoms of chunk and the memory chunk itself.

As is the case for general memory management, GLib provides some macros that can save you some typing:

 typedef struct _footype footype; GMemChunk *pile_of_mem; footype *foo; /* create a memory chunk with space for 128 footype atoms */ pile_of_mem = g_mem_chunk_new("A pile of memory",                              sizeof(footype),                              sizeof(footype)*128,                              G_ALLOC_AND_FREE); /* the same thing, with g_mem_chunk_create */ /* the name will be "footype mem chunks (128)" */ pile_of_mem = g_mem_chunk_create(footype, 128, G_ALLOC_AND_FREE); /* allocate an atom */ foo = (footype *) g_mem_chunk_alloc(pile_of_mem); /* the same thing, with g_mem_chunk_new */ foo = g_mem_chunk_new(footype, pile_of_mem); /* the same thing, but zero out the memory */ foo = g_mem_chunk_new0(footype, pile_of_mem); 

The macros' purposes should be fairly obvious from the code. Note that g_mem_chunk_create() is a shorter way to use g_mem_chunk_new() if you know the atom data type. Note, too, that each macro automatically pieces together the chunk's name. Furthermore, g_mem_chunk_new() and g_mem_chunk_new0() are the memory chunk counterparts of g_new() and g_new0() .

If you want to see some statistics on your current memory chunks, use g_mem_chunk_print( chunk ) for a brief report on one chunk or use g_mem_chunk_info() to see detailed information for all chunks.

1.4.2 Quarks

To label a piece of data in your program, you usually have two options: a numeric representation or a string. Both have their disadvantages. Numbers are difficult to decipher on their own. If you know roughly how many different labels you need beforehand, you can define an enumeration type and several alphanumeric symbols. However, you can't add a label at run time.

On the other hand, you can add or change strings at run time. They're also easy enough to understand, but string comparison takes longer than arithmetic comparison, and managing memory for strings is an extra hassle that you may not wish to deal with.

GLib has a data type called GQuark that combines the simplicity of numbers with the flexibility of strings. Internally, it is nothing more than an integer that you can compare and copy. GLib maps these numbers to strings that you provide through function calls, and you can retrieve the string values at any time.

To create a quark, use one of these two functions:

 GQuark  quark  ; gchar *  string  ;  quark  = g_quark_from_string(  string  );  quark  = g_quark_from_static_string("  string  "); 

Both functions take a string as their only parameter and return the quark. The difference between the two is that g_quark_from_string() makes a copy of the string when it does the mapping, and g_quark_from_static_string() does not.

Warning  

Be careful with g_quark_from_static_string() . It saves a tiny bit of memory and CPU every time you call it, but may not be worthwhile because you create an additional dependency in your program that can cause debugging problems later.

If you want to verify that string has a quark value, call

 g_quark_try_string(  string  ) 

This function returns the string's quark if the program has already defined the string as a quark. A return value of zero (0) means that no quark corresponds to that string (there are no quarks with a numeric value of zero).

To recover the string from a quark, use

  string  = g_quark_to_string(  quark  ); 

If successful, this function returns a pointer to the quark string. Make sure that you don't run any free() calls on that pointer ” it isn't a copy.

Here is a short quark demonstration program:

 GQuark *my_quark = 0; my_quark = g_quark_from_string("Chevre"); if (!g_quark_try("Cottage Cheese")) {   g_print("There isn't any quark for \"Cottage Cheese\"\n"); } g_print("my_quark is a representation of %s\n", g_quark_to_string(my_quark)); 
Note  

GQuark values are numbers assigned to strings and are efficient when tested for equality. However, they have no set numeric order. You can't use the quark values to test for alphabetic order, and thus, you can't use them as sorting keys. If you want to compare the strings that quarks represent, you must extract the strings with g_quark_to_string() and then apply an operation like strcmp() or g_ascii_strcasecmp() to the result.

1.4.3 C Strings

GLib offers several string functions that interoperate with strings in the standard C library (not to be confused with GString , a GLib-specific string type described in Section 1.5.1). You can use these functions to augment or supplant functions like sprintf() , strdup() , and strstr () .

The following functions return a pointer to a newly allocated string that you must deallocate yourself:

  • gchar *g_strdup(const gchar * str )

    Copies str and returns the copy.

  • gchar *g_strndup(const gchar * str , gsize n )

    Copies the first n characters of string and returns the copy. The copy always contains an additional character at the end: a NULL terminator.

  • gchar *strnfill(gsize length , gchar * fill_char )

    Creates a string that is length characters long and sets each character in the string to fill_char .

  • gchar *g_strdup_printf(const gchar * format , ...)

    Formats a string and parameters like sprintf() . However, you don't need to create and specify a buffer as you would in sprintf() ; GLib does this automatically.

  • gchar *g_strdup_vprintf(const gchar * format , va_list args )

    Like the preceding function, but is the analog to vsprintf() , a function that uses C's variable argument facility described on the stdarg(3) manual page.

  • gchar *g_strescape(const gchar * source , const gchar * exceptions )

    Translates special control characters, backslashes, and quotes in source to the normal ASCII range. For example, this function converts a tab to \t . The translations performed are for the backspace ( \b ), form feed ( \f ), line feed ( \n ), carriage return ( \r ), backslash ( \ becomes \\ ), and double quotes ( " becomes \" ). Any additional non-ASCII characters translate to their octal representation (for example, escape becomes \27 ). You can specify any exceptions in the string exceptions .

  • gchar *g_strcompress(const gchar * source )

    The reverse of g_strescape() ; that is, converts an ASCII-formatted string back to one with real escape characters.

  • gchar *g_strconcat(const gchar * string1 , ..., NULL)

    Takes any number of strings as parameters and returns their concatenation. You must use NULL as the final argument.

  • gchar *g_strjoin(const gchar * separator , ..., NULL)

    Joins a number of strings, adding separator between each string. For example, gstrjoin("", "foo", "bar", NULL) yields "foobar" . As with g_strconcat() , you must place NULL at the end of the argument list. With a NULL separator, gstrjoin() operates like g_strconcat() .

For the following functions, you may need to allocate space for the result; GLib won't make a copy for you. These functions work much like their C counterparts, where one of the arguments contains a buffer large enough for a processed string.

  • gchar *g_stpcpy(gchar * dest , const gchar * src )

    Copies src to dest , including the NULL terminator. Upon success, this function returns a pointer to the copy of this terminator in dest . This function is useful for efficient string concatenation.

  • gint g_snprintf(gchar * string , gulong n , const gchar * format , ...)

    Like sprintf() ; you must ensure that there is enough space for string in the result. However, you must specify the length of this buffer with n . The return value is the length of the output string, even if the output is truncated due to an insufficient buffer. This is the C99 standard, not the traditional snprintf () behavior that your machine's C library may exhibit.

  • gint g_vsnprintf(gchar * string , gulong n , const gchar * format , va_list list )

    Like the preceding function, but with variable arguments.

  • gchar *g_strreverse(gchar * string )

    Reverses the order of the characters in string . The return value is also string .

  • gchar *g_strchug(gchar * string )

    Eliminates whitespace from the beginning of string , shifting all applicable characters to the left in string . Returns string .

  • gchar *g_strchomp(gchar * string )

    Eliminates whitespace from the end of string . Returns string .

  • gchar *g_strstrip(gchar * string )

    Eliminates whitespace from the beginning and end of string . Returns string .

  • gchar *g_strdelimit(gchar * string , const gchar * delimiters , gchar * new_delimiter )

    Changes any characters found in delimiters to new_delimiter . If delimiters is NULL , this function uses "_-<>." ; this is the standard set found in G_STR_DELIMITERS . Returns string .

  • gchar *g_strcanon(gchar * string , const gchar * valid_chars , gchar * substituter )

    Replaces any character in string that isn't in valid_chars with substituter . Returns string . Note that this function complements g_strdelimit() .

With the exception of g_ascii_dtostr() , these string functions do not alter their arguments:

  • gchar *g_strstr_len(const gchar * haystack , gssize haystack_len , const gchar * needle )

    Looks through the first haystack_len characters of haystack for needle . This function stops when it finds the first occurrence, returning a pointer to the exact place in haystack_len . When this function fails to find needle , it returns NULL .

  • gchar *g_strrstr(const gchar * haystack , const gchar * needle )

    Like g_strstr_len , except that this function returns the last incidence of needle in haystack , and it does not take a size parameter.

  • gchar *g_strrstr_len(gchar * haystack , gssize haystack_len , gchar * needle )

    Identical to g_strrstr , except that it searches only the first haystack_len characters of haystack .

  • gsize g_printf_string_upper_bound(const gchar * format , va_list args )

    Examines format and args , returning the maximum string length required to store printf() formatting.

  • gdouble g_ascii_strtod(const gchar * nptr , gchar ** endptr )

    Converts string to a double-length floating-point number. If you supply a valid pointer address for endptr , this function sets the pointer to the last character in string that it used for the conversion. The difference between this function and strtod () is that this function ignores the C locale.

  • gchar *g_ascii_dtostr(gchar * buffer , gint buf_len , gdouble d)

    Converts d into an ASCII string, writing into buffer of maximum length buf_len and ignoring the C locale so that the output format is always the same. The resulting string is never longer than G_ASCII_DTOSTR_BUF_SIZE . This function returns a pointer to buffer .

Note  

Use g_ascii_strtod() and g_ascii_dtostr() to write to and read from files and data streams, not for anything that people read. Because these functions use a unified, locale-independent format, you'll be protected from certain problems. For example, if someone sets a German locale and runs your program, you won't have to worry about the fact that its locale reverses the meanings of the comma and dot for numbers.

Finally, here are a few functions that handle arrays of strings ( gchar ** ). NULL pointers terminate these arrays.

  • gchar **g_strsplit(const gchar * string , const gchar * delimiter , gint max_tokens)

    Uses delimiter as a guide to chop string into at most max_tokens parts . The return value is a newly allocated array of strings that you must deallocate yourself. If the input string is empty, the return value is an empty array.

  • gchar *g_str_joinv(const gchar * separator , gchar ** str_array )

    Fuses the array of strings in str_array into a single string and returns it as a newly allocated string. If separator isn't NULL , g_str_joinv() places a copy of it between each component string.

  • gchar **g_strdupv(gchar ** str_array )

    Returns a complete copy (including each component string) of str_array .

  • void **g_strfreev(gchar ** str_array )

    Deallocates the array of strings str_array and the strings themselves .

Warning  

Don't use anything other than g_strfreev() to free up an array of strings returned by a function like g_strsplit() or g_strdupv() .

1.4.4 Unicode and Character Encodings

The traditional C string functions and those in the previous section are byte strings. These functions don't need to worry about the length of an individual character because each gchar is one byte long.

The functions in this section are different, because they work with Unicode characters and strings. Unicode is an extensive , unified character set that can encode any character in any language using the Universal Character Set (UCS; see ISO 10646). Unicode was originally a 16-bit encoding. GLib supports three different encoding schemes (see also man utf-8 ):

  • UCS-4 is the full 32-bit UCS-compatible encoding. Every character is 4 bytes long, with the Unicode character occupying the lower 2 bytes (the other 2 are typically zero). The GLib data type for UCS-4 is gunichar . It is 32 bits wide and is the standard Unicode type. Some functions use UCS-4 strings ( gunichar * ).

  • UTF-16 is the native encoding (UTF stands for Unicode Transformation Format). Every character is 2 bytes wide. GLib uses the gunichar2 type for characters in UTF-16. As with UCS-4, you will see UTF-16 strings ( gunichar2 * ).

  • UTF-8 is important in practice, because it is compatible with ASCII. Normal ASCII characters use 8 bits, but other characters may require 2 or more bytes. Therefore, every file in ASCII format contains valid UTF-8 text, but not the other way around. A significant disadvantage is that the nonuniform character width of UTF-8 renders random access in a text file next to impossible ; to get to a certain part, you must start from the beginning and iterate over the characters. GLib doesn't have a type for UTF-8 because the characters don't have uniform sizes. However, you can encode UTF-8 strings with normal character strings ( gchar * ).

GLib generally uses the 32-bit UCS-4 gunichar type as its Unicode standard. The following functions test individual Unicode characters:

  • gboolean g_unichar_validate(gunichar c ) returns TRUE if c is a valid Unicode character.

  • gboolean g_unichar_isdefined(gunichar c ) returns TRUE if c has a Unicode assignment.

  • gboolean g_unichar_isalnum(gunichar c ) returns TRUE if c is a letter or numeral.

  • gboolean g_unichar_islower(gunichar c ) returns TRUE if c is a lowercase letter.

  • gboolean g_unichar_isupper(gunichar c ) returns TRUE if c is an uppercase letter.

  • gboolean g_unichar_istitle(gunichar c ) returns TRUE if c is titlecase.

Note  

Titlecase doesn't appear much in English (or many other languages, for that matter). A titlecase letter is usually some sort of composite character or ligature where the first part of the composite goes to uppercase when the letter is capitalized at the start of a word. An example is the Lj in the Croatian word Ljubija . [1]

  • gboolean g_unichar_isalpha(gunichar c ) returns TRUE if c is a letter.

  • gboolean g_unichar_isdigit(gunichar c ) returns TRUE if c is a base-10 digit.

  • gboolean g_unichar_isxdigit(gunichar c ) returns TRUE if c is a hexadecimal digit.

  • gboolean g_unichar_ispunct(gunichar c ) returns TRUE if c is some sort of symbol or punctuation.

  • gboolean g_unichar_isspace(gunichar c ) returns TRUE if c is a form of whitespace, including spaces, tabs, and newlines.

  • gboolean g_unichar_iswide(gunichar c ) returns TRUE if c normally requires twice the space of a normal character to draw on the screen.

  • gboolean g_unichar_iscntrl(gunichar c ) returns TRUE if c is a Unicode control character.

  • gboolean g_unichar_isgraph(gunichar c ) returns TRUE if you can print c ; that is, if it's not a control character, format character, or space.

  • gboolean g_unichar_isprint(gunichar c ) is like g_unichar_isgraph() , but also returns TRUE for spaces.

If you have a gunichar character c and want to know its classification in Unicode, you can run

 g_unichar_type(  c  ) 

This function returns one of the following constants (more information in [TUC]):

  • G_UNICODE_LOWERCASE_LETTER : Lowercase letter

  • G_UNICODE_UPPERCASE_LETTER : Uppercase letter

  • G_UNICODE_TITLECASE_LETTER : Titlecase letter

  • G_UNICODE_CONTROL : Unicode control character

  • G_UNICODE_FORMAT : Unicode formatting character

  • G_UNICODE_MODIFIER_LETTER : Modifier (odd-looking letters that modify pronunciation)

  • G_UNICODE_SURROGATE : A composite of two 16-bit Unicode characters that represents one character

  • G_UNICODE_UNASSIGNED : Currently unassigned character

  • G_UNICODE_PRIVATE_USE : Character reserved for private, internal use

  • G_UNICODE_OTHER_LETTER : Any miscellaneous letter

  • G_UNICODE_COMBINING_MARK : Mark that may be combined with another letter

  • G_UNICODE_ENCLOSING_MARK : Mark that contains another letter

  • G_UNICODE_NON_SPACING_MARK : Mark that usually requires no space to print; its position depends on another base character

  • G_UNICODE_DECIMAL_NUMBER : Digit

  • G_UNICODE_DECIMAL_LETTER_NUMBER : Numeral made from a letter

  • G_UNICODE_OTHER_NUMBER : Any other numeral

  • G_UNICODE_CONNECTION_PUNCTUATION : Binding punctuation

  • G_UNICODE_DASH_PUNCTUATION : Dashlike punctuation

  • G_UNICODE_OPEN_PUNCTUATION : Opening punctuation (such as a left parenthesis)

  • G_UNICODE_CLOSE_PUNCTUATION : Closing punctuation

  • G_UNICODE_INITIAL_PUNCTUATION : Starting punctuation

  • G_UNICODE_FINAL_PUNCTUATION : Terminal punctuation

  • G_UNICODE_OTHER_PUNCTUATION : Any other punctuation

  • G_UNICODE_CURRENCY_SYMBOL : Monetary currency symbol

  • G_UNICODE_MODIFIER_SYMBOL : Modifier symbol (for example, an accent )

  • G_UNICODE_MATH_SYMBOL : Mathematic symbol

  • G_UNICODE_OTHER_SYMBOL : Any other odd symbol

  • G_UNICODE_LINE_SEPARATOR : A line break (for example, a line feed)

  • G_UNICODE_PARAGRAPH_SEPARATOR : Divides paragraphs

  • G_UNICODE_SPACE_SEPARATOR : An empty space

Here are some functions for converting single gunichar characters:

  • gunichar g_unichar_toupper(gunichar c )

    Converts c to uppercase if possible and returns the result. It does not modify the character if it has an uppercase version.

  • gunichar g_unichar_tolower(gunichar c )

    Converts c to lowercase if possible.

  • gunichar g_unichar_totitle(gunichar c )

    Converts c to titlecase if possible.

  • gint g_unichar_digit_value(gunichar c )

    Returns the numeric equivalent of c . If c isn't a numeral, this function returns -1.

  • gint g_unichar_xdigit_value(gunichar c )

    Same as the preceding function, but with hexadecimal numerals.

Now that you know how to do some interesting things with gunichar characters, you probably want to know how you can get your hands on this kind of data. For the most part, you must extract Unicode characters from UTF-8-encoded strings, and in the process, you'll want make certain that these strings are valid, navigate them, and read the individual characters.

Note  

In the functions you're about to see, you can provide a NULL - terminated string, but this isn't always necessary. If a function takes a gssize parameter, you can specify the number of bytes in the UTF-8 string that the function should process. If you want to tell a function to process an entire NULL -terminated string, use -1 as the size.

  • gboolean g_utf8_validate(const gchar * str , gssize max_len , const gchar ** end )

    Reads at most max_len bytes of the UTF-8 string str , returning TRUE if the string is valid UTF-8 text. This function returns FALSE upon failure, and you can also specify an address to a gchar pointer as the end parameter. The function sets this *end to the first invalid character in the string when there is a problem. Otherwise , *end goes to the end of a valid string.

  • gunichar g_utf8_get_char_validated(const gchar * p , gssize max_len )

    Tries to extract the byte sequence at p from a UTF-8 character as a gunichar UCS-4 character, making sure that the sequence is valid UTF-8. This function returns the converted character upon success. Upon failure, there are two possible return values: (gunichar) -2 if the function ran out of data to process, or -1 if the sequence was invalid.

  • gunichar g_utf8_get_char(const gchar * p )

    Converts the UTF-8 character at p to a gunichar character and returns this result.

Warning  

g_utf8_get_char() doesn't check the validity of its parameters. Use this function for strings that you have already verified with g_utf8_validate() . Using these two functions is faster than running g_utf8_get_char_validated() on every single character in the string.

The rest of the functions in this section assume that their input is valid UTF-8. Unpleasant circumstances arise if the string is not UTF-8.

  • gchar *g_utf8_next_char(gchar * p )

    Returns a pointer to the character in the UTF-8 string following p . Therefore,

     p = g_utf8_next_char(p); 

    advances a pointer by one character. The pointer should not be at the end of the string. (This is actually a macro in the current GLib implementation, not a function.)

  • gchar *g_utf8_find_next_char(const gchar * p , const gchar * end )

    Same as the preceding function, but with end pointing to the end of the UTF-8 string p . If end is NULL , this function assumes that p ends with a NULL value. The return value is NULL if p already points the end of the string.

  • gchar *g_utf8_prev_char(gchar * p )

    Same as the preceding function, but looks back to the previous character. There is no error check, and p should not point the start of a string.

  • gchar *g_utf8_find_prev_char(const gchar * str , const gchar * p )

    Also returns the character previous to p , but provides an additional error check when you specify the start of the string with str . This function returns NULL upon failure.

  • glong g_utf8_pointer_to_offset(const gchar * str , const gchar * pos )

    Returns the offset (that is, the character index) of pos in the UTF-8 string str .

  • gchar *g_utf8_offset_to_pointer(const gchar * str , const gchar * pos )

    Returns a pointer to the pos -th character in the UTF-8 string str .

Because the traditional C string library doesn't work on UTF-8 strings, GLib provides some equivalents:

  • glong g_utf8_strlen(const gchar * str , gssize max )

    Computes the length (in characters) of str . You can specify a maximum byte length with max .

  • gchar *g_utf8_strncpy(gchar * dest , const gchar * src , gsize n )

    Copies n UTF-8 characters from src to dest . Note that you must allocate the necessary space at dest , and that n is the number of characters, not bytes.

  • gchar *g_utf8_strchr(const gchar * str , gssize len , gunichar c )

    Returns a pointer to the first occurrence of c in str , or NULL if the character is not present. Note that c must be in the UCS-4 encoding.

  • gchar *g_utf8_strrchr(const gchar * str , gssize len , gunichar c )

    Same as the preceding function, but looks for the last occurrence of c in str .

  • gchar *g_utf8_strup(const gchar * str , gssize len )

    Returns a new copy of str , translated to uppercase. This string could have a different length; characters such as the German scharfes S go from one character ( ) to two (SS) when converted to uppercase. You are responsible for deallocating the new string.

  • gchar *g_utf8_strdown(const gchar * str , gssize len )

    Same as the preceding function, but converts uppercase letters to lowercase. Don't expect a string like NUSSDORFER STRASSE to become Nu dorfer Stra e, though; your locale software probably isn't smart enough to get this right.

  • gchar *g_utf8_casefold(const gchar * str , gssize len )

    Changes the mixed-case version of str into a case-independent form and returns the result as a new string. This result isn't suitable for printed output, but works for comparison and sorting.

  • gchar *g_utf8_normalize(const gchar * str , gssize len , GNormalizeMode mode )

    Produces a canonical version of str . In Unicode, there are several ways of representing the same character, such as the case of a character with an accent: It can be a single character or a composition of a base character and an accent. To specify mode , use one of the following:

    • G_NORMALIZE_DEFAULT : Normalize everything that doesn't affect the text content.

    • G_NORMALIZE_DEFAULT_COMPOSE : Same as the preceding, but attempt to make composed characters as compact as possible.

    • G_NORMALIZE_ALL : Change everything, including text content. For example, this would convert a superscripted numeral to a standard numeral.

    • G_NORMALIZE_ALL_COMPOSE : Same as the preceding, but attempt to make composed characters as compact as possible.

Note  

Before you compare UTF-8 strings, normalize them with the same mode. The strings might have the same value but slightly different encoding styles that a comparison function won't recognize.

  • gint *g_utf8_collate(const gchar * str1 , const gchar * str2 )

    Compares the UTF-8 strings str1 and str2 linguistically (at least as much as possible). If str1 is less than str2 in the sort order, the return value is -1; if the strings are equal, 0 is the result; and if str2 comes before str1 , this function returns 1.

  • gchar *g_utf8_collate_key(const gchar * str , gssize len )

    Returns a sorting key for str . If you compare two of these keys with strcmp() , the result will be the same as a comparison of their original strings with g_utf8_collate() .

Note  

If you compare a number of UTF-8 strings frequently (for example, if you're sorting them), then you should obtain keys for all of the strings and use strcmp() as your comparison function. This approach is much faster than using g_utf8_collate() every time, because that function normalizes its parameters every time it runs, and that involves not only a bit of computation, but also memory management time.

Several conversion functions translate strings among the different Unicode encoding schemes. In general, they take a string str as input and produce a freshly allocated NULL -terminated string with the same content in a different format. Some of these functions have an items_read parameter, which is actually a pointer to an integer; it can write the number of base units converted to this integer (here, base units refers to the number of bytes in UTF-8, 16-bit words in UTF-16, and 32-bit words in UCS-4). Therefore, if you want a function to store this count in a variable i , you would pass &i as the items_read parameter to the function. Similarly, you can employ the items_written parameter to record the number of characters written to the output stream. You can use NULL for both of these parameters if you don't care about this information.

If the input is in UTF-8 format, and items_read is NULL , an error will occur when an incomplete character occurs at the end of the input string ( str ). If you want to find out what this (or any other) error is, use the address of a GError pointer as the error parameter (see the "Error Codes" section on the next page; the error class is G_CONVERT_ERROR ). These functions return NULL on failure:

  • gunichar2 *g_utf8_to_utf16(const gchar * str , glong len , glong * items_read , glong * items_written , GError ** error )

    Converts a UTF-8 string to a UTF-16 string.

  • gunichar *g_utf8_to_ucs4(const gchar * str , glong len , glong * items_read , glong * items_written , GError ** error )

    Converts a UTF-8 string to a UCS-4 string.

  • gunichar *g_utf8_to_ucs4_fast(const gchar * str , glong len , glong * items_written )

    Same as the preceding function, but roughly twice as fast, because it doesn't perform any error checking. Consequently, this function doesn't have the items_read and error parameters.

  • gunichar *g_utf16_to_ucs4(const gunichar2 * str , glong len , glong * items_read , glong * items_written , GError ** error )

    Converts UTF-16 to UCS-4.

  • gchar *g_utf16_to_utf8(const gunichar2 * str , glong len , glong * items_read , glong * items_written , GError ** error )

    Converts UTF-16 to UTF-8.

  • gunichar2 *g_ucs4_to_utf16(const gunichar * str , glong len , glong * items_read , glong * items_written , GError ** error )

    Converts UCS-4 to UTF-16.

  • gchar *g_ucs4_to_utf8(const gunichar * str , glong len , glong * items_read , glong * items_written , GError ** error )

    Converts UCS-4 to UTF-8.

  • gint *g_unichar_to_utf8(gunichar c , gchar * outbuf )

    Stores the UCS-4 character c as a UTF-8 character in outbuf . You must reserve at least 6 bytes in this buffer. The return value is the number of bytes written to the buffer. If outbuf is NULL , this function doesn't perform any translation; it simply reports the size of c in UTF-8.

Finally, a number of functions perform translations between Unicode and other character encodings:

  • gchar *g_locale_to_utf8(const gchar * str , glong len , glong * items_read , glong * items_written , GError ** error )

    Changes a string from your current locale's character encoding to UTF-8. This function (and those that follow) work just like the previous conversion functions.

  • gchar *g_filename_to_utf8(const gchar * opsysstring , glong len , glong * items_read , glong * items_written , GError ** error )

    Converts the filename opsysstring from your operating system to UTF-8.

  • gchar *g_filename_to_uri(const char * filename , const char * hostname , GError ** error )

    Combines filename and hostname into a UTF-8-encoded URI (Uniform Resource Identifier; URLs are a subset of these). The filename string must be a full pathname. You can specify NULL for hostname .

  • gchar *g_locale_from_utf8(const gchar * utf8string , glong len , glong * items_read , glong * items_written , GError ** error )

    The opposite operation of g_locale_to_utf8() ; this function translates utf8string into your current locale.

  • gchar *g_filename_from_utf8(const gchar * utf8string , glong len , glong * items_read , glong * items_written , GError ** error )

    Same as the preceding function, but the result is a filename that your operating system understands.

  • gchar *g_filename_from_uri(const gchar * uri , char ** hostname , GError ** error )

    Takes a uri value in UTF-8 encoding and produces its filename. If there is a hostname in uri as well, this function will extract it and set the pointer hostname to the hostname. If you don't care about the hostname, you can set this parameter to NULL .

Error Codes

The G_CONVERT_ERROR error class contains the following conditions:

  • G_CONVERT_ERROR_NO_CONVERSION : The requested conversion is impossible.

  • G_CONVERT_ERROR_ILLEGAL_SEQUENCE : The input string contains an invalid byte sequence.

  • G_CONVERT_ERROR_FAILED : The translation failed for an unknown reason.

  • G_CONVERT_ERROR_PARTIAL_INPUT : The input string isn't complete.

  • G_CONVERT_ERROR_BAD_URI : The input URI isn't valid.

  • G_CONVERT_ERROR_NOT_ABSOLUTE_PATH : A path given as input wasn't an absolute path , as required by a function like g_filename_to_uri() .

1.4.5 Timer

The Gtimer is nothing more than a stopwatch that is as accurate as your system clock. Here is a demonstration:

 /* gtimerdemo.c -- demonstration of GTimer */ #include <glib.h> #define DURATION 200000 int main(int argc, char **argv) {   GTimer *clock = NULL;   gint i;   gdouble elapsed_time;   gulong us;  /* microseconds */   clock = g_timer_new();   g_timer_start(clock);   g_print("Timer started.\n");   g_print("Loop started.. ");   for (i = 0; i < DURATION; i++) { ; }   /* wasting CPU time like this is only allowed in programming examples */   g_print("and finished.\n");   g_timer_stop(clock);   g_print("Timer stopped.\n");   elapsed_time = g_timer_elapsed(clock, &us);   g_print("Elapsed: %g s\n", elapsed_time);   g_print("         %ld us\n", us);   g_timer_destroy(clock);   return 0; } 

This small program illustrates everything that you need to know about GTimer . The clock variable is a pointer to a GTimer structure, initially set to NULL . To create one of these structures, the program calls g_timer_new() ; it sets clock to the return value (a GTimer pointer to the new structure).

The g_timer_start() function starts the stopwatch. The program runs through a loop that does nothing but waste processor time. [2] Afterward, it uses g_timer_stop() to halt the timer.

To retrieve the current state of the timer, call

  time  = g_timer_elapsed(  timer  ,  us_ptr  ); 

The return value is the elapsed time in seconds, represented as a double-width floating-point number. In addition, you can obtain the fractional part of the elapsed time in microseconds (millionths of a second) with g_timer_elapsed() if you provide a pointer to a gulong variable as us_ptr . Because this number does not include the whole numbers of seconds, you must multiply the integral part of the second count (obtained by a type cast) by one million and add it if you want a total number of microseconds.

Note  

If you don't care about microseconds, set us_ptr to NULL .

You can reset a GTimer structure with g_timer_reset( timer ) , and you can remove one that is no longer needed with g_timer_destroy( timer ) .

1.4.6 Message Logging

To help with runtime error diagnosis, GLib offers several utilities for logging messages to the system console. These are, in order of the priority levels:

  1. g_message() for informative messages indicating normal runtime behavior

  2. g_warning() for warnings or problems that won't cause errant operation (at least not yet)

  3. g_critical() for warnings that likely are going to matter

  4. g_error() for fatal errors; calling this function terminates your program

These utilities take parameters like printf() ” that is, a format string followed by a list of substitution parameters. You don't need to put a newline at the end of the format, though. To ensure that you and your users know that these messages come from your software, you should set the G_LOG_DOMAIN macro when you compile. This can be a short identification string that identifies the application or library. Most GNOME-based programs define G_LOG_DOMAIN with a compiler option like -DG_LOG_DOMAIN=\" name \" .

This program shows all four message logging types:

 /* messagedemo.c -- show logging features */ #include <glib.h> #define NR 42 int main(int argc, char **argv) {   g_message("Coffee preparation engaged");   g_warning("Bean canister #%d empty", NR);   g_critical("Water flow failure");   g_error("Heating element incinerated");   /* this program shouldn't reach this point */   return 0; } 

The output should look something like this:

 ** Message: Coffee preparation engaged ** (process:3772): WARNING **: Bean canister #42 empty ** (process:3772): CRITICAL **: Water flow failure ** ERROR **: Heating element incinerated aborting... 

Marking Levels as Fatal

The g_error() call in the preceding program yields an error message telling you that the program is about to abort (and perhaps produce a core dump). You can configure other log priority levels as fatal so that they behave in the same way. For example,

 g_log_set_always_fatal(G_LOG_LEVEL_WARNINGG_LOG_LEVEL_CRITICAL) 

sets this behavior for warning and critical messages. This function's argument is a bit mask that you create by applying bitwise OR to any of the following constants:

  • G_LOG_LEVEL_CRITICAL

  • G_LOG_LEVEL_WARNING

  • G_LOG_LEVEL_MESSAGE

  • G_LOG_LEVEL_INFO

  • G_LOG_LEVEL_DEBUG

Note  

If an application uses GTK+ or GNOME libraries, you can also supply --g-fatal-warnings as a command-line option to make all warning levels fatal.

Free-Form Messages

If you have have a message that doesn't fit the mold or tone of the preformatted logging utilities, you can send it to the console with g_print() or g_printerr() . The g_print() function works like printf() , sending its output to the standard output ( stdout ).g_printerr() sends to the stderr , the standard error.

Note  

Unlike message logging tools like g_message() , g_print() and g_printerr() require that you specify your own line break at the end of your message.

You may be wondering why you can't just use fprintf() with the desired output stream to do this work. Believe it or not, this function may not work well on a Windows system. Another reason is that you can define your own message-processing functions that can alter the log messages and send them output to any place that you want (such as a dialog window or log file). Here is an example:

 /* printhandlerdemo.c */ #include <stdio.h> #include <glib.h> #define N 1 /* print messages in ALL CAPS */ void my_printerr_handler(gchar *string) {   GString *msg;   msg = g_string_new(string);   msg = g_string_ascii_up(msg);   fprintf(stderr, "%s\n", msg->str);   g_string_free(msg, TRUE); } int main(int argc, char **argv) {   /* print to stdout */   g_print("If you lie %d time, no one believes you.\n", N);   /* print to stderr */   g_printerr("Ouch.\n");   /* but if you lie all of the time... */   g_set_printerr_handler((GPrintFunc)my_printerr_handler);   g_printerr("%d. Ouch. Ouch. Ouch. (Hey, that really hurts.)", N);   return 0; } 

You'll see how the string functions in my_printerr_handler() work in Section 1.5.1. Here is this program's output:

 If you lie 1 time, no one believes you. Ouch. 1. OUCH. OUCH. OUCH. (HEY, THAT REALLY HURTS.) 

As you can see, you set print handlers with g_set_print_handler() and g_set_printerr_handler() . Their only argument is a GPrintFunc function. The type definition is as follows :

 typedef void (*GPrintFunc) (const gchar *string); 

Therefore, your handler must be a void function with a single string argument.

There are two more functions that you should know about when constructing your own error messages: g_strerror() and g_strsignal() . These are platform-independent implementations of strerror() and strsignal() . The g_strerror() function takes an error code such as EBADF or EINVAL and converts it to a slightly more comprehensible message such as "Bad file descriptor" or "Invalid argument." Similarly, g_strsignal() returns the name of a signal when given a numeric signal code.

The advantages of these functions over strerror() and strsignal() are not just that they're platform independent, but that they also create UTF-8 output suitable as input for other libraries, such as GTK+.

1.4.7 Debugging Functions

GLib has several facilities that can help you find bugs in your program. Two of these are macros that take the place of normal return statements. In addition to breaking out of the function, they log a G_LOG_LEVEL_CRITICAL message. Therefore, you can use them in places that your program should not reach in normal operation:

  • g_return_if_reached() for void functions.

  • g_return_val_if_reached( val ) for other functions, where you need to return a value val .

Two other similar convenience macros are

 g_return_if_fail(  test  ) g_return_val_if_fail(  test  ,  val  ) 

If test is false, the function returns, logging a message in the process. You often see these at the beginning of GNOME functions, checking for valid parameters.

There are two more macros that carry out the rudimentary contract concept ” the assertion, where a certain condition must hold in order for a program to proceed in any meaningful sense:

  • g_assert() halts the program with g_error() if its parameter evaluates to FALSE .

  • g_assert_not_reached() doesn't take a parameter; it simply stops the program with an error message.

You'll find assertions throughout this book, as well as in most GNOME applications. In addition, most of the functions in the GNOME platform libraries use these safety features to protect against inappropriate arguments.

If your program is far enough along that you're sure that you don't need any assertions, you can set the G_DISABLE_ASSERT macro when you compile (for example, give the compiler a -DG_DISABLE_ASSERT flag). This disables all assertions and saves a little processor time, because it eliminates the tests.

1.4.8 Exception Handling with Error Reporting

The routines in the previous section help diagnose and eliminate serious runtime errors. However, these won't help you much with nonfatal errors that your program can overcome , ignore, or treat in a special way. For example, if a graphical application can't open a file selected from a pop-up file browser, you normally don't want the whole application to abort. Instead, you prefer it to find out just what the problem was, perhaps put up a dialog box, and do whatever it would do if you clicked the file browser's Cancel button.

People tend to refer to these types of errors as exceptions and ways to compensate for them as exception handling (the online GLib manual uses the term error reporting ). The traditional C style is for functions to return special error codes to test after a call; some functions provide additional details (for example, through the errno global variable). Higher-level languages often provide special syntax, such as try{} , throw() , catch(){} , and the like in C++ and Java.

GLib doesn't have any complex features like these because it uses C. However, it does provide a system called GError that's a little easier to use than the usual do-it-yourself method in C. The GError data structure is at the core; how you use this structure is just as important as its implementation.

GError and GError Functions

Functions that use GError take the address of a GError pointer as their last parameter. If you want to use a variable err declared with GError *err , you must pass it as &err . In addition, you should set the pointer's value to 0. You can specify NULL as this parameter if you like; in that case, the function will disable its error reporting.

A GError structure has the following fields:

  • domain (type GQuark ): The domain or class of the error; a label for the module or subsystem where the error occurs. Every error domain must have a macro definition with the format PREFIX_MODULE _ERROR (for example, G_FILE_ERROR ). The macro expands to a form that returns the quark's numeric value.

  • code (type gint ): The error code; that is, the specific error inside the error domain. Every possible error code requires a corresponding symbol of the form PREFIX_MODULE _ERROR_ CODE in an enumeration type called PrefixModule Error (for example, G_FILE_ERROR_TYPE in GFileError ).

  • message (type gchar * ): A complete description of the error, in plain language.

The following fragment demonstrates how to read an error condition from a function ( do_something() ) that uses GError:

 GError *error = NULL; /* use this GError variable as last argument */ do_something(arg1, arg2, &error); /* was there an error? */ if (error != NULL) {   /* report the message */   g_printerr("Error when doing something: %s\n", error->message);   /* free error structure */   g_error_free(error); } 

You can see from this code that you need to deallocate the error structure with g_error_free() after you're finished. Therefore, if you supply a GError parameter to a function, you should always check it; otherwise, you risk a memory leak.

If you want to do something other than report the error, you'll probably want to know the error domain and code. Instead of checking this by hand, you should use g_error_matches() , a function that matches errors against domains and codes. The first argument is the GError structure, the second is an error domain, and the third is a specific error code. If the error matches the domain and code, the function returns TRUE ; otherwise, it returns FALSE . Here is an example:

 GError *error = NULL; gchar *filename; BluesGuitar *fender, *bender; << .. >> filename = blues_improvise(fender, BLUES_A_MAJOR, &error); if (error != NULL) {   /* see if the expensive guitar is broken */   if (g_error_matches(error, BLUES_GUITAR_ERROR, BLUES_GUITAR_ERROR_BROKEN))   {     /* if so, try the cheap guitar */     g_clear_error(&error);     filename = blues_improvise(bender, BLUES_A_MAJOR, &error);   } } /* if nothing's working, default to Clapton */ if (error != NULL) {   filename = g_strdup("clapton-1966.wav");   g_error_free(error); } blues_play(filename); 

In this example, blues_improvise() runs, returning a filename if there wasn't a problem. However, if an error occurs, the program checks to see if the code was BLUES_GUITAR_ERROR_BROKEN in the BLUES_GUITAR_ERROR domain. If this was the problem, the program tries one more time with different parameters. Before this attempt, it clears error with g_clear_error() , a function that frees the GError structure and resets the pointer to NULL .

If there is something in error after this second try, indicating that something still isn't working right, the program gives up. Instead of trying any more blues_improvise() calls, it uses a default filename ( "clapton-1966.wav" ) so that blues_play() can do its thing.

Warning  

After you use a GError * structure, immediately deallocate it and reset the pointer. GError-enabled functions can't use the same pointer to several errors at the same time; there's space for only one error. As mentioned earlier, your program will have a memory leak if you do not free the GError memory.

Defining Your Own Error Conditions

To use the GError system to report errors in your own functions, do the following:

  1. Define an error domain by creating an appropriately named macro that expands to a unique GQuark value.

  2. Define all of the error code symbols with an enumeration type.

  3. Add a GError ** argument at the end of each of the functions where you want to use GError (that is, this argument is a pointer to a pointer to a GError structure). If the function uses variable arguments, put this parameter just before the va_args list ( ... ).

  4. In the places where your function has detected an error, create a fresh GError structure and fill it in accordingly .

Here is a definition of an error domain and some codes:

 /* define the error domain */ #define MAWA_DOSOMETHING_ERROR (mawa_dosomething_error_quark()) GQuark mawa_dosomething_error_quark(void) {   static GQuark q = 0;   if (q == 0)   {     q = g_quark_from_static_string("mawa-dosomething-error");   }   return(q); } /* and the error codes */ typedef enum {   MAWA_DOSOMETHING_ERROR_PANIC,   MAWA_DOSOMETHING_ERROR_NO_INPUT,   MAWA_DOSOMETHING_ERROR_INPUT_TOO_BORING,   MAWA_DOSOMETHING_ERROR_FAILED   /* abort code */ } 

Take a close look at the definition of mawa_dosomething_error_quark() in the preceding example. It creates new quark for the error domain if none exists, but stores the result in a static variable q so that it doesn't have to perform any additional computation on successive calls.

This fragment illustrates how to use the new domain and codes:

 void mawa_dosomething_simple(GError **error) {   gint i;   gboolean it_worked;   << do something that sets it_worked to TRUE or FALSE >>   if (!it_worked)   {     g_set_error(error,                 MAWA_DOSOMETHING_ERROR,                 MAWA_DOSOMETHING_ERROR_PANIC,                 "Panic in do_something_simple(), i = %d", i);   } } 

This function "does something," and if it fails, it uses g_set_error() to set the error condition before it returns. This function takes the error pointer address as its first argument, and if that isn't NULL , sets the pointer to a newly allocated GError structure. The g_set_error() function fills the fields of this structure with the third and fourth arguments (the error domain and code); the remaining arguments are the printf() format string and a parameter list that become the GError 's message field.

If you want to use the error code from another function, you need to take special care:

 void mawa_dosomething_nested(GError **error) {   gint i;   gboolean it_worked;   GError *simple_error = NULL;   << do something >>   if (!it_worked)   {     g_set_error(error,                 MAWA_DOSOMETHING_ERROR,                 MAWA_DOSOMETHING_ERROR_PANIC,                 "Panic in do_something_nested(), i = %d", i);     return;   }   do_something_simple(&simple_error);   if (simple_error != NULL)   {         << additional error handling >>         g_propagate_error(error, simple_error);   } } 

In mawa_dosomething_nested() , a similar error initialization occurs if the first part of the function fails. However, this functoin goes on to call do_something_simple() if the first part worked. Because the function can set an error condition, it would make sense to send that error condition back to the original caller. To do this, the function first collects the do_something_simple() condition in simple_error ; then it uses g_propagate_error() to transfer the GError structure from simple_error to error .

Warning  

Never pass a GError ** pointer that you got as a parameter to any other function. If it happens to be NULL , your program will crash when you try to dereference (access) anything behind it.

To send an error obtained from a function to some other place, use

 g_propagate_error(  error_dest, error_src  ) 

Here, error_dest is the destination of the error as a GError ** , and error_src is the source as GError * . If the destination isn't NULL , this function simply copies the source to the destination. However, if the destination is in fact NULL , the function frees the source error.

You might have noticed by now that GError tries hard to achieve transparency with respect to NULL , so that you don't have to worry about memory leaks or extra GError pointers when you don't care about the specific nature of the error. In addition, if one of your functions encounters NULL as the error condition, you can take this as a hint that the user doesn't desire any special error treatment and perhaps wants the function to patch up the problem as much as possible.

You should always keep in mind that GError is a fairly workable tool for dealing with exceptions, but only if you stick to the conventions.

[1] Thanks to Roman Maurer for this example.

[2] If you compile this program, disable your optimizer so that it doesn't eliminate this loop.




The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net