GLib es una librería de funciones y definiciones útiles disponibles para usarse cuando se crean aplicaciones GDK y GTK. Provee reemplazos para algunas funciones estándares de libc, como malloc, las cuales tienen problemas en algunos sistemas.
También provee rutinas para manejar:
El objeto GList se define como:
typedef struct _GList GList; struct _GList { gpointer data; GList *next; GList *prev; }; |
Para usar los objetos GList, simplemente:
GList *list = NULL; GList *listrunner; gint array[] = { 1, 2, 3, 4, 5, 6 }; gint pos; gint *value; /* agregar datos a la lista */ for (pos=0;pos < sizeof array; pos++) { list = g_list_append(list, (gpointer)&array[pos]); } /* atravesar la lista */ listrunner = g_list_first(list); while (listrunner) { value = (gint *)listrunner->data; printf("%d\n", *value); listrunner = g_list_next(listrunner); } /* eliminando datos de la lista */ listrunner = g_list_first(list); list = g_list_remove_link(list, listrunner); list = g_list_remove(list, &array[4]); |
El mismo código se puede utilizar con listas simples (objetos GSList) reemplazando las funciones g_list_* con las g_slist_* relevantes (g_slist_append, g_slist_remove, ...). Simplemente recuerda que ya que no puedes retroceder en una lista simplemente enlazada, no hay función g_slist_first - necesitaras mantener una referencia al primer nodo de la lista.
GLib trata de ser "inteligente" en este asunto especial: asume que seguramente reutilizaras los objetos, así que guarda la memoria asignada en un cache. Si no quieres utilizar este comportamiento, probablemente querrás crear un colocador especial.
Citando a Tim Janik:
"Si tienes cierta porción de código que usa *muchos* GLists o GNodes, y sabes que seria mejor liberarlos todos después de un rato, querrás utilizar un GAllocator. Empujar un colocador a un g_list hará privado el espacio de memoria de ese colocador para todas las operaciones glist subsecuentes (y por lo tanto tienes que tener cuidado de sacar el colocador otra vez, antes de hacer llamadas externas):"
GAllocator *allocator; GList *list = NULL; guint i; /* asignar un nuevo espacio de colocación para nodos GLis */ allocator = g_allocator_new ("list heap", 1024); g_list_push_allocator (allocator); /* hacer operaciones de lista */ for (i = 0; i < 4096; i++) list = g_list_prepend (list, NULL); list = g_list_reverse (list); /* ten cuidado de sacar el colocador antes de llamar funciones externas */ g_list_pop_allocator (); gtk_label_set_text (GTK_LABEL (some_label), "some text"); /* y asigna nuestro espacio privado de glist otra vez */ g_list_push_allocator (allocator); /* hacer operaciones de lista */ g_list_free (list); list = NULL; for (i = 0; i < 4096; i++) list = g_list_prepend (list, NULL); /* y sal otra vez (al mismo tiempo que liberas todos los nodos) */ g_list_pop_allocator (); g_allocator_free (allocator); |
Gracias a Tim Janik que escribió a gtk-list: (un poco modificado)
"Con respecto a g_malloc(), g_free() y hermanos, estas funciones son más segura que sus equivalentes libc. Por ejemplo, g_free() sólo regresa si se llama con NULL. También, si USE_DMALLOC se define, la definición para estas funciones cambia (en glib.h) para usar MALLOC(), FREE() etc... Si MEM_PROFILE o MEM_CHECK se definen, incluso tenemos pequeñas estadísticas que cuentan el tamaño de los bloque usados (mostrado por g_mem_profile() / g_mem_check())."
"Considerando el hecho de que glib provee una interface para que los pedazos de memoria salven espacio si tienes muchos bloques que siempre son del mismo tamaño y para marcarlos ALLOC_ONLY si es necesario, es fácil crear un pequeño envoltorio ahorrador alrededor de las cosas malloc/free normales - tal como gdk cubre Xlib. ;)"
"Usando g_error() y g_warning() dentro de las aplicaciones como GIMP que se apoyan por completo en gtk, incluso da la oportunidad de hacer aparecer una ventana mostrando los mensajes dentro de una ventana gtk con tu propio manejador (usando g_set_error_handler()) al estilo de gtk_print() (dentro de gtkmain.c)."
Un GScanner convertirá en símbolos (tokens) tu texto, lo que significa, que regresará un entero para cada palabra o número que aparece en su flujo de entrada, siguiendo ciertas reglas (adaptables) para realizar esta traducción. Todavía necesitas escribir las funciones de análisis sintáctico por tu propia cuenta.
Aquí hay un pequeño programa de prueba provisto por Tim Janik que hará análisis sintáctico
<SYMBOL> = <OPTIONAL-MINUS> <NUMBER> ;
constructs, while skipping "#\n" and "/**/" style comments.
#include <glib.h> /* un poco de texto de prueba para alimentar el rastreador */ static const gchar *test_text = ( "ping = 5;\n" "/* slide in some \n" " * comments, just for the\n" " * fun of it \n" " */\n" "pong = -6; \n" "\n" "# the next value is a float\n" "zonk = 0.7;\n" "# redefine ping\n" "ping = - 0.5;\n" ); /* definir valores de enumeración a ser regresados por símbolos específicos */ enum { SYMBOL_PING = G_TOKEN_LAST + 1, SYMBOL_PONG = G_TOKEN_LAST + 2, SYMBOL_ZONK = G_TOKEN_LAST + 3 }; /* cadena de símbolos */ static const struct { gchar *symbol_name; guint symbol_token; } symbols[] = { { "ping", SYMBOL_PING, }, { "pong", SYMBOL_PONG, }, { "zonk", SYMBOL_ZONK, }, { NULL, 0, }, }, *symbol_p = symbols; static gfloat ping = 0; static gfloat pong = 0; static gfloat zonk = 0; static guint parse_symbol (GScanner *scanner) { guint symbol; gboolean negate = FALSE; /* esperar un símbolo válido */ g_scanner_get_next_token (scanner); symbol = scanner->token; if (symbol < SYMBOL_PING || symbol > SYMBOL_ZONK) return G_TOKEN_SYMBOL; /* esperar '=' */ g_scanner_get_next_token (scanner); if (scanner->token != '=') return '='; /* característica opcional '-' */ g_scanner_peek_next_token (scanner); if (scanner->next_token == '-') { g_scanner_get_next_token (scanner); negate = !negate; } /* esperar un float (los enteros son convertidos a float inmediatamente) */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_FLOAT) return G_TOKEN_FLOAT; /* asegúrate que el siguiente token es un ';' */ if (g_scanner_peek_next_token (scanner) != ';') { /* no es así, come el no-punto-y-coma y manda error */ g_scanner_get_next_token (scanner); return ';'; } /* asignar valores, comer el punto y coma y salir exitosamente */ switch (symbol) { case SYMBOL_PING: ping = negate ? - scanner->value.v_float : scanner->value.v_float; break; case SYMBOL_PONG: pong = negate ? - scanner->value.v_float : scanner->value.v_float; break; case SYMBOL_ZONK: zonk = negate ? - scanner->value.v_float : scanner->value.v_float; break; } g_scanner_get_next_token (scanner); return G_TOKEN_NONE; } int main (int argc, char *argv[]) { GScanner *scanner; guint expected_token; scanner = g_scanner_new (NULL); /* ajustar el comportamiento léxico para nuestra necesidades */ /* convertir no-float (valores octale, hexadecimales ...) a G_TOKEN_INT */ scanner->config->numbers_2_int = TRUE; /* convertir G_TOKEN_INT a G_TOKEN_FLOAT */ scanner->config->int_2_float = TRUE; /* no regresar G_TOKEN_SYMBOL, sino el valor del símbolo */ scanner->config->symbol_2_token = TRUE; /* cargar los símbolos al rastreador */ while (symbol_p->symbol_name) { g_scanner_add_symbol (scanner, symbol_p->symbol_name, GINT_TO_POINTER (symbol_p->symbol_token)); symbol_p++; } /* alimentar el texto */ g_scanner_input_text (scanner, test_text, strlen (test_text)); /* dar al manejador de errores una idea de cómo se nombra la entrada */ scanner->input_name = "test text"; /* ciclo de rastreo, analisamos la entrada hasta alcanzar el final, * encontrar un error por el rastrador, o nuestra subrutina encuentre * sintaxis inválida */ do { expected_token = parse_symbol (scanner); g_scanner_peek_next_token (scanner); } while (expected_token == G_TOKEN_NONE && scanner->next_token != G_TOKEN_EOF && scanner->next_token != G_TOKEN_ERROR); /* dar un mensaje de error en los errores de sintaxis */ if (expected_token != G_TOKEN_NONE) g_scanner_unexp_token (scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE); /* finalizar el análisis */ g_scanner_destroy (scanner); /* imprimir resultados */ g_print ("ping: %f\n", ping); g_print ("pong: %f\n", pong); g_print ("zonk: %f\n", zonk); return 0; } |
Necesitas entender que el rastreador analizará su entrada y la convertirá en símbolos, te toca a ti interpretar estos símbolos, y no definir su tipo antes de que sean analizados, ej. observa gscanner analizar una cadena:
"hi i am 17"
| | | |
| | | v
| | v TOKEN_INT, value: 17
| v TOKEN_IDENTIFIER, value: "am"
v TOKEN_CHAR, value: 'i'
TOKEN_IDENTIFIER, value: "hi"
Si configuras el rastreador con:
scanner->config->int_2_float = TRUE; scanner->config->char_2_token = TRUE; scanner->config->scan_symbols = TRUE; |
y agregas "am" como símbolo con
g_scanner_add_symbol (scanner, "am", "symbol value"); |
GScanner lo analizará como
"hi i am 17"
| | | |
| | | v
| | v TOKEN_FLOAT, value: 17.0 (conversión automática int->float)
| | TOKEN_SYMBOL, value: "symbol value" (una búsqueda exitosa en la tabla segmentada
| | convirtió un TOKEN_IDENTIFIER a un
| | TOKEN_SYMBOL y se apoderó de
| v el valor del símbolo)
v 'i' ('i' también puede ser un símbolo válido, como todos los caracteres >0 y <256)
TOKEN_IDENTIFIER, value: "hi"
Necesitas emparejar la secuencia de token con tu código, y si encuentras algo que no quieres, manda un error:
/* espera un identificador("hi") */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_IDENTIFIER) return G_TOKEN_IDENTIFIER; /* espera un token 'i' */ g_scanner_get_next_token (scanner); if (scanner->token != 'i') return 'i'; /* espera un símbolo ("am") */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_SYMBOL) return G_TOKEN_SYMBOL; /* espera un float (17.0) */ g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_FLOAT) return G_TOKEN_FLOAT; |
Si pasaste de ahí, has analizado "hi i am 17" y también podrías haber aceptado "dook i am 42" y " bah i am 0.75", pero no habrías aceptado "hi 7 am 17" o "hi i hi 17".