Siguiendo con la vieja serie de post sobre Herramientas Mágicas, hoy voy a comentar la tercera y última herramienta del grupo que inicialmente tenía en mente cuando empecé con esta serie. Esta la menos mágica, pero no es menos útil que las otras dos. Hay varias herramientas más para comentar, ya lo haré más adelante, pero estas primeras tres son las que me resultaron más útiles en general. La herramienta en cuestión es Doxygen, y sirve para generar documentación acerca de un código fuente. Es ideal para documentar las interfaces de nuestras bibliotecas. A esta altura deberíamos suponer que la mayoría de ustedes ya la conoce o conoce alguna otra similar. Pero... (no le digan a nadie) yo me gradué de informático sin haber usado nada que se le parezca. Para ayudar a que eso no le vuelva a ocurrir a otros, voy a escribir un poco contando de qué se trata, presentar un ejemplo, mostrar que ZinjaI puede facilitar su uso, etc.
viernes, 16 de enero de 2015
martes, 6 de enero de 2015
¿Soy un programador o un depurador?
De las múltiples actividades de las que consta el día día de un programador, en mi caso (y en el de muchos) tipear código o dibujar diagramas de clases (por dar un par de ejemplos de actividades "estereotípicas") no es lo que más me ocupa. La mayor parte de mi tiempo como programador se me va "depurando". No es que le reste importancia a las etapas previas (todo lo contrario, cada vez las valoro más), ni que sea tan animal que por cada linea de código que escriba cometa tres errores. Pero es así. Y esto se contradice un poco bastante con lo que a uno le enseñan en la universidad, sobre primero pensar, diseñar, analizar, bosquejar en papel, y solo luego sentarse a codificar. Se puede argumentar que tanto depurar es consecuencia de saltarse o apurarse en las etapas previas. Entonces: ¿es una muy mala señal pasar más tiempo depurando prototipos erróneos o incompletos, que pensando primero y escribiendo luego las cosas bien, sin tantos defectos? Creo que no necesariamente.
martes, 23 de diciembre de 2014
Bibliotecas externas en versión debug
Siempre pensé que era mejor utilizar versiones "release" de las bibliotecas de terceros en mis proyectos, para todas las compilaciones, aún en mis versiones para debug. Es decir, que si mi programa usa una biblioteca B, al depurarlo me interesa solo depurar mi parte. Agregar la información de depuración de B sería agregar ruido. Hace poco comprobé que esto no siempre es recomendable. Es algo que ahora me parece muy obvio, pero la verdad es que antes no le había dado importancia. Nunca había usado intensivamente una versión debug de una biblioteca externa, y por eso no había notado las muchas ventajas que esto tiene.
domingo, 14 de diciembre de 2014
Por qué la ejecución era tan leeenta en el depurador
Hace unos días comenté que había estado renegando para depurar un proyecto en Windows porque al correrlo en el depurador la ejecución tardaba 17 veces más de lo normal. Encontré en aquel momento un workaround para salir del paso, pero no sabía entonces la causa real del problema. Cerré el post prometiendo escribir si la averiguaba, y ahora vengo a cumplir con eso. Ya encontré la causa, y una solución muy simple que, como siempre, estará incluida en ZinjaI para la próxima versión (muy pronto).
jueves, 11 de diciembre de 2014
Cuando la ejecución en el depurador es mucho más leeenta
Saben que en ZinjaI (y en muchísimos otros IDEs) tenemos dos formas de ejecutar un proyecto: la "normal" y la "debug". La "normal" (Ejecución->Ejecutar o F9) guarda, compila y ejecuta el resultado como si hubiésemos hecho doble click en el exe desde el explorador de archivos. La ejecución "debug" (Depuración->Iniciar o F5), en cambio, ejecuta a través de gdb. Es decir, ejecuta gdb (el verdadero depurador que hay detrás de ZinjaI), y le pide a este que cargue y ejecute el exe. Para tener control sobre esta ejecución, gdb puede hacer internamente cosas raras, normalmente sin que lo notemos. Me dí cuenta hace poco que en Windows alguna de estas cosas raras puede hacer que el programa corra mucho más lento que en la ejecución normal. Y cuando un programa, en su ejecución normal revienta luego de unos pocos minutos de proceso, este "enlentecimiento" se torna un problema grave, porque para intentar depurarlo debemos esperar una eternidad hasta llegar al problema, cada vez que lo probamos. Ahora tengo un mecanismo interesante para resolver el problema de analizar el error con el depurador sin esperar tanto tiempo.
miércoles, 19 de noviembre de 2014
Más novedades en la depuración con ZinjaI
Ya les comentaba en otros posts que venía haciendo cambios importantes en ZinjaI para la próxima versión, y especialmente en lo relacionado a la depuración y el manejo de inspecciones. Y como algunos son cambios muy grandes, esta próxima versión se viene demorando bastante. Pero creo que la espera va a valer la pena, porque así como por un lado estos cambios son grandes y lentos de implementar, por el otro lado los resultados son especialmente útiles. Todo rediseño en las tripas de un software, si se hace después de un tiempo prudencial y de haber aprendido mucho de los errores (u ordenado más precisamente, de haber aprendido de muchos errores), facilita el trabajo futuro, permitiendo pensar en cosas que antes no eran posibles o requerían demasiado trabajo. Y les vengo a mostrar un ejemplo de eso, que parece bastante mágico si se lo mira desde el ángulo adecuado.
viernes, 14 de noviembre de 2014
SFINAE: Magia con templates en C++ (parte 3)
Finalmente llegamos a la última parte. Ya introduje en la parte 1 qué es eso de sfinae, y en la parte 2 cómo usarlo para consultar en tiempo de compilación la existencia de algo. La idea era poder plantear ejercicios de programación autocalificables; es decir, que validen solos si son correctos o no al ejecutarse. Para ello nos falta acomodar un poco el resultado de la parte 2 para que sea más cómodo de usar. Primero vamos a definir una macro para declarar con una linea la clase que hace la prueba de existencia de algo. Luego vamos a definir otra macro para declarar con una linea una función que solo se ejecuta si se pasa una prueba. Con todo eso, podemos plantear un ejercicio como el siguiente:
lunes, 10 de noviembre de 2014
SFINAE: Magia con templates en C++ (parte 2)
En la primer parte comenté cómo utilizar algunas reglas particulares de la especialización de funciones genéricas en C++ para permitir o no una determinada especialización de acuerdo a alguna característica de un tipo de dato. La clave estaba en que un argumento de la plantilla intentaba especializar otra plantilla con un entero que surgía de un sizeof. Si en esa otra plantilla fallaba la substitución, no se generaba un error mientras exista alguna otra plantilla en la que no falle. Cualquier cosa que acepte el sizeof nos sirve. Esto permite, por ejemplo, preguntar por la existencia de métodos o atributos, aplicándole el sizeof a un puntero al mismo. Y así, con pequeños podemos "controlar" varias cosas. Veamos cómo con una característica de C++11 lo generalizamos mejor, y con un par de lineas adicionales lo hacemos mucho más fácil de utilizar.
jueves, 6 de noviembre de 2014
SFINAE: Magia con templates en C++ (parte 1)
SFINAE son las iniciales de "Substitution Failire Is Not An Error" (un fallo en una substitución no es un error). Así se llama informalmente a un conjunto de situaciones muy particulares en C++, donde un error que resultaría de intentar especializar un template es ignorado silenciosamente durante la compilación. Si combinamos esto con los mecanismos de sobrecarga y resolución de nombres, podemos lograr cosas muy raras, como preguntar si una clase existe, si tiene cierto método, o si se puede usar de tal forma. Con algunos pocos trucos adicionales podemos tener código que se ejecute solamente si es válido, pero si no lo es (y esto es lo interesante) no genere errores de compilación, sino que utilice un camino alternativo. Llegué a esto tratando de armar ejercicios de programación que se califiquen a sí mismos (para hacer evaluación continua sin perder mucho tiempo de clases), y como el resultado es técnicamente muy interesante, aprovecho la excusa de siempre para documentarlo.
domingo, 28 de septiembre de 2014
Sobre templates y tiempos de compilación
Cualquiera que utilice frecuentemente clases genéricas en C++ sabe que el uso de templates puede aumentar considerablemente los tiempos de compilación de un proyecto, dado que una clase/función templatizada no puede compilarse en un .cpp separado del .cpp que la usa, sino que debe estar completamente definida y declarada en el .h... ¿o no?. Siempre uso como ejemplo extremo la biblioteca CImg, que consta de un archivo .h de alrededor de 2MB de código fuente, 99% genérico, casi todo directa o indirectamente asociado a su clase principal (CImg). Esto hace que compilar un ejemplo muy pequeño tarde unos 25 segundos (en un I7-870), tiempo que aumenta notablemente cuando el ejemplo deja de ser tan pequeño. En este artículo les cuento un truco que voy a incluir en la plantilla del complemento para ZinjaI de CImg, que permite bajar esos 25 segundos a alrededor de 9. Es decir, menos de la mitad. Y esta diferencia será mucho más grande cuando el ejemplo se complique.
viernes, 19 de septiembre de 2014
Mientras tanto, en PSeInt...
Hace ya un buen rato que no vemos versiones nuevas de PSeInt. En buena parte se debe a que por diversos motivos le he dedicado realmente poco tiempo últimamente. Pero no quiere decir que no haya ocurrido nada. Hay algunos cambios ya implementados para la próxima versión oficial, y hay más cambios en camino, algunos muy interesantes. En este post les cuento un poco qué nos depara el futuro a corto y mediano plazo en PSeInt.
domingo, 14 de septiembre de 2014
Excepciones y RAII (control de errores parte 2)
En el post anterior dejé planteado el problema de cómo manejar, a nivel de código, la posibilidad de que una función falle. Es decir, con qué mecanismo poder o no detectar un error, identificarlo de forma más o menos fina, y eventualmente actuar en consecuencia. Todo pensando en cuanto cuesta eso, no en términos de eficiencia, sino de legibilidad del código, de trabajo extra a la hora de programar, y en la usabilidad de estas clases o funciones que podrían fallar. Describí a grandes rasgos, con ejemplos demasiado breves, las primeras opciones que uno podría considerar, y dejé entrever alguna conclusión.
Podríamos resumir con algo como lo que sigue. Pareciera que las excepciones son lo ideal para cosas que podría ser aceptable que fallen, aunque no sería esperable ni tan frecuente. Por ejemplo, que me quede sin memoria, que se caiga abruptamente una comunicación, etc. Mientras que dejamos los asserts (o _revienta) para cosas que definitivamente no deberían ocurrir, a menos que el programa contenga un error. Ahora vamos a analizar algunos detalles más finos e interesantes sobre el manejo de excepciones.
Podríamos resumir con algo como lo que sigue. Pareciera que las excepciones son lo ideal para cosas que podría ser aceptable que fallen, aunque no sería esperable ni tan frecuente. Por ejemplo, que me quede sin memoria, que se caiga abruptamente una comunicación, etc. Mientras que dejamos los asserts (o _revienta) para cosas que definitivamente no deberían ocurrir, a menos que el programa contenga un error. Ahora vamos a analizar algunos detalles más finos e interesantes sobre el manejo de excepciones.
sábado, 6 de septiembre de 2014
Control de errores con C++
Hoy vengo a divagar sobre un aspecto en el diseño de interfaces para clases y/o funciones que siempre me genera dudas: ¿qué pasa cuando algo sale mal durante la ejecución? Muchas cosas pueden fallar, en diferentes niveles, algunos aceptables y otros no. ¿Cuándo retornar un código de error? ¿Cuándo simplemente retornar true/false? ¿Cómo manejar esto a nivel de código? ¿Cuándo usar excepciones? ¿Cuándo assert? ¿Cuando ignorarlo? Tengo que confesar con vergüenza que durante mucho tiempo vi a las excepciones como algo a evitar, una funcionalidad innecesaria de segunda clase (como un garbage collector :). Evité por años usarlas, y por ello no se ven hoy en día en mis códigos. Pero me voy dando cuenta de que estoy equivocado. No son la solución a todos los problemas, pero tienen lo suyo, y hacen cosas que otros mecanismo no. La idea es entonces analizar cada mecanismo de control de errores para tratar de entender dónde conviene usar
cada uno.
lunes, 1 de septiembre de 2014
Entendiendo la tabla de inspecciones (parte 2)
Siguiendo con esta serie de artículos sobre cómo hace ZinjaI para mostrar las inspecciones durante la depuración, ahora voy contar un poco más qué pasa por debajo, en gdb, el verdadero depurador. Si
alguien alguna vez usó directamente gdb sabrá que su interfaz es
básicamente una linea de comandos, donde uno ingresa por ejemplo "break hola.cpp:12" para agregar un punto de interrupción, o "print x" para ver qué hay en la variable "x". Sin
embargo, cuando uno programa un front-end para gdb (una interfaz, como
ZinjaI), puede utilizar un conjunto diferente de comandos que otorgan
una salida más uniforme y predecible para que analizarla automáticamente
sea más simple y eficiente. Esta interfaz alternativa (también tipo linea de
comandos) se conoce como MI (creo que por machine-interface).
lunes, 18 de agosto de 2014
Preguntas frecuentes: "Error al crear proceso"
Ya les comenté en el post anterior que hay algunos errores que se les manifiestan a muchos usuarios de ZinjaI, y que entonces iba a comentarlos aquí, con sus causas y pseudo-soluciones para facilitar un poco las cosas. Decidí empezar por dos problemas particulares (de los cuales creo que ZinjaI no tiene culpa alguna), porque son de los que más me han consultado a mi correo personal. El que presento hoy tiene que ver con esos infames comerecursos que son los antivirus, y se manifiesta de esta forma: cada vez que desde ZinjaI se quiere compilar y ejecutar un programa, la compilación finaliza con éxito (suponiendo que el código era correcto), pero en la ventana de ejecución sale algo como "Error al crear proceso: C:\booga\dooga\algun_programa.exe".
Suscribirse a:
Entradas (Atom)