Programación en C/Glib

De Wikilibros, la colección de libros de texto de contenido libre.

Introducción

El lenguaje C fue creado como un lenguaje multiplataforma capaz de sustituir al ensamblador y generar código portable. Sin embargo a lo largo de los años se empiezan a hacer visibles algunas lagunas del diseño original. Algunos ejemplos que pueden citarse:

  • No existe estandarización en torno a la longitud real (en bits) de un entero. Un tipo int puede variar entre 16, 32 y 64 bits de longitud en función del sistema operativo y compilador utilizado.
  • No existe un soporte estándar en el lenguaje para programación orientada a objetos.
  • No existe un soporte estándar para tipos de estructuras de datos frecuentemente utilizadas como listas enlazadas, tablas hash (también llamadas diccionarios).
  • No existe un soporte estándar para textos Unicode (el estándar Unicode es posterior a la creación del C).
  • No existe un soporte estándar para patrones de diseño frecuentemente utilizados como el "bucle principal de eventos".
  • No existe un API común para manejar aplicaciones multihilo como puede existir en Java y otros lenguajes/plataformas.

La librería glib se creó con el fín de solucionar estas y otras lagunas originales del lenguaje proporcionando una capa de compatibilidad real multiplataforma en sistemas tipo UNIX (Linux, Solaris, AIX, BSD, ...), Windows, OS/2 y BeOS.

La librería glib es gratuita y licenciada bajo LGPL (Lesser GPL), lo cual significa en la práctica que puede ser utilizada tanto en programas de código abierto como cerrado.

glib provee soporte para expresiones regulares tipo PERL, un conjunto de datos tipados más seguros que el C estándar, soporte multihilo multiplataforma, un sistema de "bucle principal de eventos", colas asíncronas, carga dinámica de módulos, soporte portable para uso de ficheros, tuberías y sockets, programación orientada a objetos, utilidades de todo tipo para manipular textos, escaneado sintáctico, "Timers" y multitud de estructuras de datos frecuentemente utilizadas "slices" de memoria, listas doblemente enlazadas, colas, secuencias (listas escalables), tablas hash para búsqueda mediante llaves (donde la llave puede ser un texto o un objeto complejo), arrays dinámicos, árboles binarios balanceados, árboles "N-ários" (árboles con N ramas por nodo), quarks, listas de datos indexadas (accesibles mediante un identificador GQuark, relaciones y tuplas que pueden ser indexadas mediante un número arbitrario de campos, caches para compartición de estructuras complejas de datos.

glib provee además un marco base de utilidades para realizar tests de funcionamiento en tiempo de desarrollo.

glib no provee soporte gráfico. Frecuentemente suele emparejarse con la librería gráfica GTK+ ya que la misma utiliza glib internamente como soporte base. Sin embargo glib puede ser utilizado para aplicaciones embebidas o servidor sin dependencia alguna del sistema gráfico.

Ejemplos de uso

glib se encuentra ampliamente documentado por los propios desarrolladores de la librería y existen tutoriales disponibles en Internet. Aquí se expondrá un ejemplo de código real dónde se utiliza glib para facilitar el desarrollo en C:

Este código se utilizó para monitorizar un sistema Linux donde el disco duro había sido sustituido por un sistema de ficheros en red. El código se encarga de crear un "perro vigia" encargado de resetear el sistema en caso de detectar un fallo en el sistema de ficheros NFS (Net File System) del cual depende la máquina para su correcto funcionamiento. El mismo sirve para comprobar la facilidad con que glib permite crear timers, callbacks invocadas por el bucle principal de eventos o gestionar acceso de entrada/salida de forma más ordenada que las librerías estándar de C:

#include <glib.h> /* glib */
GError* err = NULL;

gboolean isNFSUpAndRunning() {
    /* Salvo que se indique lo contrario devuelve "KO"(0)
     * Superados todos los test devolver'a "OK" (1)
     */
    gboolean result = 0;
    GIOChannel *gioChan = g_io_channel_new_file("/NFSFlag", "r", &err);
                                                                             /*|*/
                                                                             /*|*/startBlock1: {
                                                                             /*|*/
    if(NULL != err && g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
      goto endBlock1;
    }
    g_io_channel_set_encoding(gioChan, NULL, NULL);
    gsize *len = 0;
    char* ret;
    if(G_IO_STATUS_ERROR == g_io_channel_read_to_end(gioChan, &ret, len, &err)) {
      goto endBlock1;
    }
    printf("%s", ret);
    result = 1; // Todo es correcto. Devolvemos "OK"
                                                                             /*|*/
                                                                             /*|*/} endBlock1: {
                                                                             /*|*/
    if(NULL != err)      { g_error_free(err); err = NULL; }
    if(NULL != gioChan)  g_io_channel_unref(gioChan);
                                                                             /*|*/
                                                                             /*|*/}
                                                                             /*|*/
    return result;
}

void hardReset() {
    // http://en.wikipedia.org/wiki/Magic_SysRq_key
    GIOChannel *gioChan = g_io_channel_new_file("/proc/sysrq-trigger", "w", &err);
    gsize bytes_written;
    g_io_channel_write_chars(gioChan, "b", -1, &bytes_written, &err);
    if (NULL != err) {
        printf("debug:hardReset Se detect'o un error\n"); fflush(stdout);
    }
    g_io_channel_shutdown (gioChan, /*bFlush*/ TRUE, &err);
}

static gboolean callBackCheckNFS(gpointer user_data){
    if (! isNFSUpAndRunning() ){
        hardReset();
    }
    return TRUE;
}

static gboolean setTimeOut(GMainContext* ctx) {
    g_return_if_fail (ctx != NULL);
    // Creamos un objeto timer. source indica que es la fuente de eventos
    GSource* source = g_timeout_source_new (60*1000 /*milisecs*/);
    // Asociamos un callBack a la fuente de eventos (timer).
    g_source_set_callback (source, callBackCheckNFS, (gpointer) ctx, NULL);
    // Finalmente asociamos la fuente al contexto (bucle principal de eventos)
    g_source_attach (source, ctx /*ctx->g_main_ctx*/);
    g_source_unref (source);
    return TRUE;
}

int main(int argc, char *argv[]) {
    // Creamos el objeto bucle principal de eventos.
    GMainLoop *loop = g_main_loop_new (NULL, FALSE);

    /*
     * Asociamos un Timer al bucle principal. Si falla algo abortamos el programa
     * pues no tiene sentido continuar en tal caso.
     */
    if (! setTimeOut(g_main_loop_get_context(loop)) ) {
        return 1;
    }
    // Ejecutamos el bucle principal de eventos.
    g_main_run (loop);
    return 0;
}

Referencias

  • El Interfaz de Programación de Aplicaciones (API) puede consultarse (en inglés) en la siguiente URL:

http://library.gnome.org/devel/glib/stable/

  • El tutorial de la librería gráfica GTK+ (http://library.gnome.org/devel/gtk-tutorial/stable/) incluye también ejemplos de glib y herramientas para compilar y linkar contra estas dos librerías.
  • El exitoso escritorio para UNIX de código abierto GNOME (GNU Network Model Environment) así como la plataforma Moblin de Intel, Nokia Maemo, Google Android y Google Chrome, la versión UNIX de Firefox y otros programas de código abierto como "The Gimp", Inkscape o DIA, etc ... hacen un uso extenso de glib. El código está disponible gratuitamente en la web y puede servir como una guía y referencia para utilizar glib en proyectos de software complejos con millones de líneas de código y que son utilizados diariamente por millones de personas.