Hola, buenas tardes! Un domingo más comenzamos con las charlas aquí en GNOME Hispano. La charla de hoy la dará German Poo y tratará sobre GLib Intermedio. En caso de loguear la charla no olviden poner en modo conferencia su cliente de irc y para cualquier pregunta pueden remitirmela a mi por privado. Sin más, germán, adelante. gracias alex En esta charla veremos: - Gestión de memoria dinámica - Gestión de archivos - Uso de sockets - Estructuras de datos por tiempo, y dada la naturaleza de la charla anterior puede ser que estructuras de datos no podamos verlo --- gnomero fija modos [#gnome-hispano +o kiwnix] La charla pasada trató de "glib básico", en donde Roberto Majadas (telemaco) abordó los temas de tipos de datos, mensajes de salida y manejo de cadenas. icha charla la pueden leer en línea, desde el sitio de --- kiwnix fija modos [#gnome-hispano -o kiwnix] GNOME Hispano, cuya URL es: http://www.es.gnome.org/eventos/charlairc-20021208-glib_basico.php Antes de comenzar, daré algunas reseñas que nos sitúen dentro del contexto de las charlas. Cuando decimos que enseñaremos a programar con Glib, GTK+, GNOME, etc. nos referimos a crear programas usando las funciones que nos permitan disponer de una aplicación (principalmente gráfica) con integración con el escritorio GNOME. Recordemos que glib es una biblioteca que nos provee funciones y tipos de datos que nos permiten mejorar la portabilidad de nuestras aplicaciones y simplificar la programación. Podríamos verlo como una extensión a las funciones clásicas de C, mucho mas ricas y fáciles de emplear. Por lo tanto, se debe disponer de las bibliotecas de desarrollo de glib, en debian el paquete se llama libglib2.0-dev en RedHat se llama glib2-devel. No es intención de la charla enseñar como se instalan. Los programas que aquí veremos podremos compilarlos como: $ gcc -g -Wall `pkg-config --cflags --libs glib-2.0` -o programa programa.c Donde "programa.c" es el nombre nuestro código fuente y "programa" es el nombre que tendrá nuestra aplicación una vez compilada. Para verificar que nuestro programa este sintácticamente bien escrito, indicamos al compilador que nos avise de todo lo que parezca raro y que podría repercutir en un error en la ejecución, es es decir -Wall que significa "Warging all". Por último, y nos menos importante, lo que veremos sólo es un complemento del Libro GNOME, con el cual pueden guiarse para crear sus propias aplicaciones. La URL es: http://libros.es.gnome.org/librognome/librognome/librognome/book1.html estamos listos para comenzar Gestión de memoria dinámica Las funciones principales de gestión de memoria son g_malloc, g_malloc0 y g_free. g_malloc permite solicitar un espacio de memoria al sistema operativo, tìpicamente lo empleamos cuando manejamos alguna puntero a estructura, punteros a char, etc. Es decir, nos retorna un puntero a una zona de memoria reservada. Es muy importante liberar (devolver al sistema operativo) toda la memoria que solicitamos con g_malloc. De lo contrario nuestra aplicación tendrá "memory leaks", es decir, comenzará a agotar la memoria y nuestro sistema se puede volver más lento (será más notorio si nuestra aplicación es grande). Entonces, para liberar la memoria reservada usaremos g_free. g_malloc y g_free son los equivalentes en C de malloc y free, pero completamente portables. y que seguramente, más de algún dolor de cabeza les habrá reportado alguna vez El formato de uso es: gpointer g_malloc (gulong size); gpointer g_malloc0 (gulong size); void g_free (gpointer mem); La diferencia entre g_malloc y g_malloc0, es que esta última además de reserva "size" bytes de memoria, los inicializa en 0, que es lo más probable que deseemos. Ejemplo: #include int main (int argc, char **argv) { gchar *text; gint len = strlen ("Hola mundo") + 1; text = g_malloc (len); g_snprintf (text, len, "Hola mundo"); _print ("%s\n", text); g_free (text); return 0; } veo un error, pase a escribir _print en vez de g_print por cada g_malloc que hagamos, debemos proveer un g_free para liberar la memoria Además, existen g_new y gnew0, que son meras interfaces de g_malloc y g_malloc0 respectivamente, y que nos permiten reservar "n" veces el tamaño de una estructura. Así por ejemplo, podemos tener: GString *str; str = g_new0 (GString, 5); [...] g_free (str); donde [...] es algún código nuestro que procesa str de alguna manera en este ejemplo, g_new0 es equivalente a: str = g_malloc0 (sizeof (GString) * 5) pero más fácil de leer y más cercano a orientación a objetos. Tambien existen las funciones g_realloc, g_realloc0, g_renew y g_renew0, que son para reubicar memoria y retornan un puntero a un nuevo espacio de memoria, su equivalente es realloc en C estándar. No podemos terminar de ver gestión de memoria dinámica sin gpoo: Una pregunta. ok me llevo preguntando desde la anterior charlar de toma de contacto con Glib si el utilizar, por despiste, pongamos por ejemplo, malloc en vez de g_malloc influirá en algo al programa? puede llevar a error (exceptuando el "error de llevar una consistencia en el propio código"... no se si me he explicado bien... veamos tu preguntas: que pasa si uso malloc en vez de g_malloc? ocurrirá algún error? así es como lo entiendo pues bien es muy probable que no suceda nada no mientras trabajes en la misma plataforma g_malloc está hecha para ser tu aplicación portable es decir, que se compile sin problemas en Linux, Solaris, HP/UX, etc. hasta Windows por que dice que g_malloc es como malloc pero completamente portable? malloc ya es completamente portable. fxn: si lo pensamos en términos de POSIX, sí pero acá va la otra diferencia, que no alcancé a mencionar en la primera respuesta y es que, g_malloc nos avisa si existen problemas si bien es cierto, retornará NULL es bien educado y en caso de error dirá: g_error ("%s: failed to allocate %lu bytes", G_STRLOC, n_bytes); es decir, detendrá la ejecución de nuestro programa y podremos determinar de mejor manera donde está el problema de la forma usual en C, que sucede? que la aplicación sigue su curso y luego se comporta de una manera errática o bien se cae en un lugar completamente distinto y podemos pasar horas y horas tratando de seguirle la pista sin mucho éxito no sé si esto aclara la película en ANSI C uno ha de mirar el tipo de retorno o picar un wrapper que lo haga por ti queria decir que hay que mirar si el puntero devuelto es NULL o no ok pero no siempre evitarás los descuidos y es mucho más robusto cuando las funciones te ayudan en ello además con las funciones g_malloc es posible obtener estadísticas de uso de memoria si es que se define MEM_PROFILE o MEM_CHECK que lo veremos un poco más adelante por otro lado glib provee una interfaz que permite ahorros de memoeria si tienes muchos bloques de siempre son del mismo tamaño y los marca como ALLOC_ONLY si fuera necesario facilitando la depuración en resumen son mucho mas seguras te garantizan el mismo funcionamiento de tu aplicacion en todas las plataformas te proveen facilidades como programador puedes realizar profiling que se pueden crear wrappers sobre malloc/free, es cierto pero con glib, eso ya no se hace necesario y es estándar en GNOME más preguntas? no, por el momento ninguna. veamos... decía: No podemos terminar de ver gestión de memoria sin obtener un perfil del uso de memoria de nuestro programa. Ya que hemos indicado que es importante adminstrar bien la memoria para evitar "memory leaks". Para obtener un perfil, emplearemos la función: void g_mem_profile (void); que en una de las respuestas ya mencioné La cual nos entregará un resumen del uso de memoria. Sin embargo, para que funcione como se espera, debe inicializarse una tabla de perfiles de memoria, que, afortunadamente, glib nos provee y es una variable: extern GMemVTable *glib_mem_profiler_table; que es una estructura cuyo contenido en estos momentos no nos importa mayormente mas que su finalidad esta variable la debemos usar en conjunto con g_mem_set_vtable(). Así, nuestro programa quedará de la siguiente forma: #include int main (int argc, char **argv) { gchar *text; gint len = strlen ("Hola mundo") + 1; g_mem_set_vtable (glib_mem_profiler_table); text = g_malloc (len); g_snprintf (text, len, "Hola mundo"); g_print ("%s\n", text); g_free (text); g_mem_profile (); return 0; } Antes de comenzar con las asignaciones de memoria, hemos invocado a "g_mem_set_vtable" y le hemos dicho que queremos preparar el ambiente para obtener un perfil de la memoria empleada es muy importante al momento de usarla que esta funcion debe ser llamada antes que cualquier otra función de glib y antes de finalizar, le pedimos un resumen. co la función g_mem_profile Lo compilamos y si lo ejecutamos nos dará un resultado como el siguiente: Hola mundo GLib Memory statistics (successful operations): blocks of | allocated | freed | allocated | freed | n_bytes n_bytes | n_times by | n_times by | n_times by | n_times by | remaining | malloc() | free() | realloc() | realloc() | ===========|============|============|============|============|=========== 11 | 2 | 2 | 0 | 0 | +0 12 | 2 | 2 | 0 | 0 | +0 16 | 1 | 0 | 0 | 0 | +16 20 | 1 | 0 | 0 | 0 | +20 21 | 1 | 0 | 0 | 0 | +21 28 | 2 | 0 | 0 | 0 | +56 29 | 1 | 0 | 0 | 0 | +29 44 | 2 | 0 | 0 | 0 | +88 GLib Memory statistics (failing operations): --- none --- Total bytes: allocated=276, zero-initialized=20 (7.25%), freed=46 (16.67%), remaining=230 MemChunk bytes: allocated=3176, freed=0 (0.00%), remaining=3176 pues bien básicamente eso es lo que glib nos provee para la gestión dinámica de memoria alguna pregunta antes de comenzar a ver gestión de archivos? no, lo están comprendiendo todos muy bien. :-) perfecto :-) Gestión de archivos lib ofrece algunas funciones útiles para el manejo de archivos y además provee un mecanismo unificado de acceso a sockets, pipes y archivos, para (una vez más) facilitar la portabilidad de las aplicaciones hmmm... sigo con problemas con la 'g'. Estamos hablando de Glib (no lib) GIOChannel es una capa de abstracción al cual podemos acceder con los mismo métodos a sockets, pipes, archivos y las funciones de GIOChannel comenzarán con el prefijo: g_io_channel_ así por ejemplo, si queremos abrir un archivo con GIOChannel creamos un canal con g_io_channel_new_file cuyo formato es: GIOChannel* g_io_channel_new_file (const gchar *filename, const gchar *mode, GError **error); donde el primero parámetro es el nombre del archivo a abrir el segunfo es el modo, que se emplean exactamente los mismos valores usados en fopen y el último parámetro si deseamos realizar gestión de errores es decir, cuando ocurra un error saber el detalle para tomar alguna acción o bien informar al usuario de esto último, gestión de errores, se referirá Rodrigo Moya en la siguiente charla así que por ahora, no empleremos ese parámetro, mas bien lo dejaremos en NULL si abrimos un archivo para lectura cremos un canal y luego podemos invocar alguna función como: g_io_channel_read_chars g_io_channel_read_line g_io_channel_read_line_string g_io_channel_read_to_end g_io_channel_read_unichar donde g_io_channel_read_chars lo podemos pensar como el clásico read () donde se indica una cantidad 'x' de bytes a leer sin embargo, cuando trabajamos documentos de texto, normalmente nos interesa leer línea por línea y allí nos sirven la 2da y 3ra función mencionada la diferencia entre g_io_channel_read_line y g_io_channel_read_line_string radica en que la primera usar un puntero a un gchar para devolver el valor es decir gchar * y en el segundo caso (g_io_channel_read_line_string) dejará el contenido en una variable de tipo GString que es lo que vio Roberto la semana pasada y es el manejo de Strings que nos provee glib y que no existe de esa forma en C estándar. antes de mostrar un ejemplo, les indicaré para que sirven las 2 últimas funciones g_io_channel_read_to_end lee hasta el final del archivo y la última g_io_channel_read_unichar nos permite leer en UTF8 partamos con un ejemplo sencillo: #include int main (int argc, char *argv[]) { GIOChannel *io; gchar *buffer; gsize len; if (argc != 2) { g_printerr ("Uso: giochannel archivo\n"); return -1; } io = g_io_channel_new_file (argv[1], "r", NULL); if (io != NULL) { while (g_io_channel_read_line (io, &buffer, &len, NULL, NULL) == G_IO_STATUS_NORMAL) { g_print ("%s", buffer); g_free (buffer); } g_io_channel_close (io); } else { g_printerr ("No se pudo abrir el archivo %s\n", argv[1]); } return 0; } Lo que haces esta aplicación es abrir un archivo cualquier e imprimir su contenido por pantalla es decir, si el programa lo compilamos como giochannel $ ./giochannel archivo.txt nos imprimirá el contenido del archivo analicemos paso a paso io = g_io_channel_new_file (argv[1], "r", NULL); creamos el canal, cuyo nombre de archivo lo ha dado el usuario y viene en argv[1] lo hemos abierto en modo lectura ("r") y le hemos indicado que no queremos una gestión de error mas fina eso no quiere decir que no podamos (y no debamos) verificar errores si la llamada es exitosa, nos devolvera un nuedo GIOChannel en io de lo contrario, retornará NULL y es lo que verificamos en: if (io != NULL) { es decir, si el archivo lo podemos leer, entonces... le pedimos que lea línea a línea el formato de g_io_channel_read_line es: GIOStatus g_io_channel_read_line (GIOChannel *channel, gchar **str_return, gsize *length, gsize *terminator_pos, GError **error); donde nosotros hemos ocupado solo algunos y el resto lo hemos dejado NULL veamos: while (g_io_channel_read_line (io, &buffer, &len, NULL, NULL) == G_IO_STATUS_NORMAL) { --- gnomero fija modos [#gnome-hispano +o telemaco] le hemos entregado el canal "io", y le hemos pasado el buffer es alli donde quedará alojado el contenido del la línea además, le pasamos por referencia "len", para que nos indique cuantos bytes pudo leer --- telemaco fija modos [#gnome-hispano -o telemaco] pudo haber sido NULL, si no nos interesase el siguiente parametro, terminator_pos, es para que la función nos informe en que posicion se encuentra almacenada la última posicia o mejor dicho, el terminado de línea y el último, si queremos gestión de errores la función nos retorna un valor GIOStatus y puede ser uno de los siguientes: G_IO_STATUS_ERROR, G_IO_STATUS_NORMAL, G_IO_STATUS_EOF, G_IO_STATUS_AGAIN donde G_IO_STATUS_NORMAL es sinónimo de éxito entonces nuestro ciclo while se puede leer como: "mientras pueda seguir leyendo una línea desde IO, haga" hay que notar que: declaramos buffer como gchar *buffer y el parámetro que espera la función es gchar ** por lo tanto, lo hemos pasado como &buffer si quisieramos usar GIOChannel combinado con popen ya no podriamos usar la función g_io_channel_new_file emplearemos entonces g_io_channel_unix_new () y el resto, funcionará de la misma forma si quisieramos leer un socket, igual usaremos g_io_channel_unix_new () y el resto de las funciones serán las mismas veamos como es la forma de llamarlos el formato es: GIOChannel* g_io_channel_unix_new (int fd); debemos pasarle un descriptor de archivo (fd: file descriptor) entonces: --- gnomero fija modos [#gnome-hispano +o diego] hola FILE *pfd; /* (popen file descriptor) */ luego: pfd = popen ("ls -l", "r"); io = g_io_channel_unix_new (fileno (pfd)); etc. en este caso, usamos fileno para obtener el descriptor del archivo desde un FILE * de la misma forma se realiza con un socket por ejemplo, si constuimos un cliente TCP tendremos algo así como: int main (int argc, char **argv) { int sock; [...] if( (sock = socket( PF_INET, SOCK_STREAM, 0 )) == -1 ) { /* mensaje de error porque no se creo bien el socket */ } io = g_io_channel_unix_new (sock); y de esta forma ya tenemos un GIOChannel a partir de un socket es decir, ya hemos visto como se relacion un pipe, un socket y un archivo que sucede si por algùn motivo debemos abrir un archivo con fopen? es decir, que sucede si estamos obligados a usar FILE * en ese caso: usamos lo mismo que con popen FILE *fd; realizamos el fopen y luego: io = g_io_channel_unix_new (fileno (fd)); otra situación es cuando queremos crear archivos temporales por ejemplo, cuando nuestra aplicación tiene la opción de "guardar" guardamos todo lo que está en memoria a un archivo temporal si es exitoso, renombramos el archivo temporal al nombre que ya existía garantizando que no perderemos datos para crear un archivo temporal usaremos g_mkstemp int g_mkstemp (char *tmpl); que funciona igual que mkstemp () esta función nos devuelte un int, es decir, el número del descriptor de archivo por ejemplo: int fd; fd = g_mkstemp ("my_archivo-XXXXXX"); io = g_io_channel_unix_new (fd); y ya tenemos todo bajo GIOChannel aunque estamos viendo GIOChannel hare un alcance respecto a g_mkstemp la explicacion para tener g_mkstemp y no usar directamente mkstemp es portabilidad en donde exista mkstemp, glib lo usará en donde no, lo proveerá así nuestra aplicación podrá funcionar en cualquier plataforma donde exista glib alguna pregunta? no :-) pues bien llevamos 1 hora y 30 minutos así que antes de terminar les mencionaré algunas funciones utiles, que no son parte de GIOChannel pero seguramente los usaran en el futuro g_file_get_contents () permite leer el contenido *completo* de un archivo y dejarlo en una variable esto es util se queremos un editor de texto sencillo o si sabemos que los archivos de todas formas lo necesitamos en memoria o bien son pequeños eso nos evita iteraciones y lineas de codigo g_file_test () nos permite verificar el tipo de un archivo lo que normalmente hariamos con fstat o stat así podemos saber si el archivo en cuestión es un enlace simbólico, si existe, si es ejecutable, directorio, etc. y las funciones para manejar directorios: g_dir_open, g_dir_read_name que sus nombres son bastante descriptivos. mas pueden revisarlo en la documentación de Glib pues bien la próxima charla Rodrigo Moya les enseñará como crear programas con plugins (extensiones) gestión de errores y como trabajar con hilos (threads) eso es todo :-) Excelente charla German! --- alex fija modos [#gnome-hispano -m] plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap clap, clap plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas bien bien ;-) Germán plas plas plas plas plas plas plas plas plas plas plas plas plas plas plas Buena la charla fenomenal mu guena si señor muy buena, plas plas plas plas felicidades y gracias muy bien dada y muy claro todo lo que has dicho ya me estaba asustando, donde nadie preguntaba :-) xDD bravo!! tá curra la verdad plas plas plas plas