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.

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