Mostrando entradas con la etiqueta ingeniería de software. Mostrar todas las entradas
Mostrando entradas con la etiqueta ingeniería de software. Mostrar todas las entradas

martes, 10 de enero de 2023

Más avances en la reconstrucción de PSeInt

Durante las últimas dos semanas pude invertir una buena cantidad de horas en PSeInt, y seguir con la tan postergada reestructuración interna. De a poquito empieza a tomar forma y a verse una luz al final del túnel.

viernes, 25 de noviembre de 2022

Primeros cambios en el interior PSeInt (2da parte)

Repito el comienzo del último post: "hay tres grandes objetivos: unificar algoritmos repetidos, eliminar todo el estado global, y separar/independizar los más posible las etapas del proceso de análisis e interpretación". En aquel me centraba en el primero, en este cuento cómo van los otros dos.

jueves, 29 de septiembre de 2022

Primeros cambios en el interior PSeInt

Ya comenté que iba a ir refactorizando muy de a poco el horrible código de pseint. Se podría decir que hay tres grandes objetivos: unificar algoritmos repetidos, eliminar todo el estado global, y separar/independizar los más posible las etapas del proceso de análisis e interpretación. Veamos cómo y para qué.

viernes, 2 de septiembre de 2022

La metamorfosis interna de PSeInt

Siempre dije que internamente el intérprete está muy mal diseñado, y muchas veces amagué con rehacerlo completamente. Sin embargo ninguno de esos amagues se completó por falta de tiempo. Así que ahora voy a tomar el camino más largo porque... Claro, como me falta tiempo, mejor ir por el camino más largo, ¿no? ¿WTF?

miércoles, 29 de julio de 2020

Sin comentarios

Pasé gran parte de los últimos meses trabajando en un proyecto nuevo. Lo  estoy desarrollado en C++ moderno y aplicando absolutamente todo lo que aprendí de mis errores en estos años. Y hay algo raro que se dio sin querer con cierta naturalidad en el código: casi no hay comentarios. Eso no suele ser una buena señal, pero en este caso sí que lo es.

miércoles, 14 de marzo de 2018

Organización de los archivos de un proyecto (parte 1)

Casi todas las plantillas de proyecto de ZinjaI crean una única carpeta en la que los fuentes, el archivo de proyecto y eventuales extras van juntos, y solo se separan en una subcarpeta (debug/release) los archivos generados en la compilación. Esto es lo más directo para empezar, y funciona bien cuando es un proyecto pequeño.

Pero cuando los binarios empiezan a requerir archivos de datos/recursos, el proyecto crece y tenemos varias decenas de fuentes, la arquitectura se complejiza y empezamos a dividir en módulos o bibliotecas, etc; entonces esa organización se vuelve engorrosa, la carpeta del proyecto, una ensalada; y hay que hacer algo al respecto.

La mayoría de los alumnos no considera esto hasta que es demasiado tarde, simplemente por falta de experiencia (o al menos eso me pasó a mí y lo aprendí por las malas).


martes, 13 de junio de 2017

Nuevo evaluador de expresiones para PSeInt

Cuando empecé a pensar en reescribir el parser de PSeInt tenía planeado enfocarme en el tokenizado y las reglas sintácticas de cada instrucción. Pero había dos cosas que pensaba mantener del sistema viejo, al menos por ahora: la gestión de la memoria, y la evaluación de expresiones. Ahora resulta que de pronto me encuentro reescribiendo también al evaluador de expresiones.

jueves, 27 de abril de 2017

El arte de nombrar (parte 2)

En la primera parte hice comentarios mayormente prácticos sobre problemas y sugerencias a la hora de dar nombres a variables, clases, funciones, etc. Ahora vengo a completar las ideas con otro punto de vista, pensando más en la elección de las palabras que en la forma de escribirlas o abreviarlas.

Estas reflexiones tienen dos orígenes: por un lado la introducción del clásico libro de patrones de diseño de "la Pandilla de 4", por el otro una mini-frustración personal que me agarra a veces dando clases.

jueves, 6 de abril de 2017

El arte de nombrar (parte 1)

En general, ponerle nombres a las cosas no suele ser tarea simple. En particular, hablando de programación, nombrar variables, clases, funciones, etc, no constituyen excepciones sino todo lo contrario. ¿Cuál es la verdadera importancia de un buen nombre en el código? ¿Qué haríamos sin "foo" y "bar"?

No cabe duda de que un nombre descriptivo es mejor que uno genérico. Obviamente "vel", "pos" y "acel" son mejores identificadores para una terna de variables que guardan el estado de un cuerpo rídigo que "a", "b" y "c". Pero... ¿alcanza? He aquí algunas consideraciones mayormente prácticas al respecto.

martes, 7 de marzo de 2017

Reescribiendo PSeInt (parte 2.c): complicaciones

Si estuviera haciendo un intérprete para un lenguaje propio y para un uso real, seguramente tendría cuidado de ajustar el lenguaje para que sea simple de parsear. Esto es bueno por mil razones, pero la directa es facilitarle la vida al intérprete y a todas las demás herramientas que surjan junto al lenguaje. Es la clase de cosas que hacen que en un lenguaje como C++ algo tan simple como renombrar un método sea una pesadilla, mientras que en otros lenguajes más nuevos como Java en verdad es simple. En fin, volviendo al pseudocódigo, el pseudolenguaje que usamos no es de los súper simples para este punto de vista, y eso complica un poquito el nuevo diseño del intérprete.

miércoles, 22 de febrero de 2017

Reescribiendo PSeInt (parte 2.b): implementación

En la parte 2.a expliqué la estructura general del proceso de parseo e interpretación, indicando cuales serán las cuatro etapas y sus responsabilidades. En esta entrega, algunos detalles más sobre cómo implemento esto a nivel de clases y patrones de diseño.

viernes, 17 de febrero de 2017

Reescribiendo PSeInt (parte 2.a): arquitectura

Ya introduje en el primer post de esta serie los problemas que busco resolver en esta travesía de reescribir desde cero la parte más crítica de PSeInt. Ahora toca hablar un poco más en detalle de la nueva arquitectura interna de esta "parte".

jueves, 28 de abril de 2016

Registros en PSeInt: Paso 0

Uno de estos días quise empezar con la implementación de registros en PSeInt. Para esto hay que reestructurar algunas cosas, y quiero aprovechar la volteada para arreglar otras. Entre los principales cambios, está la forma representación interna de una variable (tanto de su tipo, como de su contenido). Un cambio así repercute por tooodas partes. Creyendo que ya tenía un panorama completo de lo que buscaba, mucha experiencia de los diseños anteriores, y una buena batería de tests automáticos, empecé a  escribir desde cero nuevas clases superpoderosas para tipos y datos contemplando las infinitas combinaciones (cosas como una referencia a un arreglo de registros con atributos escalares de tipos todavía sin definir). Después de completar el 90% de unos tres diseños diferentes y no poder cerrarlos y/o integrarlos, empecé a sospechar que así no iba a converger nunca.

miércoles, 16 de marzo de 2016

Describiendo los cuadros de diálogo de ZinjaI

Tengo en ZinjaI muchísimos pequeños y no tan pequeños cuadros de configuración. Estos cuadros suelen representar un conjunto bastante lineal de "valores" configurables. En general estos "valores" se pueden reducir a tres o cuatro tipos básicos, y entonces todos los cuadros y paneles se pueden reducir a una lista ordenada de valores con sus tipos, más unas pocas indicaciones adicionales y opcionales sobre el estilo visual o pequeños agregados. Estuve rediseñando todo el código alrededor de la confección de estos cuadros de diálogo para que reorganizarlos, cambiar los estilos, o agregar y quitar elementos sea mucho más simple, rápido, y a prueba de errores.

jueves, 23 de julio de 2015

La escencia de la programación de computadores

El gran Brian Kernighan escribió una vez, hace como mil años (circa 1976), la siguiente frase:

"Controlling complexity is the essence of computer programming."

Una posible traducción sería: "El control de la complejidad es la esencia de la programación de computadoras." Debo haber leído esta cita por primera vez hace al menos 10 años. En su momento me pareció una más. No eran palabras rimbombantes, ni conceptos extraños. Parecía más bien un punto de vista, una opinión, con algo de cierto, algo de exagerado, como tantas otras. Otras frases me impactaron/gustaron mucho más cuando las leí por primera vez. Sin embargo, con esta pasó algo muy especial. Con el tiempo, muuuy lentamente, a medida que cometía mis propios errores e iba aprendiendo con cada uno de ellos a programar un poquito mejor, me fui dando cuenta de que esa no es una frase más, sino una de las verdades más profundas e indiscutibles que podemos enunciar sobre la programación.

viernes, 5 de junio de 2015

std::function: Ya tengo mi (nuevo) martillo

Todos conocen la frase "si solo tienes un martillo, todo te parece un clavo" o alguna variación con la misma idea. A mi, con la programación me pasa algo curioso. Tengo una caja repleta de herramientas de todo tipo, pero cada vez que agrego a la caja un martillo nuevo, todo lo demás se me convierte en clavo. Mi caja está mayormente llena de C++. Diría que casi todo C++03 está en la caja, y que gradualmente he ido agregando herramientas de C++11/14. En algún punto me pasó con eso de SFINAE y los trucos que se podían hacer, y los apliqué por todos lados. En otro momento, agregé a la caja las maravillosas "Variadic Templates" y grité "I have a hammer!", y variadic templates para todos.

Ahora, la funcionalidad de C++11 que se está tornando increíblemente "martillosa", es la aparición de las funciones lambda, y como yapa de la clase auxiliar std::function. Es decir, la posibilidad de crear funciones y clausuras "al vuelo", sin siquiera ponerles tipo ni nombre, y la facilidad que provee la clase std::function para apuntarle a esos pequeños monstruitos sin que nos importen ni preocupen sus especies. En MotoGT 2 están martillando cuanto clavo (y tornillo) se les cruza. Y el resultado es una delicia. Un buen motor puede dar muchas más libertades sin agregar ruido cuando hace uso de estas cosas, y permite al programador ser mucho más productivo.

domingo, 31 de mayo de 2015

Las bases de MotoGT 2: overview

Hace un tiempo les comenté que pienso reescribir MotoGT desde cero. Llamo a este proyecto MotoGT 2. Aunque por fuera, al principio será un clon del primer MotoGT con apenas unos pocos detalles nuevos visibles, por dentro la historia es totalmente diferente. El objetivo es obtener una base de código más sólida con la cual experimentar fácilmente agregando y quitando cosas al juego, para que lo que limite su desarrollo sea mi creatividad y mi poca idea de game-design, y no un código complicado y difícil de modificar o mantener.

Entonces, tengo que escribir un pequeño motor de juego. Lo suficientemente general como para que me provea esa flexibilidad. No tan general como para renegar con cosas que no necesito. Con una interfaz de alto nivel para no lidiar con detalles finos de implementación al concetrarme en la lógica del juego. Con capas o subsistemas desacoplados para que, por ejemplo, la biblioteca que use para gráficos y sonido no me condicione la forma de programar esa lógica del juego. Y más, como para que un imprevisto no tire abajo el proyecto.

martes, 26 de mayo de 2015

Constructores privados y std::shared_ptr

Tuve un problema de diseño/implementación con una clase en particular mientras escribía algunas cosas básicas para el nuevo MotoGT, que me resultó muy interesante por lo simple que es el planteo, y por los caminos rebuscados que puede tomar la respuesta. Básicamente quería garantizar que las instancias de una clase particular solo se pudiesen crear por medio de una función auxiliar. La solución habitual es poner el constructor de la clase como privado/protegido y hacer a la clase amiga de la función. Pero esto no es 100% seguro desde algún punto de vista y, por sobretodo, se complica cuando queremos usar std::shared_ptr y std::make_shared con esa clase. Si no se van, les cuento mejor dónde aparece el problema, para qué sirve, y a qué solución llegué.

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, 5 de mayo de 2014

Interesante problema de Estructuras de Datos

En varios casos en ZinjaI tengo datos que necesito utilizar en dos contextos diferentes, requiriendo en uno el conjunto completo, y en otro solo una parte. Tomemos por ejemplo la lista de breakpoints de un proyecto. Mientras se edita un archivo fuente, la mayoría de las operaciones con breakpoints (agregar, quitar, definir propiedades, etc) se hará sobre los de ese fuente. Además, al guardar esta información en el proyecto, los breakpoints de un fuente se guardan asociados al mismo. Sin embargo, una lista de breakpoints por fuente no siempre es lo mejor porque muchas operaciones en el depurador (a nivel gdb) se harán sobre la lista completa. Por ejemplo, al inicializar la depuración, se deben setear todos los breakpoints en gdb antes de ejecutar, sin importar de dónde provengan. La misma idea aplica a varias otras cosas, como por ejemplo los archivos del proyecto (que están agrupados por categorías) o próximamente las inspecciones (que están asociadas a distintas ventanas/paneles de la interfaz).

Entonces, hace poco me armé un par de clases para representar dos contenedores que operen sobre los mismos elementos. Es decir, habrá un contenedor para el conjunto global, y otro para los subconjuntos parciales. A nivel algoritmos y estructuras de datos creo que es un ejercicio interesante, y por eso vengo a (documentarlo) comentarlo  en este post.