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).
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
Nombre | Equivalente | Descripció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) | |
gchar | char | Carácter |
guchar | unsigned char | Carácter sin signo |
gshort | short | Entero corto |
gushort | unsigned short | Entero corto sin signo |
glong | long | Entero largo |
gulong | unsigned long | Entero largo sin signo |
gint | int | Entero |
guint | unsigned int | Entero sin signo |
gfloat | float | Número real |
gdouble | double | Número real de doble precisión |
gboolean | int | Tipo para almacenar valores VERDADERO/FALSO |
gpointer | void * | Tipo para almacenar punteros a distintos objetos |
gconstpointer | const 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).
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
Prototipo | Descripció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
Prototipo | Descripció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.
Probablemente la mejor parte de GLib sean sus contenedores. Aquí tenemos una lista de los contenedores de GLib.
Tabla 4. Contenedores de GLib Comunes
Nombre | Descripción |
---|---|
GList | Lista doblemente enlazada |
GSList | Lista enlazada simple |
GHashTable | Tabla de claves (N.T: hash) |
GCache | Cache |
GTree | Árbol binario balanceado |
GNode | Árbol n-simo |
GString | Cadena de tamaño dinámico |
GArray | Vector (N.T: Array) de tamaño dinámico |
GPtrArray | Vector de punteros de tamaño dinámico |
GByteArray | Vector de bytes (guint8) de tamaño dinámico |
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
Prototipo | Descripció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); |
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
Prototipo | Descripció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; } |
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
Prototipo | Descripció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); |
Para mas información mirad el archivo de cabecera glib.h y la documentación el sitio web www.gtk.org.