Estoy tratando de agregar algunas opciones a ZinjaI para poder controlar la forma en que ejecuta los programas que compila. Actualmente, gracias a los toolchains alternativos se puede modificar por completo la forma en que se compila un proyecto (utilizando un makefile personalizado, o hasta un script). Ahora quiero poder hacer algo parecido con la ejecución, pero aquí hay ciertos detalles interesantes a tener en cuenta. ¿Para qué serviría esto? Algunos ejemplos son casos donde lo que ZinjaI compila es parte de algo más grande y lo que en realidad hay que ejecutar es ese algo (como los organismos para la comvida por ejemplo). En otros casos hay que correr el ejecutable a través de una herramienta (por ejemplo al utilizar mpi para paralelizar, a través de mpirun). Pero el caso más útil será cuando simplemente se quiera hacer algo antes de la ejecución, como borrar/crear archivos, o definir variables de entorno, para luego ejecutar normalmente. El problema es que hay algunas particularidades de cómo se lanzan nuevos procesos hijos desde un proceso padre en GNU/Linux, y de cómo se gestionan los entornos en los shells que interpretan los scripts, que me han dado más de un dolor de cabeza. Finalmente llegué a una forma muy simple de hacer más o menos lo que quería, forma que no pude encontrar buscando en Google, y por eso vengo a comentarla.
jueves, 26 de septiembre de 2013
jueves, 19 de septiembre de 2013
Getters y setters automágicos en C++
Hablando con un amigo y colega hace unos días, me comentaba que en Haxe, un lenguaje con varias cosas tomadas (aparentemente) de actionscript y javascript, se pueden definir atributos con getters y setters en una linea como esta: "public var(A,B)", donde A y B pueden ser "null" si no queremos que se puedan ver o modificar desde fuera de la clase, "default" si queremos que se pueda como si fueran públicos, "get" o "set" si más abajo queremos implementar un getter o setter especial, y otras variantes. Algo interesante de esto es que desde el programa/función "cliente" de la clase (el que la usa para algo mediante su interfaz pública), el acceso a estos atributos no cambia de un caso a otro, sino que es siempre igual, como si fueran públicos. Pero en realidad se mete en medio (si queremos) de forma transparente el getter/setter propio.
A esta clase de cosas que parecen pero no son simples atributos públicos, muchos lenguajes las llaman propiedades. Inmediatamente me acordé de las clases para representar widgets en las ventanas con Borland Builder, donde podía hacer edit1->text="hola" en lugar de edit1->SetText("hola") sin problemas. Y dije: "esto se puede hacer igual de transparente en C++". En el caso de objetos de una interfaz gráfica, hay varias formas de que parezca transparente, pero mi idea era generalizarlo, y plantear algún mecanismo para lograr esto con cualquier atributo sin meter mucho ruido en el código de la clase.
A esta clase de cosas que parecen pero no son simples atributos públicos, muchos lenguajes las llaman propiedades. Inmediatamente me acordé de las clases para representar widgets en las ventanas con Borland Builder, donde podía hacer edit1->text="hola" en lugar de edit1->SetText("hola") sin problemas. Y dije: "esto se puede hacer igual de transparente en C++". En el caso de objetos de una interfaz gráfica, hay varias formas de que parezca transparente, pero mi idea era generalizarlo, y plantear algún mecanismo para lograr esto con cualquier atributo sin meter mucho ruido en el código de la clase.
lunes, 9 de septiembre de 2013
Sobre la Programación Orientada a Objetos
Me crié (hablando de programación) con Basic, y con uno de los viejos, donde había que ponerle a todo números de lineas y no teníamos subs. Eso hizo que mi forma de pensar un código fuera fuertemente "estructurada". Más tarde, alguien me sugirió QBasic como una forma de hacer lo mismo que hacía en Basic (el mismo código) pero con un editor más bonito. Explorando qué más tenía QBasic encontré las subs (funciones o subrutinas) y eso me hizo un poco más modular. Cuando pasé a VisualBasic, mi forma de pensar no cambió. La poca y bizarra orientación a objetos de los primeros VB pasó totalmente desapercibida para mí, y sólo incorporé el concepto de evento.
Con ese background (que llevó varios años y estaba bien acentado), entré a la universidad y me dispararon directamente con la Programación Orientación a Objetos (POO) y bastante de C++. Además, era el primer año que la materia se daba en C++; los docentes eran muy buenos programadores con Pascal/Delphi, y tuvieron que aprender C++ solo para actualizar la materia. Todo esto y más hicieron que la orientación a objetos parezca algo forzada. Y si bien no la cuestioné de entrada, tampoco la entendí ni aproveché tanto en mis primeros proyectos. Solo para la parte visual parecía realmente tener sentido.
Con ese background (que llevó varios años y estaba bien acentado), entré a la universidad y me dispararon directamente con la Programación Orientación a Objetos (POO) y bastante de C++. Además, era el primer año que la materia se daba en C++; los docentes eran muy buenos programadores con Pascal/Delphi, y tuvieron que aprender C++ solo para actualizar la materia. Todo esto y más hicieron que la orientación a objetos parezca algo forzada. Y si bien no la cuestioné de entrada, tampoco la entendí ni aproveché tanto en mis primeros proyectos. Solo para la parte visual parecía realmente tener sentido.
domingo, 1 de septiembre de 2013
Exprimiendo mejor al Diagrama de Flujo
Con la verificación de sintaxis en tiempo real, una mejor sincronización con la ayuda rápida, las plantillas de estructuras de control de la derecha, y la lista de funciones y operadores de la izquierda, el autocompletado y los tooltips, el indentado inteligente, y algunos otros detalles, creo que la interfaz para escribir un algoritmo en pseudocódigo en PSeInt está bastante bien encaminada y completa. La que no está taaan completa es la de edición de diagramas de flujo, ya que hasta hace poco, si bien se podía editar el diagrama y mandar a ejecutar directamente desde ese editor, tenía varias falencias. Por ejemplo, si había errores de sintaxis, había que volver al pseudocódigo para ver cuales eran y dónde estaban, o si se quería hacer un seguimiento paso a paso también. Estas cosas están cambiando, y en este post les cuento las novedades (a nivel usuario) que ya están listas en el repositorio, y también los cambios de diseño interno (a nivel desarrollador) que tengo que analizar antes de seguir en esta linea.
miércoles, 28 de agosto de 2013
Primeros pasos con git (parte 1)
Lo prometido es deuda, así que vamos a empezar a hacer cosas con git. En este post vamos a crear un repositorio "central" en algún lado, clonarlo en otro, y luego modificarlo y publicar los cambios en el primero, para que después un tercero pueda obtener también esos cambios. En realidad este post no es más que una excusa para documentar en algún
lado lo básico de mi forma de trabajar con git para la próxima vez que empiece un proyecto,
porque ya voy armando una receta y me la suelo olvidar.
Antes de empezar aclaro que considero a un repositorio como "central", al de sourceforge si estamos hablando de alguno de mis proyectos públicos, o a un repositorio en una carpeta especial de mi disco si estamos hablando de uno privado, pero acuérdense que es solo una idea mía, a git le da igual cualquiera. Luego, en otro lugar, en una carpeta de trabajo, hago los cambios y los subo al "central". Más tarde si alguien más quiere ver que hice se baja los cambios, o si yo mismo quiero seguir en otra pc, me los bajo también.
Antes de empezar aclaro que considero a un repositorio como "central", al de sourceforge si estamos hablando de alguno de mis proyectos públicos, o a un repositorio en una carpeta especial de mi disco si estamos hablando de uno privado, pero acuérdense que es solo una idea mía, a git le da igual cualquiera. Luego, en otro lugar, en una carpeta de trabajo, hago los cambios y los subo al "central". Más tarde si alguien más quiere ver que hice se baja los cambios, o si yo mismo quiero seguir en otra pc, me los bajo también.
lunes, 12 de agosto de 2013
Mejor interfaz para los errores en PSeInt
Siempre digo que aprender a programar involucra aprender dos o tres grandes cosas: en un primer escalón los fundamentos (la lógica), luego el lenguaje, y tal vez un poco más abajo las herramientas (el compilador y el IDE). No es fácil aprender todo al mismo tiempo, y creo que sin dudas los fundamentos son lo primero y más importante, son la parte más universal y duradera de este conocimiento. Por eso siempre planteo que para enseñar a programar debemos primero sentar buenas bases en este aspecto tratando de que los otros dos no "molesten" demasiado. Para evitar que el lenguaje "moleste" es que utilizamos el pseudocódigo. Para evitar que la herramienta "moleste" es que, o bien no utilizamos ninguna (pseudocódigo solo en papel como le gustaba a Dijkstra), o bien propongo PSeInt. Es decir, el pseudocódigo debe ser natural y flexible para que los errores que consuman el tiempo sean los de lógica, y la interfaz intuitiva y rápida para que tampoco desvíe la atención, de forma que todo esto se vuelva invisible.
La interfaz de PSeInt arrastra varias decisiones de diseño tomadas por razones "históricas" a veces, técnicas otras, o hasta simplemente por no pensarlas demasiado. En los últimos años he intentado pensarlas más y con un sentido didáctico, pero arrastrando la inercia inicial. Un punto importante y ejemplificador de esto que hablo es la forma en que se muestran los errores de un algoritmo dentro del editor. En este artículo cuento los porqués de algunos cambios que estoy probando para la próxima versión, y los invito a dejar sus opiniones.
La interfaz de PSeInt arrastra varias decisiones de diseño tomadas por razones "históricas" a veces, técnicas otras, o hasta simplemente por no pensarlas demasiado. En los últimos años he intentado pensarlas más y con un sentido didáctico, pero arrastrando la inercia inicial. Un punto importante y ejemplificador de esto que hablo es la forma en que se muestran los errores de un algoritmo dentro del editor. En este artículo cuento los porqués de algunos cambios que estoy probando para la próxima versión, y los invito a dejar sus opiniones.
martes, 6 de agosto de 2013
Primeros pasos con git (parte 0)
Trabajé durante mil años sin usar sistemas de control de versiones (cosas como svn, cvs, git, mercurial, bazaar, etc) ni nada parecido, y durante todo ese tiempo no sentí la necesidad de hacerlo. Principalmente porque trabajaba solo, o en pequeños grupos, entonces el modelo de llevar y traer zips/tgzs fechados alcanzaba como para no complicarse "por demás" con todo un repositorio. Sin embargo, en algún momento quise ofrecer el código de ZinjaI y PSeInt "en tiempo real" (que cada cambio que haga esté inmediatamente disponible para quien lo quiera probar), porque eso es algo que me gusta de muchos proyectos de los que soy usuario, y porque tiene otras ventajas adicionales (como por ejemplo que algunos usuarios puedan ayudar con el testing de forma temprana).
Hasta entonces, como dije, solo había usado los sistemas de control de versiones para clonar repositorios ajenos y nada más. A partir de ese momento, empecé a experimentar un poco con crear repositorios, hacer y publicar cambios, etc. Y tuve que elegir un sistema en particular, y me incliné por git porque era el que estaba de moda, y porque es un producto que nace de la mano del mismísimo Torvalds para gestionar nada menos que El Kernel, así que potencia y flexibilidad estaban garantizadas. Además, su popularidad prometía mucha documentación actualizada, es decir, un fácil y rápido aprendizaje.
Hasta entonces, como dije, solo había usado los sistemas de control de versiones para clonar repositorios ajenos y nada más. A partir de ese momento, empecé a experimentar un poco con crear repositorios, hacer y publicar cambios, etc. Y tuve que elegir un sistema en particular, y me incliné por git porque era el que estaba de moda, y porque es un producto que nace de la mano del mismísimo Torvalds para gestionar nada menos que El Kernel, así que potencia y flexibilidad estaban garantizadas. Además, su popularidad prometía mucha documentación actualizada, es decir, un fácil y rápido aprendizaje.
jueves, 25 de julio de 2013
MinGW y las calling conventions
Ya hace más de 6 meses que no vemos publicada una nueva versión "oficial" de ZinjaI. Es normal que en el primer tercio del año los esfuerzos se dirijan mayormente a PSeInt, y este año se han presentado motivaciones adicionales para que así sea aún en mayor medida y por algo más de tiempo. Sin embargo, el desarrollo de ZinjaI nunca se detiene por completo, y quienes observan de tanto en tanto el repositorio saben que siempre hay algo nuevo, aunque solo sea un bug. Pero esta vez hay una razón más para retrasar el lanzamiento de una nueva versión, y esta es un cambio importante en mingw (el port del compilador gcc a Windows). Siempre lleva tiempo actualizar gcc ya que hay que "armarlo" por partes, compilar algunas bibliotecas que agrego en ZinjaI, y luego acomodar y empaquetar todo, pero esta nueva versión tiene algo especial. En qué consiste este cambio y cómo nos afecta a los usuarios de ZinjaI es lo que voy a tratar de explicar en este post.
miércoles, 10 de julio de 2013
En cajas de 12
Programar me divierte, y mucho. Pero estoy hablando de resolver problemas interesantes, no de hacer ABMs y páginas webs para el negocio de la esquina (no lo tomen a modo despectivo). Me refiero a problemas de optimización, algoritmos y estructuras de datos por ejemplo. Por eso, cuando veo algún contest de programación, intento participar. Un contest de programación es una competencia, donde hay que resolver problemas programando, y gana el que resuelve más problemas, o el que los resuelve en menos tiempo, o el que hace el código más eficiente. Allí suelen aparecer problemas muy interesantes, y suele ser además el pie para después sentarme a aprender algo nuevo (a pelear otra vez con ese problema que no me salió durante la competencia). Un contest de mucho nombre a nivel mundial es el Google Code Jam, y ya se imaginarán por qué. Empecé a participar ahí creo que en 2008, y hasta el día de hoy sigo más o menos igual: cada año avanzo dos o tres rondas, pero nunca llego a las finales, y usualmente ni siquiera estoy cerca. Solo un año me gané una remera, pero ese año el contest no era global, sino limitado a Latinoamérica.
Cuando me inscribí por primera vez en el sistema online para participar, tuve que llenar un formulario con varias preguntas, entre ellas una que decía si me gustaría trabajar en Google. Obviamente Google busca, además de buena publicidad, identificar buenos programadores para reclutar. Como todo geek que se precie de tal dije que sí sin pensarlo porque: a) ¿a quien no le gustaría trabajar en la empresa más importante del mundo en lo que a software se refiere? b) ¿qué importa qué responda, si total está claro que está fuera de mi alcance? Pero todo esto viene al caso, por algo que pasó hace poco.
Cuando me inscribí por primera vez en el sistema online para participar, tuve que llenar un formulario con varias preguntas, entre ellas una que decía si me gustaría trabajar en Google. Obviamente Google busca, además de buena publicidad, identificar buenos programadores para reclutar. Como todo geek que se precie de tal dije que sí sin pensarlo porque: a) ¿a quien no le gustaría trabajar en la empresa más importante del mundo en lo que a software se refiere? b) ¿qué importa qué responda, si total está claro que está fuera de mi alcance? Pero todo esto viene al caso, por algo que pasó hace poco.
martes, 2 de julio de 2013
Herramientas mágicas: CppCheck
En la primer entrega de la serie Herramientas Mágicas les presenté Valgrind, un analizador dinámico. Lo de dinámico es porque analiza al programa en movimiento, mientras se ejecuta. No es el único de este tipo, ya volveremos a eso, pero ahora voy a presentar un analizador estático: CppCheck. Esto quiere decir que, por el contrario, este tipo de analizador no ejecuta al programa en cuestión, sino que se basa solamente en inspeccionar muy minuciosamente su código fuente.
Para hacer una analogía con algo conocido, digamos que un analizador estático es parecido a medio compilador. Sería la parte del compilador que mira el código generando los warnings y errores, no la que traduce. Pero el objetivo del compilador es traducir a código máquina. Dado que para ello tiene que hacer un análisis previo del código fuente, va detectando "de paso" (como efecto colateral) potenciales errores que avisa en forma de warning. Pero solo hace el análisis necesario para traducir y eventualmente optimizar, y no pierde tiempo en otros detalles. Un analizador estático como CppCheck, en cambio, tiene por objetivo el encontrar errores, por lo que el tipo de análisis que hace toma más tiempo, para detectar cosas más específicas o intrincadas.
Para hacer una analogía con algo conocido, digamos que un analizador estático es parecido a medio compilador. Sería la parte del compilador que mira el código generando los warnings y errores, no la que traduce. Pero el objetivo del compilador es traducir a código máquina. Dado que para ello tiene que hacer un análisis previo del código fuente, va detectando "de paso" (como efecto colateral) potenciales errores que avisa en forma de warning. Pero solo hace el análisis necesario para traducir y eventualmente optimizar, y no pierde tiempo en otros detalles. Un analizador estático como CppCheck, en cambio, tiene por objetivo el encontrar errores, por lo que el tipo de análisis que hace toma más tiempo, para detectar cosas más específicas o intrincadas.
miércoles, 5 de junio de 2013
Sobre optimizaciones y precisión numérica
Recibí un correo diciendo que el siguiente código en PSeInt:
a<-rc(13);
Escribir a<a;
evidenciaba un error de lógica, ya que el resultado daba Verdadero, cuando debiera dar Falso (un número no puede ser menor que sí mismo). Pruebo el código en GNU/Linux y da Falso, pero el usuario dijo que usaba Windows. Entonces voy a Windows y efectivamente da Verdadero. Así que abro ZinjaI y me dispongo a Depurar en Windows, pero he aquí la sorpresa: en el depurador da Falso. Cuando se trata de variables sin inicializar, o manejo de direcciones de memoria, no es raro que una versión Debug dé mejor que una Release, pero en este caso estaba seguro que no se trataba de eso. Estaba a punto de ponerme a analizar el código objeto generado en cada caso (o sea, leer ensamblador, o tal vez saltar por la ventana, estaba decidiendo), pero antes noté algo interesante.
a<-rc(13);
Escribir a<a;
evidenciaba un error de lógica, ya que el resultado daba Verdadero, cuando debiera dar Falso (un número no puede ser menor que sí mismo). Pruebo el código en GNU/Linux y da Falso, pero el usuario dijo que usaba Windows. Entonces voy a Windows y efectivamente da Verdadero. Así que abro ZinjaI y me dispongo a Depurar en Windows, pero he aquí la sorpresa: en el depurador da Falso. Cuando se trata de variables sin inicializar, o manejo de direcciones de memoria, no es raro que una versión Debug dé mejor que una Release, pero en este caso estaba seguro que no se trataba de eso. Estaba a punto de ponerme a analizar el código objeto generado en cada caso (o sea, leer ensamblador, o tal vez saltar por la ventana, estaba decidiendo), pero antes noté algo interesante.
jueves, 30 de mayo de 2013
And the winner is....
Como muchos ya habrán notado, PSeInt fue uno de los nueve candidatos a proyecto del mes de junio en SourceForge. Con algún criterio cada semana en SourceForge se eligen los "proyectos destacados de la semana" y se muestran en la portada. Hace poco me entero que estos proyectos se eligen cada semana de entre los que hayan publicado actualizaciones la semana anterior, y no entre todos. Esto explica cómo PSeInt llegó a esa lista en tres oportunidades. A fin de mes, de entre los proyectos de las 4 semanas, se elige un subconjunto para posibles proyectos del mes, y se deja la decisión final al público en general mediante una votación. Para evitar que una persona vote muchas veces, el voto se autentica mediante una cuenta de twitter. Y así funciona. Ahora, habiendo finalizado la votación de mayo, vengo a hacer algunos comentarios y reflexiones acerca de cómo se dieron las cosas.
lunes, 27 de mayo de 2013
Algunos porqués del pseudocódigo
Tengo una eterna discusión (en el buen sentido de la palabra) con varios usuarios de PSeInt acerca de hasta dónde debe llegar este lenguaje. Me han sugerido agregar la posibilidad de leer y escribir archivos, de declarar estructuras o pseudo-clases, de usar tipos de datos más específicos, escribir las palabras claves en inglés, agregar operadores como el de desplazamiento de bits, o hasta en algún caso incorporar una instrucción para enviar y recibir datos por el puerto serie (¿?). El dilema es el de siempre, ¿cuánto de pseudo y cuánto de código? Pareciera que mucho de pseudo significa que no se parece casi nada a un lenguaje real y que tiene en principio poca potencia (digamos que muchas cosas no se pueden hacer); mientras que mucho de código pareciera implicar que será fácil pasar luego a un lenguaje real (será más parecido), y que tal vez se puedan hacer cosas bastante complejas.
lunes, 20 de mayo de 2013
Depuración (parte 3): Controlando la ejecución II
En el post anterior mencioné los mecanismos básicos para controlar la
ejecución del programa: puntos de interrupción, step over y step in. En esta tercera parte voy a completar la idea con algunos más para detener el
programa y una aclaración importante sobre el nivel de granularidad con que se pueden indicar estos puntos de la ejecución en el código fuente. También, les voy a contar sobre algunos otras formas especiales para volver atrás en el tiempo o alterar (casi) arbitrariamente la
ejecución, cosas que en la práctica permiten analizar varias veces un mismo error, o ensayar algunas soluciones antes de codificarlas. La ventaja radica en no tener que reiniciar el programa y llegar nuevamente hasta el punto conflictivo, lo cual puede llevar bastante tiempo o ser tedioso. Pero además, como efecto colateral, esto sentará una base que permitirá hacer algunos trucos para sortear ciertas limitaciones más adelante cuando hablemos de inspección de expresiones y otras acciones similares.
martes, 23 de abril de 2013
Depuración (parte 2): Controlando la ejecución I
Habiendo introducido las nociones básicas en la parte 1, vamos a profundizar sobre los dos aspectos prácticos más importantes: el control de la ejecución y la inspección de los datos en memoria. En esta parte 2 (y la que sigue) voy a hablar del control de la ejecución. Con el control de la ejecución me refiero a cómo definimos cuándo y hasta dónde se ejecuta. Hay que saber que el mecanismo básico es el punto de interrupción, pero cualquier depurador nos provee un conjunto de instrucciones para avanzar de a poco sin tener que explicitar nuevos puntos de interrupción. Por ejemplo, si estoy detenido en cierto punto y quiero que ejecute una sola linea y vuelva a detenerse (ejecución paso a paso), tendría que poner un punto de interrupción en la siguiente línea y decirle luego que continúe ejecutando, y una vez detenido nuevamente quitar ese punto de interrupción. Si bien es un mecanismo válido, los depuradores tienen instrucciones para hacer todo esto junto y de forma transparente, de modo que al usuario el usuario ni se entere de ese punto de interrupción auxiliar. Y así, hay comandos para avanzar por linea o por instrucción (que no es lo mismo), hasta llegar a cierta línea, o hasta salir de cierta función, etc. También hay mecanismos para alterar la ejecución y volver a ejecutar una línea que ya pasamos, y otros trucos útiles. De eso habla este post.
Suscribirse a:
Entradas (Atom)