GLib

Acuerdos en la Nomenclatura

GLib es una biblioteca muy usada en GTK+ y en casi todo GNOME. Las funciones de GLib empiezan con g_ (como g_strdup), la definición de tipos de GLib para la mayoría de ellos tienen simplemente como prefijo una g (como gint32), y las estructuras de GLib están en mayúsculas y empiezan con una G (como GHashTable).

Definición de Tipos

GLib proporciona algunos tipos predefinidos para facilitar la portabilidad, simplificar y clarificar el código y para mantener cierta consistencia. La siguiente tabla muestra estos tipos. Si el campo Equivalente está en blanco, significa que no hay representación equivalente en el C estandar.

Tabla 1. Tipos en GLib

NombreEquivalenteDescripción
gint8 Entero con signo de 8 bits
guint8 Entero sin signo de 8 bits
gint16 Entero con signo de 16 bits
guint16 Entero sin signo de 16 bits
gint32 Entero con signo de 32 bits
guint32 Entero sin signo de 32 bits
gint64 Entero con signo de 64 bits (vea nota al margen)
guint64 Entero sin signo de 64 bits (vea nota al margen)
gcharcharCarácter
gucharunsigned charCarácter sin signo
gshortshortEntero corto
gushortunsigned shortEntero corto sin signo
glonglongEntero largo
gulongunsigned longEntero largo sin signo
gintintEntero
guintunsigned intEntero sin signo
gfloatfloatNúmero real
gdoubledoubleNúmero real de doble precisión
gbooleanintTipo para almacenar valores VERDADERO/FALSO
gpointervoid *Tipo para almacenar punteros a distintos objetos
gconstpointerconst void *Tipo para almacenar punteros a distintos objetos inmutables

Se debe tener en cuenta que gint64 y guint64 pueden no estar disponibles en todas las plataformas. Puede comprobar esto en su código viendo si la macro G_HAVE_GINT64 está definida

Como puede ver, algunos de los tipos como gint parecen no tener otro sentido en la vida que tener el prefijo 'g' (Son idénticos a los ofrecidos por C estandar). La razón de su existencia es la de hacer el código mas consistente y limpio. Aunque no sea un crimen no usar estos tipos, debería emplearlos para facilitar la portabilidad de su código. Algunos de los tipos como gboolean están sólo para mejorar la claridad del código y podría usar también int para hacer exactamente lo mismo, pero el anterior método indica claramente que se esta hablando de un valor que solo puede tomar los valores TRUE/FALSE (VERDADERO/FALSO).

Portabilidad y Funciones de Utilidad

Hay funciones que o bien no se comportan exactamente igual en todos los sistemas, o bien son inseguras o bien no existen en alguna plataforma (o en ninguna), así que GLib proporciona sus propias implementaciones o recubrimientos que tienen un comportamiento constante y que normalmente comprueban los argumentos.

Aquí tiene algunas de las funciones más útiles que encontrará en esta categoría. Tenga en cuenta que el encabezado "Prototipo" es a título informativo, ya que algunas de estas funciones pueden ser realmente macros.

Tabla 2. Algunas Funciones Portables de GLib

PrototipoDescripción
gchar * g_strdup (const gchar *)Devuelve la localización de una nueva cadena que es una copia del argumento, si el argumento es NULL, se devuelve NULL
gpointer g_malloc (int tam)Devuelve una nueva región de memoria de 'tam' bytes, si no se puede reservar la memoria solicitada, el programa abortará invocando g_error
void g_free (gpointer p)Libera la memoria apuntada por 'p', si 'p' es NULL no realiza ninguna operación
gint g_snprintf (gchar *cadena, gulong n, gchar const *formato, ...) Funciona simplemente como sprintf escribiendo los argumentos de acuerdo con 'formato' en 'cadena', sin embargo solo usará 'n' bytes de la 'cadena', así que truncará el resultado si éste necesitase más. Devuelve el numero de bytes que son en realidad escritos en 'cadena'.
void g_usleep (gulong cont)Suspende la ejecución durante al menos 'cont' microsegundos
gint g_strcasecmp (const gchar *c1, const gchar *c2)Compara la cadena 'c1' y 'c2' sin tener en cuenta las mayúsculas
gint g_strncasecmp (const gchar *c1, const gchar *c2, guint n)Compara los 'n' primeros caracteres de la cadena 'c1' y 'c2' sin tener en cuenta las mayúsculas

También hay algunas funciones de utilidad y macros que realmente no se encuentran en las bibliotecas normales de C. Aquí hay una pequeña lista de algunas de las más útiles e importantes.

Tabla 3. Algunas Funciones Útiles de GLib

PrototipoDescripción
g_new (tipo, cont)Una macro que asignará memoria nueva para 'cont' elementos del tipo 'tipo' y devuelve el resultado como 'tipo'. Es equivalente a '(tipo) g_malloc(cont * sizeof(tipo))'
g_new0 (tipo,cont)La misma semántica que g_new, excepto que la memoria devuelta estará a cero. Tenga en cuenta que no se debería asumir que poner la memoria a cero pondrá a cero los números reales (Del tipo 'float')
gchar * g_strconcat (const gchar *cad, ...)Cuando se le pasa cualquier numero de argumentos del tipo (const char *) y un NULL despues del último argumento, devolverá una nueva cadena que proviene de la concatenación de todos los argumentos.
gchar * g_strdup_printf (const gchar *formato, ...)Una función como printf que devolverá una nueva cadena con el resultado de la operación printf
gchar * g_strstrip (gchar *cadena)Eliminará los espacios en blanco del principio y final de una cadena. No reserva memoria nueva, pero modifica la cadena original y devuelve un puntero a ella. Si desea reservar memoria nueva use una construcción como esta: 'cadena2 = g_strstrip(g_strdup(cadena1));'

Hay otros utilísimos métodos en GLib, y le insto a estudiar la documentación de GLib y el archivo de cabecera de GLib (glib.h): si lo hace ahorrará gran cantidad de tiempo no reimplementando funcionalidades básicas.

Contenedores

Probablemente la mejor parte de GLib sean sus contenedores. Aquí tenemos una lista de los contenedores de GLib.

Tabla 4. Contenedores de GLib Comunes

NombreDescripción
GListLista doblemente enlazada
GSListLista enlazada simple
GHashTableTabla de claves (N.T: hash)
GCacheCache
GTreeÁrbol binario balanceado
GNodeÁrbol n-simo
GStringCadena de tamaño dinámico
GArrayVector (N.T: Array) de tamaño dinámico
GPtrArrayVector de punteros de tamaño dinámico
GByteArrayVector de bytes (guint8) de tamaño dinámico

GList, Lista Doblemente Enlazada

La forma más fácil de usar listas o pilas en su código es con GList. La estructura básica de GList representa por un lado una lista doblemente enlazada y por otro un único nodo de la lista. Podrá añadir sus datos dentro del puntero de datos de GList (El miembro 'data' perteneciente a la estructura). Aquí tenemos una enumeración de las funciones que actúan sobre GList. Las funciones normalmente recogen un puntero y devuelven la lista modificada (Un puntero a ella). Tenga en cuenta que el primer nodo puede ser ahora diferente.

Tabla 5. Funciones Más Importantes de GList

PrototipoDescripción
GList* g_list_append (GList *lista, gpointer datos)Añade al final de la lista un nuevo nodo que contiene el puntero 'datos'. Si 'lista' es NULL creará una lista nueva.
GList* g_list_prepend (GList *lista, gpointer datos)Añade en el inicio de la lista un nodo que contiene 'datos', si 'lista' es NULL creará una lista nueva.
GList* g_list_remove (GList *lista, gpointer datos)Elimina el nodo que contiene el parametro 'datos' de la lista. Tenga en cuenta que sólo se comparan punteros (Y no contenidos).
GList* g_list_find (GList *lista, gpointer datos)Encuentra el nodo de GList que contiene el parametro 'datos'. De nuevo, téngase en cuenta que se comparan únicamente los punteros.
GList* g_list_next (GList *lista)Una macro que devuelve un puntero al nodo posterior.
GList* g_list_previous (GList *list)Una macro que devuelve un puntero al nodo anterior.
void g_list_free(GList *list)Libera la lista entera. NO libera la memoria asignada a sus datos, esto debe hacerlo usted mismo antes antes de invocar a esta función.

Para acceder a los datos de un nodo particular de GList lea el miembro data en la estructura GList. Así, el código que crearía una lista doblemente enlazada de dos elementos con dos cadenas duplicadas, y después liberara esa lista y las cadenas, quedaría así:

GList *list = NULL; /*el actual puntero a la lista*/
GList *li; /*simplemente un puntero temporal a un nodo usado para interaccionar
             con la lista*/
...
/*aquí añadimos dos cadenas a la lista*/
list = g_list_append(list,g_strdup("Cadena 1"));
list = g_list_append(list,g_strdup("Cadena 2"));
...
/*aquí nos movemos por la lista, liberando todas las cadenas y al final 
/* liberamos la lista en sí*/
for(li = list; li!= NULL; li = g_list_next(li)) {
        char *string = li->data;
        g_free(string);
}
g_list_free(list);

GString, Cadenas de Tamaño Dinámico

Otro contenedor simple de usar y muy útil es el contenedor GString. Es un contenedor para cadenas de tamaño dinámico, ideales en las ocasiones en las que no sabe que longitud tendrá la cadena que va a necesitar. Aquí tenemos una lista de las funciones más importantes.

Tabla 6. Funciones Más Importantes de GString

PrototipoDescripción
GString* g_string_new (const gchar *cad)Crea una GString nueva con el valor 'cad'
void g_string_free (GString *cadena, int borra_contenido)Libera la estructura GString y opcionalmente también el segmento de datos de la cadena
GString* g_string_append(GString *cadena, const gchar *val)Añade al final 'val' a 'cadena'
GString* g_string_prepend (GString *cadena, const gchar *val)Añade al principio 'val' a 'cadena'
void g_string_sprintf (GString *cadena, const gchar *formato, ...)Una función como sprintf para GString
void g_string_sprintfa (GString *cadena, const gchar *formato, ...)Una función como sprintf para GString, pero añade la cadena en lugar de sobreescribirla

Para acceder a los datos de la cadena para usarlos como un char *, simplemente acceda al elemento str de la estructura GString. En realidad es posible liberar la estructura GString sin liberar el segmento de datos. Esto es útil si quiere crear una cadena en C normal. El siguiente ejemplo es una función que coge un vector de enteros y los escribe dentro de una cadena y devuelve un char *.

char *
create_number_list(int array[], int array_len)
{
        int i;           /* el contador del vector */
        GString *string; /* la variable GString */
        char *ret;       /* al valor de retorno */

        /* crear una nueva GString vacía */
        string = g_string_new("");

        /* recorrer el vector de enteros */
        for(i=0; i<array_len; i++) {
                /* añadir el número a la cadena en paréntesis */
                g_string_sprintfa(string, "(%d)", array[i]);
        }

        /* poner el valor de retorno */
        ret = string->str;

        /* liberar la estructura GString, pero no los datos */
        g_string_free(string,FALSE);

        /* devolver la cadena */
        return ret;
}

GHashTable

Aunque usado con menor frecuencia que GList y GString, el contenedor para tablas de claves es muy útil también. Una tabla de claves se podría entender como un conjunto de objetos (en GLib el término objeto se refiere a un gpointer) y sendas llaves, estás últimas nos permitirán acceder a cada objeto posteriormente. GLib lleva este hecho más allá, haciendo que la llave sea un gpointer también, y dandonos la posibilidad de que le proporcionemos una función de comparación y de acceso (N.T: hashing). Aunque esto hace a GHashTable mucho más flexible, puede llevarnos a alguna confusión con respecto a la asignación de la memoria para las llaves. Vamos a enunciar algunas funciones importantes y nos ocuparemos de los detalles posteriormente:

Tabla 7. Funciones Más Importantes de GHashTable

PrototipoDescripción
GHashTable* g_hash_table_new (GHashFunc func_hash, GCompareFunc func_clave_cmp)Crea una tabla de claves nueva usando la función de generación de claves y la de comparación especificadas
void g_hash_table_destroy (GHashTable *tabla_hash)Destruye la tabla de claves y libera la memoria. Esto no implica que libere ningún dato o las claves, esto lo tendrá que hacer usted mismo
void g_hash_table_insert (GHashTable *tabla_hash, gpointer clave, gpointer valor)Introduce un nuevo 'valor' con una clave 'clave'
void g_hash_table_remove (GHashTable *tabla_hash, gconstpointer clave)Elimina el valor con la clave 'clave' de la tabla. No libera ni el valor ni la clave.
gpointer g_hash_table_lookup (GHashTable *tabla_hash, gconstpointer clave)Busca el valor del puntero con clave 'clave'. Devuelve NULL si no lo encuentra
gboolean g_hash_table_lookup_extended (GHashTable *tabla_hash, gconstpointer lookup_key, gpointer *orig_key, gpointer *value)Consulta si existe el dato con clave 'clave_valor', almacena la clave del puntero original en 'clave_orig' y el valor en 'valor'. Devuelve TRUE si la consulta tuvo éxito, FALSE en caso contrario. Debe usar esta función al eliminar un elemento de la memoria para deshacerse de la clave original.
void g_hash_table_foreach (GHashTable *tabla_hash, GHFunc func, gpointer datos)Ejecuta 'func' sobre cada dato almacenado en la tabla de claves. El parámetro 'datos' se le pasará a la función como último argumento. El prototipo de GHFunc es el siguiente.
void (*GHFunc) (gpointer clave, gpointer valor, gpointer datos)Esta es la función prototipo que usará con la función que se pasa a g_hash_table_foreach. Coge la 'clave', el 'valor' y los 'datos', especificado este último campo en la llamada a g_hash_table_foreach.
guint g_str_hash (gconstpointer v)Una función para generar claves a partir de cadenas
gint g_str_equal (gconstpointer v, gconstpointer v2)Una función de comparación de cadenas para tablas de claves
guint g_int_hash (gconstpointer v)Una función para generar claves a partir de enteros
gint g_int_equal (gconstpointer v, gconstpointer v2)Una función de comparación de enteros para tablas de claves

Para crear una tabla de claves, pase las funciones de acceso y de comparación de claves a g_hash_table_new. Hay funciones genéricas definidas para cadenas (g_str_hash y g_str_equal) y otras. Sin embargo si le pasa NULL como funciones de acceso y de comparación, obtendrá un puntero directo: los punteros serán en realidad utilizados como claves.

El problema de la reserva de memoria se vuelve aparente cuando empezamos a usar cadenas como claves.GHashTable no almacena la cadena, todo lo que almacena es un puntero. Por tanto, cuando insertamos un valor dentro de la tabla, tenemos que crear una copia nueva de la clave para ese valor (un otro caso, habrá elementos que compartan la clave). Esto es importante recordarlo pues sino las cosas no funcionarán como esperamos. El otro problema es como liberar entonces la clave. Si usa g_hash_table_remove, dará como clave una cadena con el mismo contenido que la clave original pero no en la misma zona de memoria (un puntero distinto). Entonces, el puntero a la clave original se habrá perdido y a menos que haya almacenado la dirección en algún sitio se habrá creado una laguna en la memoria. Lo que tiene que hacer en su lugar es usar g_hash_table_lookup_extended para obtener primero los punteros a la clave y al valor y luego hacer uso de g_hash_table_remove.

El siguiente ejemplo creará una nueva tabla de claves, insertará una pareja de cadenas, las recuperaremos, y entonces destruiremos la tabla y los valores almacenados en ella:

/* función que usaremos para liberar las claves y los datos en la tabla antes
de que la destruyamos */
static void
free_key_value(gpointer key, gpointer value, gpointer user_data)
{
        g_free(key);
        g_free(value);
}

...

/* en algún lugar del código   */

GHashTable *ht;

/* crea una nueva tabla de claves cadenas como claves*/
ht = g_hash_table_new(g_str_hash, g_str_equal);

/* introduce una pareja de cadenas (es decir, colores marcados por la forma) */
g_hash_table_insert(ht, g_strdup("triangulo"), g_strdup("verde"));
g_hash_table_insert(ht, g_strdup("cuadrado"), g_strdup("rojo"));
g_hash_table_insert(ht, g_strdup("círculo"), g_strdup("azul"));

/* de nuevo, en algún lugar del código */
...
/* ahora aquí deseamos imprimir el color de un cuadrado */
char *color;

/* coger el color del cuadrado*/
color = g_hash_table_lookup(ht, "cuadrado");

printf("El color del cuadrado es: %s\n",color);

/* de nuevo en algún lugar del código */
...
/* Ahora simplemente queremos destruir la tabla de claves y liberar toda la
 * memoria asociada a ella. Usaremos la función free_key_value y la usaremos
 * sobre todos los valores de la tabla. */
g_hash_foreach(ht, free_key_value, NULL);

/* ahora podemos destruir la tabla de claves actual */
g_hash_table_destroy(ht);

Más Información Sobre GLib

Para mas información mirad el archivo de cabecera glib.h y la documentación el sitio web www.gtk.org.