Desde que comencé a publicar mis proyectos de software libre utilizo SourceForge. Allá por el 2004 cuando decidí "colgar" PSeInt en algún lugar de la web, la gente de la lista de mails de gleducar me sugirió SourceForge, y dado que yo no conocía casi nada al respecto, acepté la sugerencia. Desde entonces y hasta el día de hoy, mis proyectos se han alojado exclusivamente allí, y después de varios años de utilizar sus servicios puedo sacar algunas conclusiones.
Para empezar, tuvo sus puntos flojos en algunas ocasiones. Hace un tiempo solía tener problemas con algunos mirrors que en combinación con ciertos navegadores me dejaban a veces las descargas incompletas. En otros momentos el acceso al sitio era bastante lento. Las interfaces para realizar algunas tareas solían ser tediosas. Pero todo eso fue cambiando, a veces de a poco, a veces de golpe, y mantener mi confianza en este servicio cuando aparecieron alternativas muy prometedoras y populares (como pueden ser google code o github), está pagando con creces.
viernes, 26 de octubre de 2012
martes, 23 de octubre de 2012
Parseo y mayusculización en PSeInt
Estoy casi seguro de que esa palabra (mayusculización) no existe, pero viene bien para hablar de algo que pasa en PSeInt cuando le pedimos que haga algo con un algoritmo que no sea simplemente guardarlo. Por ejemplo, si le pedimos que genere el diagrama de flujo, ya sea para verlo o editarlo, todos los identificadores pasan a estar en mayúsculas. Si le pedimos que exporte a código C++ ocurre algo similar, pero en este caso con minúsculas. Si le pedimos que lo ejecute y el intérprete encuentra errores, los identificadores que pone para aclarar entre paréntesis en los mensajes de error están siempre en mayúsculas. Y así ocurre con algunas otras situaciones menos frecuentes.
La justificación es bastante simple: para simplificar el "parseo" del pseucódigo, todo se pasa a mayúsculas. "To parse" significa analizar, o procesar, y el "parser" es una parte fundamental de cualquier intérprete/compilador. Sin embargo, es muy frecuente no dejarle al parser toodo el trabajo, sino hacer antes un preprocesamiento del código para simplificar algunas tareas posteriores (que no son difíciles en realidad, solo tediosas o poco eficientes de otra forma). En muchos casos, parte de este preproceso está en el parser mismo, ya que lo realiza internamente.
La justificación es bastante simple: para simplificar el "parseo" del pseucódigo, todo se pasa a mayúsculas. "To parse" significa analizar, o procesar, y el "parser" es una parte fundamental de cualquier intérprete/compilador. Sin embargo, es muy frecuente no dejarle al parser toodo el trabajo, sino hacer antes un preprocesamiento del código para simplificar algunas tareas posteriores (que no son difíciles en realidad, solo tediosas o poco eficientes de otra forma). En muchos casos, parte de este preproceso está en el parser mismo, ya que lo realiza internamente.
viernes, 12 de octubre de 2012
La estrella del show
En este mini-show que componen mis proyectos personales, unos posts en un blog que pareciera tener alguna fijación con los insectos, y otros delirios afines, donde ustedes, lectores y usuarios, son los preciados espectadores que busco cautivar, PSeInt se roba todos los flashes. Mientras que a mis últimos 7 posts les llevó unos 10 días llegar a la (para nada envidiable) cantidad de 50 visitas a cada uno, la nueva versión de PSeInt tuvo más 200 descargas en sus primeras 10 horas de vida y eso que esas 10 horas fueron entre las 10 de la noche y las 8 de la mañana, lo que implica que la mayoría de los usuarios estaba cenando, durmiendo, mirando tele, o haciendo cualquier otra cosa, pero no creo que trabajando o estudiando tanto (aunque pensandolo bien yo suelo hacer mis tareas de mantenimiento, lo que incluye actualizaciones, durante la noche). Para el final de su primer día, había llegado a nada menos que 900 descargas.
lunes, 1 de octubre de 2012
Nuevo miniproyecto: wxTimeLineEditor
Hace un tiempo agregué en la página de PSeInt, en la parte de documentación, una "linea de tiempo" que resume los hitos más importantes para ese proyecto. Me pareció útil para ver rápidamente cómo evolucionó el proyecto en estos casi 9 años, porque creo que la historia de PSeInt tiene varios matices interesantes, dado que nació con pocas expectativas, y tiempo después el hecho publicarlo como software libre me generó unas cuantas gratas sorpresas, y con él fui aprendiendo muchas cosas. En fin, la linea de tiempo no era realmente un gráfico de una linea de tiempo, sino una simple tabla de fechas y acontecimientos porque en aquel momento no encontré un software que me permita generar el gráfico como yo quería. Lo que buscaba era algo sencillo, offline, y que permita exportar la imagen. Probé unos cuantos, algunos muy malos, otros innecesariamente complicados, otros solo para Windows, y algunos muy buenos pero que no se ajustaban a lo que yo quería, o que me daban errores varios al utilizarlos. Finalmente decidí programar algo yo, ya que tomando la experiencia que ya conté del graficador de diagramas de flujo, iba a ser trabajo de una tarde/noche o a lo sumo un fin de semana, al menos para las muy pocas funcionalidades que realmente necesitaba, "los extras vendrán después" pensé. A la noche tenía mi preciado grafiquito. Así es el poder que da aprender a programar, si no encuentro lo que quiero, tengo siempre la alternativa de hacerlo (o intentar, tampoco hago magia :). En fin, la noticia es que ahora registré el proyecto en SourceForge y si les sirve para algo ya pueden empezar a descargarlo.
viernes, 28 de septiembre de 2012
Compilación y bibliotecas (parte 5): crear bibliotecas con ZinjaI
En los cuatro posts anteriores de esta serie Compilación y Bibliotecas hablé un poco en general del proceso de compilación y el uso de bibliotecas externas, para explicar finalmente en el cuarto cómo utilizar un biblioteca X en un proyecto de ZinjaI. En la última entrega de la serie, vamos a ver un caso mucho menos frecuente, pero igualmente interesante: vamos a ver cómo crear nuestras propias bibliotecas con ZinjaI.
Desde hace ya unas cuantas versiones, ZinjaI presenta en el cuadro de diálogo de Opciones de Compilación y Ejecución de Proyecto, una pestaña titulada "Biblioteca". Esta pestaña sirve para crear una biblioteca a partir de una parte del proyecto. Sabemos ya que una biblioteca por sí misma no se puede ejecutar, sino que requiere de un programa cliente. Cuando uno desarrolla una biblioteca va desarrollando al mismo tiempo uno o más programas clientes para probar su funcionamiento. Entonces, la idea es que en el proyecto de ZinjaI tendremos un conjunto de archivos que conforman los fuentes de una o más bibliotecas, y otro conjunto de archivos que conforman el programa cliente. En la pestaña "Biblioteca" es donde decimos qué bibliotecas generar y con qué archivos. También se puede indicar allí mediante un checkbox que el proyecto solo generará una biblioteca, y no habrá programa cliente si así se quisiera, pero creo que no es lo normal durante el desarrollo, aunque puede ser útil en los perfiles para release.
Desde hace ya unas cuantas versiones, ZinjaI presenta en el cuadro de diálogo de Opciones de Compilación y Ejecución de Proyecto, una pestaña titulada "Biblioteca". Esta pestaña sirve para crear una biblioteca a partir de una parte del proyecto. Sabemos ya que una biblioteca por sí misma no se puede ejecutar, sino que requiere de un programa cliente. Cuando uno desarrolla una biblioteca va desarrollando al mismo tiempo uno o más programas clientes para probar su funcionamiento. Entonces, la idea es que en el proyecto de ZinjaI tendremos un conjunto de archivos que conforman los fuentes de una o más bibliotecas, y otro conjunto de archivos que conforman el programa cliente. En la pestaña "Biblioteca" es donde decimos qué bibliotecas generar y con qué archivos. También se puede indicar allí mediante un checkbox que el proyecto solo generará una biblioteca, y no habrá programa cliente si así se quisiera, pero creo que no es lo normal durante el desarrollo, aunque puede ser útil en los perfiles para release.
miércoles, 26 de septiembre de 2012
Compilación y bibliotecas (parte 4): utilizar bibliotecas desde ZinjaI
En este post voy a tratar de ser bien práctico y dar instrucciones
precisas de qué hacer para utilizar bibliotecas no estándar en ZinjaI.
Lo primero que hay que saber es qué se necesita para utilizar una
biblioteca. Repasando los posts anteriores se llega a la conclusión de
que se necesitan dos cosas, los archivos de cabecera (.h o .hpp), y los
binarios (.dll, .lib, .so, o .a). Los primeros son necesarios para
compilar las partes del programa cliente que hacen uso de la biblioteca.
Estos fuentes deberán incluirlos (con "#include") para que el
compilador no se queje de que no conoce las clases o funciones de la
biblioteca al querer utilizarlas. Los segundos son necesarios para
enlazar el ejecutable final, ya que los primeros solo decían qué hace la
biblioteca, pero no cómo. En algunos casos particulares puede que esté
todo en los .h y no haya binarios. Esto suele ocurrir cuando hay
templates involucrados (como la biblioteca CImg por ejemplo), o cuando
por decisión de diseño porque la biblioteca no es tan compleja, el que la
desarrolló lo hizo así para facilitar su utilización (por ejemplo, la
biblioteca EasyBMP), pero son excepciones (y aún así requieren binarios del sistema, bibliotecas de Windows por ejemplo), normalmente se requieren las
dos cosas (como con GLUT, wxWidgets, SFML, QT, etc, etc, etc).
sábado, 22 de septiembre de 2012
Compilación y bibliotecas (parte 3): ese oscuro y mágico proceso
Centrándonos ahora en el proceso de compilación de un programa C++, hay que aclarar que es un proceso de varias etapas, y que no se realiza por un solo programa, sino por un conjunto. El compilador es en realidad un grupo de programas. En el proceso de convertir el código C++ en código objeto hay que preprocesar los fuentes, analizar la sintaxis y traducir a lenguaje ensamblador, convertir el código ensamblador en código de máquina dentro de un archivo llamado "objeto", aplicando optimizaciones, agregando información para el depurador, etc, y finalmente juntar todos los archivos objetos y, si las piezas encajan, empaquetar todo en un archivo ejecutable final. La parte del preproceso en C/C++ es la que analiza las lineas que empiezan con # (como los #define, #ifdefs, #includes) y otras tareas similares (quitar comentarios y expandir macros por ejemplo). Luego viene la etapa más importante, que sería la verdadera compilación, que es donde se traduce el código fuente a código de máquina. Pero en un proyecto real, el código fuente no es un sólo gran archivo, sino que está distribuido en muchas partes, y se debe traducir cada uno de ellas. Luego, juntando todas esas traducciones, se confecciona el ejecutable. Este último paso se conoce como enlazado. En general, a fines prácticos, al programador sólo le interesa diferenciar entre la etapa de compilación (incluyendo aquí en un solo bloque preproceso, compilación y ensamblado), y la etapa de enlazado.
miércoles, 19 de septiembre de 2012
Compilación y bibliotecas (parte 2): Intérpretes vs Compiladores
A la hora de ejecutar un programa escrito con algún lenguaje de programación hay, en principio, dos grandes opciones: interpretar y compilar. Digo en principio, porque se podría discutir un buen rato sobre dónde ubicar lenguajes administrados, máquinas virtuales por ejemplo, o intérpretes que utilizan algún formato intermedio que no es ni fuente ni objeto, pero simplificando en los dos casos de libro más claros, tenemos intérpretes y compiladores. Un compilador es un programa que "traduce" el código fuente de un lenguaje de alto nivel a código de máquina listo para ser ejecutado. Un intérprete, en cambio, es otro programa que interpreta las instrucciones del código de alto nivel e intenta ejecutarlas él mismo. La diferencia en principio no parece clara, pero pongamos una metáfora. Si quiero escuchar una canción, digamos "Over the rainbow", la hoja con la partitura y la letra serían el código fuente. Un compilador sería un músico que toma la partitura, y graba la canción por ejemplo en un cd de audio que voy a poder escuchar en mi auto cuando quiera aunque el músico se haya retirado, vendido su guitarra y quemado la partitura. Un intérprete en cambio sería un músico que toca la canción en vivo para mi, leyendo de la partitura cada vez que quiero escucharla, y que tengo que llevar sentado con su guitarra en el asiento del acompañante todo el tiempo. Parece poco cómodo, pero tiene sus ventajas. Digamos que quiero probar cómo quedaría si agrego algunos arreglos, o si cambio la letra por otra nueva que se me ocurrió, o si afino la guitarra un semitono más arriba. En el primer caso, tendría que llamar al músico "compilador" para volver a grabar un nuevo cd, mientras que en el segundo tendría que hacer lo mismo de siempre, pedirle al músico "intérprete" que interprete una vez más la partitura, que esta vez estará modificada.
lunes, 17 de septiembre de 2012
Compilación y bibliotecas (parte 1): ¿por qué?
viernes, 7 de septiembre de 2012
Próximamente: SubProcesos en PSeInt!
La posibilidad de crear funciones/subprocesos/subrutinas/como-quieran-llamarlos en PSeInt es por lejos la funcionalidad por la que más veces me han preguntado los usuarios. Desde hace años que joden con eso. Y desde hace años que les digo lo mismo: por razones históricas el código base del intérprete está tan mal diseñado que me es imposible agregarlas en ese estado. Pero con el paso del tiempo, muchos cambios internos fueron sucediéndose. Por ejemplo, la clase que administra la memoria virtual del intérprete es completamente nueva, culpa de esto los tipos de datos se gestionan de forma muy diferente a cómo se lo hacía en las primeras versiones, y gracias a esto la evaluación de expresiones a mejorado muchísimo y se ha vuelto también más flexible. Después de muchos intentos, esas partes quedaron listas para un futuro mejor. Seguro que tienen sus errores, pero comparativamente ha sido un gran paso. Por contraparte, el código que analiza la estructura de las instrucciones y que en verdad parsea la sintaxis más allá de las expresiones sigue siendo bastante horrible en su diseño. De a poco voy puliendo la implementación, pero el diseño no ha cambiado sustancialmente. No me gusta el enfoque actual, pero no estoy seguro de que el enfoque teóricamente correcto para un intérprete sea el mejor en este caso. No obstante, repensando el tema de los subprocesos por enésima vez llegué a la conclusión de que la no tan pequeña evolución de la que hablaba ya era sufiente como para intentar implementarlos sin morir en el intento. Puse manos a la obra, y algo salió. En este post quiero presentar con bombos y platillos la tan pedida y flamante funcionalidad, y explicar algunas cuestiones de diseño desde el punto de vista del lenguaje y la interfaz, ya no desde la implementación del intérprete. Hay muchos puntos grises que aún no me terminan de convencer y que posiblemente cambien en el futuro, así que hay que tomar todo como está, en estado experimental, sin terminar, sujeto a cambios, pero igual ya hay disponible en el repositorio git un intérprete capaz de aceptar la definición de funciones/subprocesos por parte del usuario.
lunes, 27 de agosto de 2012
¿Hacia dónde debe ir el pseudolenguaje de PSeInt?
Escribo este post para abrir formalmente un debate con la comunidad de usuarios de PSeInt. Quiero pedir sus opiniones y hacer que sean partícipes del futuro de PSeInt, y entonces comienzo escribiendo esto para dejar claro mi punto de vista y mis objetivos como principal y único desarrollador de este proyecto. La motivación del debate reside en la necesidad de ampliar (o no) el pseudolenguaje que utilizo. Varios usuarios, en la mayoría docentes, me han pedido extender el pseudolenguaje para poder hacer tal o cual cosa. Por ejemplo: definir funciones/subprocesos, manejar cadenas, escribir y leer archivos, controlar mejor la salida por consola (ubicación, colores, etc). Por otro lado, algunos usuarios alumnos escriben preguntando ¿cómo hago tal cosa? que actualmente no se puede hacer, e indirectamente también están aportando una nueva idea. Entonces, tengo que decidir si el lenguaje va a crecer o no para permitir estas cosas, cuales debo permitir y cuales no, y sobre todo, de qué manera se integran. Para tomar esta decisión de la mejor forma y dejar contenta a la mayor cantidad de personas posibles es que quiero iniciar antes un debate. En primer lugar voy a describir mi visión del pseudocódigo, y debo advertir que será innegociable. A continuación, presentaré algunas de las posibles mejoras, mencionando los pros y contras que veo en ellas, como se relacionan con mi visión innegociable, y tratando de dejar abierto el camino para la futura discusión.
miércoles, 8 de agosto de 2012
El runner, la terminal y las señales
Hace muy poquito escribí acerca del uso de la función signal de C para interceptar un segmentation fault. En estos días estuve tratando de utilizarla para otras cosas y eso me llevó a notar algunos problemas en el runner de ZinjaI. El runner es un pequeño ejecutable que utiliza ZinjaI para ejecutar indirectamente los programas y proyectos que compila, a modo de wrapper. Desde el punto de vista del usuario el runner es casi invisible, pero básicamente se encarga de cambiar el directorio de trabajo, informar cómo terminó la ejecución (con qué código de salida), y esperar una tecla si es necesario para que no se cierre la terminal.
Cuando uno ejecuta directamente (no mediante ZinjaI) un programa de consola en Windows, este se ejecuta en una de esas ventanas negras y se cierra inmediatamente cuando termina. En GNU/Linux es peor aún, ya que si estamos en el entorno gráfico ni siquiera se toma la molestia de abrir una ventana. En GNU/Linux, ZinjaI llama a una terminal gráfica para que la cosa se vea, y le pide al runner que, entre otras cosas, espere una tecla después de que termine el programa, para que el usuario pueda ver qué resultado arrojó. Básicamente ZinjaI pide a una terminal gráfica (xterm o konsole por ejemplo) que ejecute el runner, y éste hace un fork (se "divide" en dos procesos idénticos, se llama padre al original e hijo al nuevo), ejecutando al programa que realmente queríamos ver en el nuevo proceso hijo. El proceso original espera a que termine el hijo, y analiza su código y estado de salida, que puede ser el valor de retorno del proceso (usualmente el return 0 del final), o el número de la señal que lo obligó a detenerse de forma anormal. Hasta aquí todo más o menos normal. Eso es lo que hace el runner en las versiones de ZinjaI estables. Pero jugando con las señales me surgió un nuevo problema: cuando mando una señal desde el teclado en la terminal, ambos procesos la reciben.
Cuando uno ejecuta directamente (no mediante ZinjaI) un programa de consola en Windows, este se ejecuta en una de esas ventanas negras y se cierra inmediatamente cuando termina. En GNU/Linux es peor aún, ya que si estamos en el entorno gráfico ni siquiera se toma la molestia de abrir una ventana. En GNU/Linux, ZinjaI llama a una terminal gráfica para que la cosa se vea, y le pide al runner que, entre otras cosas, espere una tecla después de que termine el programa, para que el usuario pueda ver qué resultado arrojó. Básicamente ZinjaI pide a una terminal gráfica (xterm o konsole por ejemplo) que ejecute el runner, y éste hace un fork (se "divide" en dos procesos idénticos, se llama padre al original e hijo al nuevo), ejecutando al programa que realmente queríamos ver en el nuevo proceso hijo. El proceso original espera a que termine el hijo, y analiza su código y estado de salida, que puede ser el valor de retorno del proceso (usualmente el return 0 del final), o el número de la señal que lo obligó a detenerse de forma anormal. Hasta aquí todo más o menos normal. Eso es lo que hace el runner en las versiones de ZinjaI estables. Pero jugando con las señales me surgió un nuevo problema: cuando mando una señal desde el teclado en la terminal, ambos procesos la reciben.
viernes, 3 de agosto de 2012
Numeración de Versiones
Cómo numerar las versiones de un programa no es un detalle menor. El número de versión comparativamente dice mucho. El cambio ayuda a despertar interés, influye sobre el deseo de actualizar del usuario, da idea de la edad del proyecto, etc. Es importante que el usuario pueda determinar exactamente qué versión tiene, sobre todo para cuando reporta un error o pide algún cambio. Y si el número le indica algo fácilmente mejor.
Primero hay quienes numeran sus versiones de la forma tradicional, con la 1.0, 1.1, 2.0, etc. Dentro de este esquema, hay muchas formas de utilizarlo. En general, un número de versión alto sugiere un programa maduro, mientras que un número bajo puede sugerir un producto incompleto. Pero conozco proyectos excelentes, que llevan años en desarrollo, llenos de funcionalidades, y que no han llegado todavía ni a la versión 1, como por ejemplo Inkscape, que va por la versión 0.48, y sin embargo todavía no encontré algo que no pueda hacer, y eso que yo solía ser un usuario más o menos avanzado de Corel Draw, allá por las versiones 3, 5 y 7. En el caso de Inkscape, el motivo creo que es simplemente una planificación demasiado ambiciosa; van a usar el 1.0 cuando tengan todo lo que soñaron en esa herramienta y algunas cosas más que se les ocurrieron después también. En otra línea se encuentran proyectos como Firefox, cuyo número de versión avanza de forma ridícula, y en poco más de un año pasamos de la 4 a la 14. El cambio parece haber sido impulsado por las odiosas comparaciones con la competencia. Si Internet Explorer iba a por el 9, Chrome por el 6, Opera alrededor del 11, Mozilla con el 3 parecía poco. Por una cuestión de puro marketing diría, decidieron acelerar la numeración. Algo similar o peor pasó con mi tan preciada distribución de GNU/Linux favorita, Slackware. Slackware quedó atrasada en números respecto a otras distros, y la solución fué más directa: no cambiaron su sistema de numeración ni mucho menos, simplemente dieron un salto en el tiempo y se comieron unos cuanto números pasando directamente desde la 4 a la 7 y luego siguieron como si nada. Jamás hubo una versión 5 o 6.
Primero hay quienes numeran sus versiones de la forma tradicional, con la 1.0, 1.1, 2.0, etc. Dentro de este esquema, hay muchas formas de utilizarlo. En general, un número de versión alto sugiere un programa maduro, mientras que un número bajo puede sugerir un producto incompleto. Pero conozco proyectos excelentes, que llevan años en desarrollo, llenos de funcionalidades, y que no han llegado todavía ni a la versión 1, como por ejemplo Inkscape, que va por la versión 0.48, y sin embargo todavía no encontré algo que no pueda hacer, y eso que yo solía ser un usuario más o menos avanzado de Corel Draw, allá por las versiones 3, 5 y 7. En el caso de Inkscape, el motivo creo que es simplemente una planificación demasiado ambiciosa; van a usar el 1.0 cuando tengan todo lo que soñaron en esa herramienta y algunas cosas más que se les ocurrieron después también. En otra línea se encuentran proyectos como Firefox, cuyo número de versión avanza de forma ridícula, y en poco más de un año pasamos de la 4 a la 14. El cambio parece haber sido impulsado por las odiosas comparaciones con la competencia. Si Internet Explorer iba a por el 9, Chrome por el 6, Opera alrededor del 11, Mozilla con el 3 parecía poco. Por una cuestión de puro marketing diría, decidieron acelerar la numeración. Algo similar o peor pasó con mi tan preciada distribución de GNU/Linux favorita, Slackware. Slackware quedó atrasada en números respecto a otras distros, y la solución fué más directa: no cambiaron su sistema de numeración ni mucho menos, simplemente dieron un salto en el tiempo y se comieron unos cuanto números pasando directamente desde la 4 a la 7 y luego siguieron como si nada. Jamás hubo una versión 5 o 6.
lunes, 30 de julio de 2012
Espejitos de Colores
Vengo a develar el oscuro misterio que envuelve a mi computadora cuando se conecta a un proyector para dar clases. Los más supersticiosos dicen que el código se escribe solo, los escépticos quieren saber por qué. Yo no entiendo nada...
Uno, como desarrollador de un proyecto de software que a su vez consume (que uso para mis otros desarrollos o para mis clases), tiene una posición de privilegio. Porque si uno programa para otro, tiene que hacer ingeniería de software de la que no me gusta hacer. Hay que averiguar que necesita el usuario (averiguar no es preguntarle, normalmente el usuario no sabe ni qué quiere ni qué se puede hacer), hay que entender su posición, imaginar el detalle de los casos de uso, identificar los problemas, proponer las mejoras, etc. Estando a la vez en la posición del desarrollador y del usuario, no hay tanto que adivinar. Generalmente creo que sé que me gustaría ver en mis programas, y sé qué puedo implementar y que no (aunque también suelen sorprenderme gratamente sugerencias de otros usuarios o características de otros entornos que no se me habían ocurrido).
Uno, como desarrollador de un proyecto de software que a su vez consume (que uso para mis otros desarrollos o para mis clases), tiene una posición de privilegio. Porque si uno programa para otro, tiene que hacer ingeniería de software de la que no me gusta hacer. Hay que averiguar que necesita el usuario (averiguar no es preguntarle, normalmente el usuario no sabe ni qué quiere ni qué se puede hacer), hay que entender su posición, imaginar el detalle de los casos de uso, identificar los problemas, proponer las mejoras, etc. Estando a la vez en la posición del desarrollador y del usuario, no hay tanto que adivinar. Generalmente creo que sé que me gustaría ver en mis programas, y sé qué puedo implementar y que no (aunque también suelen sorprenderme gratamente sugerencias de otros usuarios o características de otros entornos que no se me habían ocurrido).
lunes, 23 de julio de 2012
Cómo prepararse para enfrentar un segfault (parte 2)
En la primer parte comenté cómo hacer para que un programa simule un punto de interrupción si encuentra una condición inesperada. Buscando algo más en google, encontré algunas otras macros interesantes, diseñadas para generar errores en tiempo de compilación y ya no de ejecución. Si bien esto no cuadra con el título (más abajo hay otros ejemplos que sí), igual vale la pena comentarlo para completar el tema de los asserts y la macro _revienta que empezé en la primer parte. Se basan en errores como declarar arreglos con tamaños negativos, o tratar de utilizar especializaciones de una plantilla que no existen. La idea de la primer versión es (simplificado y con llaves extra para evitar un problema cuando hay mensajes repetidos):
#define _compile_assert(x,mensaje) { char _compile_assert##mensaje [x?1:-1]; }
....
_compile_assert(sizeof(T1)
<sizeof(T2),EL_TAMANIO_DE_T1_DEBE_SER_MENOR_QUE_EL_DE_T2);
que va a generar un error del tipo"error: size of array '_compile_assert_EL_TAMANIO_DE_T1_DEBE_SER_MENOR_QUE_EL_DE_T2' is negative", pero que también va a generar un warning del tipo "unused variable '_compile_assert_EL_TAMANIO_DE_T1_DEBE_SER_MENOR_QUE_EL_DE_T2'" si estos warnings están habilitados. La ventaja de este enfoque es que permite insertar en el mensaje de error algún texto propio para agregar más información. El segundo enfoque no, pero aún así me parece más elegante:
#define _compile_assert(x,mensaje) { char _compile_assert##mensaje [x?1:-1]; }
....
_compile_assert(sizeof(T1)
<sizeof(T2),EL_TAMANIO_DE_T1_DEBE_SER_MENOR_QUE_EL_DE_T2);
que va a generar un error del tipo"error: size of array '_compile_assert_EL_TAMANIO_DE_T1_DEBE_SER_MENOR_QUE_EL_DE_T2' is negative", pero que también va a generar un warning del tipo "unused variable '_compile_assert_EL_TAMANIO_DE_T1_DEBE_SER_MENOR_QUE_EL_DE_T2'" si estos warnings están habilitados. La ventaja de este enfoque es que permite insertar en el mensaje de error algún texto propio para agregar más información. El segundo enfoque no, pero aún así me parece más elegante:
Suscribirse a:
Entradas (Atom)