Programación en C/Glib

IntroducciónEditar

C fue creado como un lenguaje multiplataforma capaz de sustituir al ensamblador y generar código portátil. 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 admite de manera estándar la programación orientada a objetos.
  • No admite de manera estándar los tipos de estructuras de datos frecuentemente utilizadas tales como las listas enlazadas y las tablas hash (también llamadas diccionarios).
  • No permite de manera estándar la utilización de textos Unicode (la norma Unicode es posterior a la creación del C).
  • No admite de manera estándar los patrones de diseño frecuentemente utilizados como el "bucle principal de eventos".
  • No existe una API común para manejar aplicaciones multihilo como puede existir en Java y otros lenguajes/plataformas.

La biblioteca 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 biblioteca glib es gratuita y está disponible en virtud de la licencia 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 compatibilidad 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 usoEditar

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;
}

ReferenciasEditar

  • 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.