tag:blogger.com,1999:blog-1649118545758954592024-03-04T12:23:04.592-08:00Cucarachas Racingzaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.comBlogger221125tag:blogger.com,1999:blog-164911854575895459.post-21275281047945451122023-06-01T04:55:00.000-07:002023-06-01T04:55:30.793-07:00¿Y ahora qué sigue?<p>Finalmente pude publicar una actualización de PSeInt después de tanto tiempo. Repasemos en qué situación quedó este proyecto y cuáles serían mis próximos pasos, tanto para PSeInt como para ZinjaI.</p><p> </p><p><span></span></p><a name='more'></a><b>Primeros pasos en ZinjaI</b><br /><br />Habiendo trabajado tanto en PSeInt últimamente, lo lógico sería que ahora me concentré por un tiempo en ZinjaI. Lleva mucho más tiempo desactualizado que PSeInt. Todavía ni publiqué una versión que use wx3 y strings unicode (aunque esto ya "compila" desde hace un par de años). A la par de publicar estos cambios (hay pequeños detalles que pulir antes de dar por finalizada la transición), tengo que actualizar el toolchain para Windows (que es el que más usan los alumnos y está muy desactualizado). El problema de cambiar el toolchain (en este caso será mingw32 por mingw-w64), es que hay que actualizar también todos los complementos, y registrar todo esto en la documentación (ya que es software de terceros, hay que tener cuidado con las licencias por ej).<br /><br />De la mano de esto vendrá además, como pasó con PSeInt, una nueva versión nativa de 64bits para Windows (esto no debería requerir mucho trabajo), y versiones actualizadas para macOS (esto sí probablemente requiera mucho trabajo), ya que la versión actual ya no funciona en este sistema.<br /><br /><b> </b><p></p><p><b>Nuevas funcionalidades en ZinjaI</b><br /><br />Una vez finalizados los cambios que mencioné antes, podré entonces aprovechar la nueva base y empezar a agregar funcionalidad. Por ej: que los fuentes puedan tener cualquier codificación (poder usar por ej UTF8, que se ha vuelto más común, en lugar de ISO8859 como ahora); tratar de poner (un poquito) al día el mecanismo autocompletado; agregar facilidades para cosas útiles como usar los múltiples sanitizers disponibles en los compiladores modernos; mejorar el mecanismo de complementos; explorar las nuevas funcionalidades de scintilla y ver qué se puede hacer/simplificar con eso (por ej, ahora permite editar en múltiples lugares en simultáneo, cosa que antes debía "simular" zinjai; o utilizar las anotaciones para mensajes de error o resultados de herramientas como cppcheck); etc.<br /><br /><br /><b><br />El interior de PSeInt</b><br /><br />Respecto a PSeInt, el gran refactory que quería lograr para tener un diseño mucho mejor y más flexible todavía está por la mitad. El trabajo de cada módulo todavía no es reutilizable como biblioteca, pero el principal (pseint) está un poco más cerca (digamos que ya hice el 50% de ese camino), y como efecto colateral otras partes del código se fueron simplificando también (como psexport). Una vez que tenga esa flexibilidad en el núcleo de PSeInt, tendré que hacer lo propio en la GUI. Todavía hay mucho trabajo en wxPSeInt para poder aprovechar toda la flexibilidad que va a dar el nuevo pseint (por ejemplo, poder variar las palabras reservadas arbitrariamente).<br /><br />Una vez que cada módulo individualmente esté suficientemente "bonito", probablemente intente jubilar el mecanismo de módulos y pasar a integrar las partes como verdaderas bibliotecas en un solo gran ejecutable, para simplificar y optimizar muchísimo la comunicación entre ellas. Con esto el ida y vuelta entre partes será mucho más fluido y seguramente se notará en los detalles.<br /><br /><br /><br /><b>El exterior de PSeInt</b><br /><br />De la mano de los nuevos cambios y la futura flexibilidad, vendrá por ejemplo un mayor poder de personalización de los perfiles. Esto me obligará a rediseñar el cuadro de selección y configuración (al de selección ya tuve que reimplementarlo en la última actualización porque con tantos perfiles ya se volvía muy lento para cargar). Por otro lado, si el lenguaje puede cambiar tanto, la ayuda como está ahora (texto fijo) dejará de tener sentido. Me gustaría que la ayuda se adaptara al perfil, y para eso tengo que poder escribir texto de ayuda condicional y plantillas donde el sistema reemplace las palabras claves (planeo definir un formato simple basado en markdown + un pequeño preprocesador). Y así, muchas cosas de las que sí ve el usuario irán cambiando/mejorando. Hasta podría integrar un mecanismo de traducción de la GUI similar al de ZinjaI para poder traducir PSeInt completo a otros idiomas. Pero para todo esto, todavía falta terminar de ajustar el interior.<br /><br /><br /><br />En resumen, hay mucho por hacer en estos dos proyectos. En el corto plazo, supongo que intentaré completar una actualización de ZinjaI que use la última versión de wxWidgets y ofrezca un compilador y complementos actualizados para Windows. Esa sería la primera prioridad, y de hecho necesitaría tener lo de los complementos listo para el segundo cuatrimestre, ya que en el cursado de Computación Gráfica utilizo complementos nuevos, y me vendrían muy bien algunas funcionalidades recientes de C++ en los códigos de los TPs. Una vez resuelto eso, no se cual de los otros tres grandes grupos de cambios atacaré primero. Probablemente marchen muy de a poco y en paralelo los tres, según ganas, tiempo, estado de ánimo, etc. El tiempo dirá, pero lo importante será no quedarse quieto. </p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com16tag:blogger.com,1999:blog-164911854575895459.post-59918404880992198632023-04-28T04:26:00.000-07:002023-04-28T04:26:08.356-07:00"No me acuerdo" (a.k.a. "estudié por catálogo").<p>"No me acuerdo" es, en el 95% de los casos, una de las peores respuestas que me pueden dar mis alumnos. Y tiene que ver con la forma de encarar el estudio y un problema muy recurrente que vemos en las carreras de ingeniería: lo que voy a llamar "estudiar por catálogo". <br /></p><p><span></span></p><a name='more'></a> <p></p><p>¿Por qué odio esa respuesta? Porque espero transmitirles a los alumnos conceptos fundamentales a partir de los cuales <b>razonar</b> las soluciones a los problemas. Entonces cuando pregunto "¿cómo resolverías tal cosa?", no pregunto <i>si te acordás</i> cómo se resuelve, o <i>qué decía el libro</i> sobre ese problema. De hecho, si me salió una buena pregunta, seguro que ese caso no estaba directamente el libro. Le estoy preguntando al estudiante cómo lo resolvería él, con lo que entendió. Razonando, y justificando a cada paso ese razonamiento. Y no con solo memoria.<br /></p><p>Todo lo contrario a aprender y razonar es memorizar. Uno puede memorizar una solución sin entenderla. Y entonces luego, cuando aparece un problema similar (sea en el parcial, o en la vida real del profesional), probablemente ni lo reconozcan. Porque si memorizaron solamente, quiere decir que no descifraron cual era la esencia del problema y la solución, cuales eran los fundamentos sobre los que se apoya, las ideas importantes que lo caracterizan, o el tipo de razonamiento que me lleva a esa solución. <br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijj5KWBwWV-Yo6NnepKQ5GuGQO7AEoeUtEMLRFtelTkoYuGbHDh4bakTnZkwR-ob3pJvlxoe0NPIuH82vWgi8smjCcyDhDni5Ou8_7Sx60B6VAK-GPAgSQMAbvZyCCoWMx7jmNK_-PJ_GtNE8OazWg1vjn4NAB5hJAwR08D6KfkS2RAnu2ttOdsP1vWg/s640/quico-manzanas.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="640" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijj5KWBwWV-Yo6NnepKQ5GuGQO7AEoeUtEMLRFtelTkoYuGbHDh4bakTnZkwR-ob3pJvlxoe0NPIuH82vWgi8smjCcyDhDni5Ou8_7Sx60B6VAK-GPAgSQMAbvZyCCoWMx7jmNK_-PJ_GtNE8OazWg1vjn4NAB5hJAwR08D6KfkS2RAnu2ttOdsP1vWg/w400-h225/quico-manzanas.png" width="400" /></a></div><div style="text-align: center;"><i><span style="font-size: x-small;">"Yo me lo sabía con manzanas!" (perdón a los lectores que tienen </span></i></div><div style="text-align: center;"><i><span style="font-size: x-small;"> </span></i><i><span style="font-size: x-small;">menos de mil años por las referencias modernas de este post)</span></i></div><p>Entonces, no quiero que estudien de memoria, y en un examen voy a hacer todo lo posible por preguntar de forma tal que se note si lo hicieron o no. Pero cuando alguien se traba y dice "no me acuerdo" está reconociendo abiertamente que estudió de memoria, y, lo que es peor, que por dentro está haciendo un esfuerzo por "recordar" en lugar de "razonar". No se si es peor que lo haya hecho, o que ni se de cuenta de que tiene que disimularlo. Lo mismo pienso cuando empiezan una respuestas con "el libro decía que...". No le pregunté al libro, te pregunto a vos!</p><p> </p><p>Hace ya unos años, en una charla de café con dos colegas de la UNL (Nestor y Leonardo) uno de ellos (creo que fue Leo) tiró la idea del "catálogo" de problemas: Los chicos se aprenden un catálogo de problemas, y en el examen, cuando se les pide algo, buscan en ese catálogo. Y entonces, si les pedimos algo que no está en el catálogo, hacen agua. Aunque se resuelva con los mismo conceptos básicos que muchos otros problemas que sí están. </p><p>Cuando uno estudia de memoria y no comprende del todo, esas relaciones
son difíciles de ver, y no se puede construir nada nuevo desde allí. Ni
hablar de cuando recuerdan mal un paso y se traban completamente, o peor
aún, salen para cualquier lado o justifican de cualquier forma para que
encaje, sin ser autoconscientes de las burradas que pueden estar
diciendo.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV8uCfc1LjiLStYlHLzr745jwd0YPREtUtjlpuxW-sHycGGjN4Oa66wEUNN7eI9cH5SxynuXZANSMJ-tp7NKnffWSaIfWRA2oTXfzBSS6u2-PKSncfZfDb9HKulfd7B6vEG0uhb8zdpXIgQwk-qyVbYdbNSGlEzXsZ6ZuYBL9KKhDbQ2KAscWv1UPkzw/s900/detective_correspondencia.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="https://twitter.com/pbongiovanni/status/1393238221321031682" border="0" data-original-height="506" data-original-width="900" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV8uCfc1LjiLStYlHLzr745jwd0YPREtUtjlpuxW-sHycGGjN4Oa66wEUNN7eI9cH5SxynuXZANSMJ-tp7NKnffWSaIfWRA2oTXfzBSS6u2-PKSncfZfDb9HKulfd7B6vEG0uhb8zdpXIgQwk-qyVbYdbNSGlEzXsZ6ZuYBL9KKhDbQ2KAscWv1UPkzw/w400-h225/detective_correspondencia.jpg" title="https://twitter.com/pbongiovanni/status/1393238221321031682" width="400" /></a></div><p></p><div style="text-align: center;"><span style="font-size: x-small;"><i>Lo de "estudiar por catálogo" suena como lo de "sea detective privado por correspondencia"... </i></span><br /><span style="font-size: x-small;"><i>No tiene nada que ver, pero puede que sea lo primero que se nos va a venir a la cabeza a los más "viejos".</i></span></div><p>Pienso que en ingeniería es mucho más importante saber entender y analizar los problemas, que acordarse de memoria las soluciones del catálogo. En la vida real tendremos Google, los libros de cuando estudiábamos, Wikipedia, ChatGTP, o lo que sea para ira a consultar los detalles. Memorizarlos temporalmente para un parcial no tiene utilidad a largo plazo. Más aún cuando la ingeniería de la vida real no suele tener respuestas cerradas para los problemas interesantes. Siempre hay opciones con pros y contras, y hay que tener "criterio" para sopesarlas, tomar las decisiones de compromiso y encontrar una balance adecuado al caso. Pero para eso hay que encontrar la esencia de cada herramienta o método y aprender a distinguirla entre el ruido en los casos reales.</p><p> <br /></p><div><p></p><p>Es literalmente "perder" el tiempo repetir lo mismo una y otra vez, hacer mecánicamente los mismos ejercicios mil veces, o estudiar cada problema de los exámenes anteriores como si fuera una caso particular de interés. No me canso de decir que veo en mis exámenes muchos alumnos que reprueban a pesar de haber estudiado realmente mucho. A los de la materia de 3er año (Computación Gráfica), siempre les digo que estudian mal, nunca que estudian poco. Y me cuesta corregir ese hábito. Más cuando antes han podido aprobar unas cuantas materias por catálogo (tal vez luego de varios reintentos, pero aprobar al fin).<br /></p><p>¿Entonces está muy mal usar la memoria? No, tampoco dije eso. Dije que
"solo" memoria es muy malo. Pero por supuesto
que un poco sirve para acelerar las cosas. Tampoco tenemos tiempo de volver a
deducir tooodo en un examen, hay atajos (a veces algún que otro paso "mágico" que difícilmente se nos ocurra bajo presión) que tenemos que recordar. Pero
si estudiamos a conciencia seguramente los recordaremos de tanto
haberlos aplicado y analizado (no simplemente "repetido"). Entonces la
memoria (y el catálogo) puede ser una ayuda, un atajo, pero no debe
nunca ser la base, nunca debe ser algo imprescindible.</p></div>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com6tag:blogger.com,1999:blog-164911854575895459.post-10917500482350225812023-04-18T08:04:00.001-07:002023-04-18T08:04:20.343-07:00Un pequeño y raro bug en PSeInt<p>La semana pasada corregí un bug de PSeInt de lo más curioso, por la forma en que se manifestaba, y la causa del mismo. Es un ejemplo de las "sutilezas" de programar orientado a eventos y la inversión del control, y de lo importante de la programación defensiva.</p><p><span></span></p><a name='more'></a> <p></p><p>Todo empieza cuando una alumna me muestra que en su notebook cuando corre un pseudocódigo que genera una división por cero, y luego maximiza la terminal de ejecución, la mitad de las veces el editor de pseudocódigo revienta (se cierra de mala manera). </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix1b0nO2bI07bMwplk-hMPr9oKC4kHfrPJ4OGgE2g4Ye-T5Ip4-rPytv_gOsaaLp4DXIkylegcHvH9MGwppZ3YAoStTNbtE_B0ifsm8Pdw4vAMS3tz2rZEblpYNusxYBAH8lg_kJLvuISh0GtHbttmSVsKjjgaMgZvjCH5Hs-bLj_sHQgjCc2rIrxDTA/s1306/bug-events.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="642" data-original-width="1306" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix1b0nO2bI07bMwplk-hMPr9oKC4kHfrPJ4OGgE2g4Ye-T5Ip4-rPytv_gOsaaLp4DXIkylegcHvH9MGwppZ3YAoStTNbtE_B0ifsm8Pdw4vAMS3tz2rZEblpYNusxYBAH8lg_kJLvuISh0GtHbttmSVsKjjgaMgZvjCH5Hs-bLj_sHQgjCc2rIrxDTA/w400-h196/bug-events.png" width="400" /></a></div><p>Recordemos que los módulos (editor y terminal) son ejecutables separados. Entonces, ¿Cómo es que un evento de uno hace reventar al otro? Y además, un evento que no tiene nada de especial, ni callback asociado; un evento que no debería generar ningún tipo de comunicación entre los módulos. Más aún, solo ocurre si se maximiza la ventana haciendo doble click en la barra de título, pero no si se maximiza con el botón de maximizar. ¿WTF?</p><p> </p><p>Por suerte logré reproducir el problema de forma más o menos consistente con la versión publicada de PSeInt para Windows. Entonces lo primero que hice fue reemplazar el ejecutable del editor por una versión debug para tratar de agarrar el problema en el depurador. Allí me encuentro con que el programa revienta porque recibe a través del canal de comunicación entre ambos módulos (un socket) la orden de seleccionar un instrucción del algoritmo, pero el número de instrucción es basura (muy fuera de rango). </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXw-DBQhYTf5L4bdkLqCGiUQoV-w2qA6bR1WIRptZYjuecxKZCvhBBb3erSJ74ztazb8vOaD1U6lgK4fsB-mpITSfrx7EaKqHe3cUs0R4DQxQr3aDQiGS7oqI5av-K6venH3-8aOyaw30Pq7RyP4PeNpNVR9P0WHjIuq7FB2Y3DHvPOo1q18RsnuxVxg/s608/Programming-progress-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="https://www.agent-x.com.au/comic/programming-progress/" border="0" data-original-height="588" data-original-width="608" height="309" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXw-DBQhYTf5L4bdkLqCGiUQoV-w2qA6bR1WIRptZYjuecxKZCvhBBb3erSJ74ztazb8vOaD1U6lgK4fsB-mpITSfrx7EaKqHe3cUs0R4DQxQr3aDQiGS7oqI5av-K6venH3-8aOyaw30Pq7RyP4PeNpNVR9P0WHjIuq7FB2Y3DHvPOo1q18RsnuxVxg/w320-h309/Programming-progress-2.jpg" title="https://www.agent-x.com.au/comic/programming-progress/" width="320" /></a></div><p>Entonces, la culpa no es del editor, sino de la terminal que envía ese comando. Pero ¿por qué la terminal enviaría ese comando al maximizar? Ese comando se envía en el momento en que el algoritmo genera un error (para que el editor marque la linea del error) o cuando el usuario hace click en una entrada/salida (para que el editor marque el leer/escribir asociado). Nada tiene que ver, en principio, un cambio de tamaño de la ventana.</p><p>Lo siguiente fue entonces depurar la terminal. Para que la terminal envíe estos comandos, debe ejecutarse controlada por el editor, y hay un ida y vuelta de argumentos en la linea de comandos, archivos temporales y alguna info inicial por el socket que es muy difícil de simular artificialmente. Entonces, en lugar de lanzar la terminal en el depurador como normalmente haría, reemplacé el ejecutable de la terminal en la instalación de PSeInt por uno con información de depuración, y el proceso para depurar pasó a ser: iniciar la ejecución del algoritmo (y con ello de l terminal) desde PSeInt (para que arme ese canal de comunicación entre ambos procesos), y luego "adjuntar" el depurador al proceso de esa terminal (para esto sirve lo de "adjuntar" que casi nadie usa).<br /></p><p> </p><p>Con este mecanismo, y luego de muchos intentos, logré llegar al caso en que tras maximizar la ventana, esta enviaba el comando conflictivo. Mirando el backtrace veo que estaba en un evento de "soltar el botón del ratón"; y ahí me cayó la ficha. Lo normal sería que no pueda haber un evento de soltar el botón del ratón si no hubo antes uno de apretarlo. Pero en este caso, cuando se maximiza una ventana haciendo doble click, esta cambia de tamaño en el segundo "apretar", no espera al segundo "soltar". Entonces cuando "suelta" el segundo click, el puntero ya no está en la barra de título, sino dentro de la ventana (porque ya se maximizó), y ahí es cuando tenemos el "soltar" sin el "apretar" previo.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Yac_QNCBdwUQN7DzHysKkFbyo4acFUM7tQY7Easmm-ttZ4cXgYZuQoBjwz_P82VgdZRjwr0m1l_4qM05TLA7hE1DnfZmYaxW4lhvmHwn_swtxBDuo4dx-uM-LNB2q2rI-8DV4qx0jhTA7UI6os6nwcH6e2QsYDoJNkF1YFbJaRP6GnuiHGtymnta7A/s1632/92-root-cause.png" style="margin-left: 1em; margin-right: 1em;"><img alt="https://www.monkeyuser.com/2018/root-cause" border="0" data-original-height="1632" data-original-width="1500" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Yac_QNCBdwUQN7DzHysKkFbyo4acFUM7tQY7Easmm-ttZ4cXgYZuQoBjwz_P82VgdZRjwr0m1l_4qM05TLA7hE1DnfZmYaxW4lhvmHwn_swtxBDuo4dx-uM-LNB2q2rI-8DV4qx0jhTA7UI6os6nwcH6e2QsYDoJNkF1YFbJaRP6GnuiHGtymnta7A/w368-h400/92-root-cause.png" title="https://www.monkeyuser.com/2018/root-cause" width="368" /></a></div><p></p><p>Esta clase de cosas "sutilezas" aparecen frecuentemente al programar en base a eventos. Y puede variar de un sistema operativo a otro (por ej, que en otro se maximize al segundo "soltar"). Ya es sabido que hay que prever que el usuario pueda generar los eventos fuera del orden que queremos. Pero aquí se muestra también que hay que estar preparados para combinaciones que a priori parecen imposibles.</p><p>Si hubiera programado más defensivamente, el evento de soltar botón podría haber estado verificando si efectivamente habíamos pasado antes por el de apretar en lugar de asumirlo (lo que lo hacía trabajar con variables sin inicializar). O podría haber previsto en el editor que llegue un comando erróneo, y validar los números de linea o de instrucción antes de intentar usarlos.</p><p> </p><p>Resumiendo... Conociendo la separación en módulos/ejecutables, y que el evento de maximizar no tiene nada especial programado, pensé que el reporte de error era imposible, que algo había entendido mal. No lo creí hasta que no lo vi, y fue desconcertante. Pero a pesar de ser un error "tonto", es un ejemplo más de cómo se va el tiempo, a veces en "detalles", y la
cantidad de cosas que pueden malir sal cuando se empiezan a combinar los
eventos de formas difíciles de prever. </p><p>Algo muy bueno para cerrar es que en el camino también encontré por qué en la mayoría de los casos no funcionaba la recuperación de los algoritmos luego de reiniciar el editor, y eso también está corregido para la próxima.<br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com1tag:blogger.com,1999:blog-164911854575895459.post-15097301324107283932023-03-22T07:15:00.000-07:002023-03-22T07:15:48.751-07:00El proceso de publicar una versión de PSeInt<p>Últimamente estuve escribiendo sobre los cambios internos de PSeInt. Todavía estoy muy lejos de donde quiero estar en este sentido, pero hice una pausa en un punto razonable como poder sacar una versión. Para actualizar los perfiles, publicar algunas correcciones a bugs u otras mejoras menores, actualizar las opciones de descarga, etc... El mantenimiento que vengo posponiendo hace más de un año. </p><p>Pero aún así, aunque el código nuevo anda, todavía no pude publicarlo. ¿Qué me está frenando?<br /></p><span><a name='more'></a></span><p><b> </b></p><p><b>Los perfiles</b></p><p>Aunque no lo parezca, agregar los perfiles nuevos que van solicitando los usuarios demanda mucho tiempo. Los recibo por correo y rara vez listos para agregar al repositorio. Usualmente tengo que editar la descripción, elegir un nombre que no se pise con los preexistentes, buscar el sitio web de la institución para obtener un ícono (con suerte en algunos casos lo envían) y ajustarle tamaño y formato, registrar los datos en una planilla interna mía y responder el mail, ubicar los archivos donde corresponde en el repo y documentarlo en el changelog. A veces hasta tengo que intercambiar algunos mails adicionales porque me envían los datos de la institución pero no la configuración del lenguaje.<br /></p><p>En fin, digamos que cada perfil me consume de 5 a 10 minutos. Luego de tanto tiempo sin sacar una versión, se me acumularon más de 70 correos solicitando agregar o actualizar perfiles. Son varias horas de trabajo tedioso cargarlos todos. Y no quedaría bien que publique la actualización sin incluir los que llegaron suficientemente a tiempo.</p><p><b> </b></p><p><b>Las herramientas de compilación</b> </p><p>Tengo varios toolchains que uso para generar las versiones para los distintos sistemas. Usualmente, como suelo automatizar la mayor parte del empaqueto con scripts de bash,
generar una release suele ser bastante rápido. Pero esta vez tuve que actualizar todo porque el nuevo código requiere C++17, y los compiladores que usaba eran demasiado viejos (además de que Apple cambió oootra vez su arquitectura de hardware).</p><p> </p><p>En el caso de <b>Windows</b>, usaba el mingw32 que armé para ZinjaI (que se había estancado en gcc6), mediante wine. Ahora planeo lanzar un ZinjaI basado en mingw64 (con 2 versiones, una de 32bits y otra de 64bits). Ya estuve actualizando estos otros scripts que descargan y acomodan estos archivos para ZinjaI.</p><p>Pero un cambio de compilador implica también recompilar todos los complementos (y de paso, actualizar sus versiones). Esa es la parte lenta y tediosa. La mitad de las veces hay que ajustar el script de compilación o hacer algún parche para que funcione (lo cual implica googlear un buen rato para entender el error y encontrar/armar el parche). Y para PSeInt necesito algunos de esos complementos (empezando por el de wxWidgets).</p><p> </p><p>En el caso de <b>MacOS</b> es todavía peor. Uso un compilador cruzado (una versión de gcc o de clang compilada en GNU/Linux pero que genera binarios para MacOS). Obtener la versión adecuada del SDK de macOS, y compilar el compilador implica varios pasos que nunca funciona a la primera. Y siempre pasan cosas "raras" con los SDKs de MacOS (ejemplo más abajo)<i><span style="font-size: x-small;">.</span></i> </p><p>Actualicé todo esto, y ahora puedo compilar binarios para los nuevos procesadores ARM64. Peeero... No los puedo testear, porque no hay máquina virtual para emular eso en una notebook Intel (y entonces ¿cómo se qué estoy distribuyendo?... todavía no tengo respuesta). Solo pude testear la versión alternativa compilada para procesadores Intel en una máquina virtual con un macOS que ya está bastante viejo.<br /></p><p><br /></p><p>En el caso de <b>GNU/Linux</b>, las cosas suelen ir más sobre rieles. Porque es la plataforma que compilo nativa, y la que más conozco como para resolver cualquier problema. Aquí empecé teniendo que cambiar de distribución porque ahora la mayoría ya no dan versiones de 32bits (entre ellas Ubuntu, que es la que estaba usando). Pero armé una VM nueva basada en Linux Mint 19 de 32bits, y pude compilar todo. </p><p>Luego quise probar con Ubuntus 18.04 LTS y 19.10 para los 64bits pero los repos ya no funcionan, y entonces una vez instalado el sistema base no puedo agregar lo que me falta para compilar wx. Creo que no habría problemas si parto de 20.04 LTS, pero trato de evitarlo porque si uso una versión demasiado "reciente" corro el riesgo de que los binarios dependan de algo relativamente nuevo y no les funcione a los usuarios con sistemas más viejos/desactualizados (que son muchos más de los que deberían). Ya veré si puedo hacer andar algo intermedio. </p><p><br /></p><p>Y las versiones cada vez son más: win32, win64, mac-arm64, mac-x86_64 (ya <i>deprequé</i> mac-i686 y mac-ppc), linux32, linux64... y podrían aparecer más, como una para Chromebooks, o una para Rapsberry Pi, si supiera cómo hacerlas y probarlas. Pero entonces también tuve que reestructurar los scripts de compilación del propio PSeInt. Ya no era sostenible tener un makefile por módulo y por plataforma. Mejoré esto, pero como todo, también llevó su tiempo.<br /></p><p>Lo bueno es que aproveché el tener que reconstruir casi todo para documentar el proceso; muchos detalles ahora <a href="https://sourceforge.net/p/pseint/code/ci/pseint2/tree/docs/release-process.md">están aquí</a>.</p><p><br /></p><p><b>Las pequeñas diferencias entre plataformas</b></p><p>Aún en código relativamente básico (que solo usa C++ estándar) me encuentro con problemas al cambiar de compilador. </p><p>Por ejemplo, en macOS, si queremos soportar versiones previas a 10.13*, hay partes muy específicas de la biblioteca estándar que no tendremos
disponibles. Me topé con el problema al usar la función <i>std::get</i> (para extraer un dato de un <i>std::variant</i>).
Encontrar el por qué del problema ya fue un problema, y luego buscar una
corrección que no me obligue a ensuciar todo el código con workarounds
por culpa de una sola plataforma también se llevó su tiempo.<br /><i><span style="font-size: x-small;">*no se si es muy necesario, no estoy familiarizado con el "ecosistema" de macs.</span></i> <br /></p><p>Otro ejemplo apareció al activar C++17 con el nuevo mingw64. Este deriva de una mala práctica que yo tenía cuando empecé a programar: poner el "using namespace std" en headers. Como en C++17 se define un significado para "byte" y Windows en sus propios headers también quiere definir otro significado para "byte", entran en colisión. Nuevamente, entender la raíz del problema lleva un rato de investigar, y después agregar toneladas de "std::"s para solucionarlo otro poquito más.<br /></p><p> </p><p></p><p><b>Todo lo demás</b></p><p>Finalmente, hay que hacer otras cosas como actualizar la ayuda de acuerdo a los cambios, actualizar la imagen de la splash-screen, completar el changelog, correr los antivirus (y rezar para que no salten falsos positivos), subir los archivos, actualizar en el sitio web (la lista de opciones de descarga, los números de versiones, los hashes de los nuevos archivos, agregar una noticia...), etc, etc, etc.</p><p><br /></p><p>Sumando todo esto, la release está requiriéndome muchísimo tiempo. Y recuerden que esto consume de mi "tiempo libre". El trabajo en la Universidad (que es mi verdadero "trabajo") también se complicó últimamente, ni hablar del tiempo que se lleva la bebé, y que finalmente debo destinar otro poco de tiempo a otras cosas, como hacer deporte para no perder la cabeza. Así que por estos motivos la nueva versión viene muy muy demorada. Pero la buena noticia es que ya falta muy poquito, el 90% de lo que mencioné ya está hecho.<br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com8tag:blogger.com,1999:blog-164911854575895459.post-14726126098618516392023-02-03T02:27:00.001-08:002023-02-03T02:27:28.675-08:00Las palabras clave en los fuentes de PSeInt<p>Siguiendo con los cambios internos en PSeInt, me embarqué en uno de los más trabajosos, que tiene que ver con la posibilidad de variar las palabras claves del pseudocódigo. Veamos el problema, la parte resuelta, la parte que falta, y las ventajas que traerá cuando se complete.</p><span><a name='more'></a></span><p><br /></p><p> <b>1. El Problema </b></p><p>El código original de PSeInt estaba pensado para interpretar una única e indiscutible versión del pseudocódigo (reglas y palabras clave), que era la que me enseñaban en la cátedra donde empecé este proyecto. Entonces, por ej, "ESCRIBIR" como constante aparece muchas veces<i> </i>en los fuentes (en varios módulos, y en algunos casos muchas veces en cada uno). Y también con pequeñas variaciones (ej "ESCRIBIR ", con un espacio al final). Si quiero cambiar esa palabra clave, es un problema.<br /></p><p>Pero hay más. No alcanza con buscar todos los lugares con dice ESCRIBIR y analizar si es la constante, si agrega espacio o algo, etc. También aparecen en el código otras constantes como <i>8</i> representando la "longitud de <i>Escribir</i>", o<i> 9</i> representado lo mismo más un carácter adicional (como un espacio o un punto y coma). Imaginar y buscar las constantes numéricas es demasiado trabajo.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu59MpmzuqeCnnWdUS0W7aenznTTUNCgWJQU_uvTVIDav_ZZbMvJsY7JCnCQMWNK7vYF66KPDRMYWsbMgUEXd57n2hoZnUvdhVupOaud9x4rt6Po53qtlAi_LIsyYQyGF7Q-8Sk14T6KK-sbPXSEIU_IqcdWaO-KatuGVBsCvBQRqF08Z_IEbxTuq7zA/s400/random_number.png" style="margin-left: 1em; margin-right: 1em;"><img alt="https://xkcd.com/221/" border="0" data-original-height="144" data-original-width="400" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu59MpmzuqeCnnWdUS0W7aenznTTUNCgWJQU_uvTVIDav_ZZbMvJsY7JCnCQMWNK7vYF66KPDRMYWsbMgUEXd57n2hoZnUvdhVupOaud9x4rt6Po53qtlAi_LIsyYQyGF7Q-8Sk14T6KK-sbPXSEIU_IqcdWaO-KatuGVBsCvBQRqF08Z_IEbxTuq7zA/w320-h115/random_number.png" title="https://xkcd.com/221/" width="320" /></a><span style="font-size: x-small;"><i> <br /></i></span></div><p></p><p style="text-align: center;"><span style="font-size: x-small;"><i>Hay tanto de esto en el código original de PSeInt.</i></span><br /></p><p>Cuando empecé a permitir algunas variaciones (como <i>Mostrar</i> en lugar de <i>Escribir</i>, o <i>Algoritmo</i> en lugar de <i>Proceso</i>), fueron todos casos excepcionales, agregados con <i>ifs</i> ad-hoc, normalmente en la etapa de normalización, donde se detecta la palabra alternativa y se reemplaza por la original. Esto complica un poco más el código porque agrega las alternativas como casos particulares. Y no siempre son tan simples como agregar un <i>or</i> a la condición de un <i>if</i> (<i>palabra=="ESCRIBIR" or palabra=="MOSTRAR"</i>), porque hay veces en que <i>una </i>palabra clave se forma en realidad con más de una palabra (<i>Hasta Que</i>, <i>Fin Algoritmo</i>, Para Cada, etc). Estos casos suelen tener más código raro, y no siempre se resuelven de la misma forma.<br /></p><p>Con todo esto, permitir modificar un poco más las palabras clave, o incluso agregar alguna nueva, es un dolor de cabeza.</p><p><br /></p><p><b>2. La primera mejora</b></p><p>Lo que <a href="https://cucarachasracing.blogspot.com/2023/01/mas-avances-en-la-reconstruccion-de.html">comenté que había hecho</a> para que todos los módulos puedan reaprovechar el primer parseo que hace el módulo principal <i>(pseint</i>), que consistía entre otras cosas en reemplazar el formato de salida intermedio (el pseudocódigo normalizado) por una estructura de datos mucho más específica, sirvió para que las palabras clave ya no se repitan tanto en todos los módulos. En la nueva estructura de datos, cada instrucción ya viene con su tipo identificado, y sus partes cortadas, por lo que los módulos que consumen esta estructura ya no necesitan de las palabras claves.</p><p>Este cambio, que ya está listo hace unas semanas, eliminó la repetición y los números mágicos de módulos como <i>psexport</i>, o <i>psdraw</i>, que eran los más complicados en este sentido. Resta trabajar también sobre <i>wxPSeInt</i>, que como esos cambios no rompían su compilación, no me vi obligado a actualizar todavía. Sacando esto, el problema queda bastante acotado al módulo principal, <i>pseint</i>.<br /></p><p> </p><p><b>3. El trabajo actual</b></p><p>Actualmente estoy en el módulo principal. Los cambios del punto 2 ya lo mejoraron un poco, pero falta bastante. En estos días armé una estructura de datos para representar una palabra clave, para luego hacer que todo análisis de código que busque esa palabra dependa solo de esa estructura. Y ahí se gestiona de forma bastante transparente para el código cliente la posibilidad de palabras alternativas: el cliente compara un string contra el objeto que representa la palabra clave, y la función de comparación se encarga de ver todas las alternativas que guarde el objeto. Y esta función considera también los casos especiales como palabras compuestas por más de una palabra, o cuando una palabra clave es igual al comienzo de otra (ej: <i>Para</i> y <i>Para Cada</i>), y esconde esa complejidad también.<br /></p><p>Con todo esto, el código cliente se simplifica mucho en varios casos. Ya lo implementé en la parte de detectar una instrucción (la primer palabra clave), pero por lo mal que está hecho el resto del análisis, no es tan simple hacerlo para las demás (las que van en medio o al final, como <i>Hacer</i>, <i>Hasta</i>, <i>Con Paso</i>, <i>Sin Saltar</i>, etc). Y en eso estoy ahora.<br /></p><p><br /></p><p><b>4. Las ventajas a futuro</b></p><p>Cuando esto termine, ya no me costará casi nada personalizar las palabras claves o agregar alternativas. Con tanta libertad que se va sumando, voy a tener que reformatear el cuadro de personalización del lenguaje, y el formato del archivo de perfil, pero finalmente cada docente podrá elegir sus palabras, u ordenar las que ya tenemos de forma que el intérprete priorice la de su preferencia (por ej, al generar código en la GUI o desde el editor de diagramas; o hasta en los mensajes de error y en las ayudas).<br /></p><p>Y finalmente esto da pie para empezar a pensar en traducciones a otros lenguajes (pseudocódigo en portugués, francés, o klingon). Este no es un objetivo mio, pero sí me lo han consultado algunos usuarios alguna vez. Si se logra de rebote, bienvenido sea. Faltaría, eso sí, la posibilidad de traducir la GUI además del pseudocódigo, y eso no está implementado ni planeado de momento. Pero si hay suficientes interesados, supongo que en algún punto podría tomar el mecanismo que uso en ZinjaI y adaptarlo.<br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com5tag:blogger.com,1999:blog-164911854575895459.post-16476453101325780942023-01-17T02:48:00.001-08:002023-01-17T02:56:35.044-08:00Sobrecargando funciones según el tipo de retorno<p>En C++, la sobrecarga de funciones consiste en tener dos o más funciones con un mismo nombre pero diferentes parámetros (en tipo y/o en cantidad). Al hacer una llamada, el compilador analiza los argumentos y decide a cuál corresponde invocar. Notar que en esta definición no menciono al tipo/valor de retorno, y es porque no sirve para resolver la sobrecarga (¿qué haríamos por ej con <i>cout<<foo()</i>?). Pero a veces sería útil poder hacerlo. Me encontré con esta pseudo-necesidad en PSeInt, y vengo a comentar una solución que me funcionó muy bien.</p><p> </p><span><a name='more'></a></span><p></p><p>Por completitud, comento que había visto alguna vez otra solución. Que la función retorne un objeto que actúe a modo de proxy entre ella y el destino final del valor de retorno. El objeto debe tener sobrecargados los operadores de conversión a diferentes tipos, y es en esas sobrecargas donde se decide qué hacer. Entonces la función que ve el cliente es siempre la misma, y solo crea el proxy, pero las conversiones (implícitas para el cliente, cuando asigna el valor de retorno a algo) son las que realmente hacen el trabajo diferenciado.</p><p> </p><p>Pero en mi caso quería algo más. Algo como:<br /></p><p> <i>auto &det = instruction.</i><i>getDetails</i><i>();</i></p><p>En el nuevo código de PSeInt tengo una clase <i>Instruction</i> representa una acción o estructura de control genérica, cualquiera. Todas tienen cosas como una linea de código fuente (<i>std::string</i>), y una referencia a dónde estaba esa linea en el programa (un par de <i>int</i>s), y un <i>enum</i> que dice qué instrucción es:</p><p> <i>enum InstructionType { IT_LEER, IT_ESCRIBIR, IT_ASIGNAR</i>, ... };</p><p></p><p>Pero luego <i>Instruction</i> también tienen un <i>std::variant</i> con los detalles de la instrucción en particular. Si es un <i>Leer,</i> hay un vector de variables a leer; si es un <i>Mientras</i> hay una condición y una referencia a la posición del <i>FinMientras</i>; si es un <i>Para</i> hay un un contador, un paso, valores inicial y final, etc. </p><p>El pseudocódigo parseado se guarda en memoria como un <i>std::vector<Instruction></i> y las funciones que lo procesan suelen tener un <i>switch</i> que según el atributo <i>type </i>deciden qué hacer:</p><p></p><p><i> for (Instruction &inst : programa) {<br /> switch (inst.type) {<br /> case IT_LEER:<br /> ...<br /> case IT_ESCRIBIR:<br /> ...</i> </p><p>Y lo que quiero en cada <i>case</i> es obtener los detalles:</p><p><i> case IT_ASIGNAR: {<br /> const auto &det = inst.</i><i>getDetails</i><i>(); // det guarda variable y valor a asignar<br /> memoria->Set(det.variable, evaluarExpresion(det.valor) );<br /> ...</i><br /></p><p>Y es ese <i>getDetails</i> el problema. No puede el método retornar algo de diferente tipo en cada llamada.</p><p> </p><p>Sin embargo, hay un dato importante: cuando le pido los detalles, ya se de qué instrucción se trata. Voy a usar eso para resolver la sobrecarga<i>.</i> La clave es que el valor de <i>type</i> es conocido en tiempo de compilación (y en el fondo es un entero), por lo que se puede usar como argumento de template:</p> <i> case IT_ASIGNAR: {</i><i><br /> const auto &det = </i><i>getDetails</i><i><IT_ASIGNAR>(inst);</i><i><br /> ...</i><p>Entonces <i>getDetails</i> pasa a ser una función libre genérica, wrapper de unos structs genéricos:</p><p><i> template<int IT_ALGO> struct DetailsHelper { }; // el struct</i><i> </i></p><p><i> template<int IT_ALGO> auto &getDetails(Instruccion &inst) { // la función wrapper<br /> _expects(inst.type==IT_ALGO); // verificar precondición (false=bug)<br /> return DetailslHelper<IT_ALGO>::get(inst); // método static del struct especializado<br /> }</i> <br /></p><p>Y las diferentes sobrecargas serán en realidad diferentes especializaciones explícitas de este struct, donde cambia el método estático que el wrapper necesita:<br /></p><i></i><p><i> template<> struct DetailsHelper<IT_LEER> { // especialización explícita<br /> static auto& get(Instruction &inst) { <br /> return std::get<Instruction::ILeer>(inst.details); // acceso al std::variant</i><br /><i> }<br /> };<br /> ...así para todos los posibles IT_ALGO...<br /><br /></i></p><p><i>DetailsHelper</i> debe ser un struct/clase genérica, y no una función, para permitir el truco ¿Recuerdan por ej que <i>std::vector<bool></i> es diferente a cualquier otra especialización de <i>std::vector</i>? Es la misma idea. Pero aquí el argumento del template no es un tipo, sino un valor (esto se puede para enteros). Y por eso la función wrapper, para esconder los structs auxiliares.<br /></p><p>Quedó la "molestia" de tener que decirle a <i>getDetails</i> el valor del enum. Pero eso es en realidad una ventaja, ya que permite verificar que la instrucción pueda darme lo que quiero (el <i>_expects</i>) y deja claro en el código qué espero (para que sea más legible/entendible) sin tener que fijar el tipo de retorno (así la clase <i>Instrucción </i>puede variar más adelante y que a nadie le moleste).</p><p> </p><p>Aunque este post se sale de la linea que venían teniendo los anteriores, me pareció suficientemente interesante y útil como para comentarlo. La versión real se puede ver en el <a href="https://sourceforge.net/p/pseint/code/ci/pseint2/tree/pseint/Instruccion.hpp">repo git</a>.</p><p><br /></p><p><span style="font-size: x-small;">* Sí, ya se que hay una mezcla de idiomas en los nombres. Hay una mezcla de estilos horrible por todo el código.
Cuando vuelva a ser más o menos estable me debería tomar un buen tiempo para definir convenciones coherentes en todo el
proyecto y reformatearlo.</span><br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com3tag:blogger.com,1999:blog-164911854575895459.post-61066726575211477652023-01-10T04:58:00.000-08:002023-01-10T04:58:02.475-08:00Más avances en la reconstrucción de PSeInt<p>Durante las últimas dos semanas pude invertir una buena cantidad de horas en PSeInt, y seguir con la tan postergada <a href="https://cucarachasracing.blogspot.com/2022/09/la-metamorfosis-interna-de-pseint.html">reestructuración interna</a>. De a poquito empieza a tomar forma y a verse una luz al final del túnel.</p><span><a name='more'></a></span><p></p><p></p><p></p><p>Recordemos que hay varios ejecutables, que llamo <a href="http://cucarachasracing.blogspot.com/2012/12/destripando-pseint.html">módulos</a>. El que realmente analiza y ejecuta el pseudocódigo es "pseint". Otros le piden a "pseint" que haga solo la verificación de errores y la normalización del pseudocódigo, y parten de esa salida, como el que exporta a otros lenguajes ("psexport"), o el editor de diagramas ("psdraw"). A estos pedidos los orquesta la GUI ("wxPSeInt"). Por ej, para ver un diagrama, la GUI guarda el pseudocódigo en un temporal, le pide a "pseint" que haga su parte y deje la versión normalizada en otro temporal, y luego a "psdraw" que dibuje lo que hay en ese otro temporal.</p><p style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUsW8fRP_kDpOiL8g1PrUllgcutdLggBJIRX8bOc4rPyZ58Av1-zJICX3t2ejFcxS9VpFfq4wL9lBYbFLyOBq-YoNoIlz9Ws2ASx_RH4qJ4G3RBCn7PnTTeKeaJ4ZUJ0SPGyFeuEA1q6rCFxfONMRD8WthptlGXcI3VhJCnLxw7alZhNA10E9tjS4DGA/s1223/56-refactor-man.png" style="margin-left: 1em; margin-right: 1em;"><img alt="https://www.monkeyuser.com/2017/refactor-man" border="0" data-original-height="1223" data-original-width="1200" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUsW8fRP_kDpOiL8g1PrUllgcutdLggBJIRX8bOc4rPyZ58Av1-zJICX3t2ejFcxS9VpFfq4wL9lBYbFLyOBq-YoNoIlz9Ws2ASx_RH4qJ4G3RBCn7PnTTeKeaJ4ZUJ0SPGyFeuEA1q6rCFxfONMRD8WthptlGXcI3VhJCnLxw7alZhNA10E9tjS4DGA/w393-h400/56-refactor-man.png" title="https://www.monkeyuser.com/2017/refactor-man" width="393" /></a></p><p>Comenté en los <a href="https://cucarachasracing.blogspot.com/2022/09/primeros-cambios-en-el-interior-pseint.html">últimos</a> <a href="https://cucarachasracing.blogspot.com/2022/11/primeros-cambios-en-el-interior-pseint.html">posts</a> que había estado trabajando exclusivamente en el módulo pseint. La idea a futuro es que la mayoría de lo que hace pase a ser parte de una biblioteca compartida por todos los módulos, y los datos de salida sean suficientemente completos y específicos para que no se necesite reparsear nada (es decir, mucho más "predigerido" que un simple pseudocódigo normalizado). Así elimino código y pasos innecesarios.</p><p> </p><p style="text-align: left;">Los cambios internos en "pseint" que hice el año pasado rompieron la comunicación con entre los módulos. Si bien lo ideal sería empezar a usar la nueva "biblioteca", todavía no existe porque faltan más arreglos para que sea posible. Peero... los datos luego de ese 1er análisis sí tienen ya la forma adecuada. Entonces lo que hice fue implementar una serialización temporal muy simple de esos datos, para que los demás módulos partan desde ahí (reemplazar el pseudocódigo normalizado intermedio).<br /></p><p></p>Esto me llevó a modificar mucho "psexport" y "psdraw". Ahora se cumple con el objetivo de simplificar el código (la entrada tiene información que antes había que reconstruir) y evitar múltiples implementaciones del mismo parseo (todos usan una misma función para leer esa entrada). Y los deja bastante más listos para cuando ese primer paso esté finalmente en una biblioteca.<p></p><p>Entre una cosa y otra, se me fueron 2 semanas. Lo peor fue adaptar <a href="http://cucarachasracing.blogspot.com/2014/02/polimorfismo-en-psexport.html">psexport</a>, porque cambió la interfaz base de la clase que exporta y eso modificó (simplificó, pero requirió reescribir buena parte) a todas las herencias, las clases que se encargan específicamente de cada lenguaje de salida. Y ya que estaba, en el proceso pude corregir algunos errores en las traducciones.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzSFz64qQqVZzteM0pdiIzA8SsE9i8EZhSg-lSXURST6ctx6MIKHeSoWvy60aszl4fBsTOaQf1B00Vac5prfD7qC0m_xvuGUivhFdimJ8ricm7DpAG_VEyg22gCG6lJ3ClXS_udknZcC1PsDxRMtcImdNuTLjj8qnqpsP5fYnPGIojtFeFcdusu_QByw/s1600/155-cleanup.png" style="margin-left: 1em; margin-right: 1em;"><img alt="https://www.monkeyuser.com/2019/cleanup" border="0" data-original-height="1600" data-original-width="1600" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzSFz64qQqVZzteM0pdiIzA8SsE9i8EZhSg-lSXURST6ctx6MIKHeSoWvy60aszl4fBsTOaQf1B00Vac5prfD7qC0m_xvuGUivhFdimJ8ricm7DpAG_VEyg22gCG6lJ3ClXS_udknZcC1PsDxRMtcImdNuTLjj8qnqpsP5fYnPGIojtFeFcdusu_QByw/w400-h400/155-cleanup.png" title="https://www.monkeyuser.com/2019/cleanup" width="400" /></a></div><p></p><p>Algo clave, que hizo posible todo este proceso, es contar con <a href="https://cucarachasracing.blogspot.com/2013/03/la-importancia-del-testing.html">tests automatizados</a>. Cada 5 minutos rompo algo sin darme cuenta; pero por suerte tengo un script que corre cientos de algoritmos en pseint y verifica si sus salidas o errores cambian. Algo similar también tengo para probar exportar 10 ejemplos clave con psexport a cada posible lenguaje. Y ahora agregué también casos de prueba para psdraw: un script que toma una lista de ejemplos, genera con psdrawE las imágenes de los diagramas, y las compara con imágenes de referencia para detectar cambios.</p><p></p><p>Entonces, luego de todo este trabajo, pseint va tomando forma, psdraw y psexport empiezan a reaprovechar más código y simplificarse un poco, y los tests automatizados abarcan cada vez más, generando más confianza como para pensar en pasar este código a la rama principal y pronto empezar a publicar versiones con estos cambios.<br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com4tag:blogger.com,1999:blog-164911854575895459.post-29731578369781475052022-11-25T07:26:00.001-08:002022-11-25T07:26:39.371-08:00Primeros cambios en el interior PSeInt (2da parte)<p>Repito el comienzo del <a href="https://cucarachasracing.blogspot.com/2022/09/primeros-cambios-en-el-interior-pseint.html">último post</a>: <i>"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".</i> En aquel me centraba en el primero, en este cuento cómo van los otros dos.</p><span><a name='more'></a></span><p><br /></p><p>Se supone que todos sabemos lo malas que son las variables globales, pero probablemente también sabemos lo cómodas que son a veces. En <i>pseint </i>(el módulo) hay muuucho estado global, empezando por el perfil del lenguaje, las opciones de ejecución, información del control de errores, entradas predefinidas, etc. </p><p>Esto se debe, en parte a que cuando las puse ahí hace más de 10 años no era tan consiente de lo que implicaba, y a que en el diseño inicial el módulo se ejecutaba una sola vez por cada prueba/análisis de un algoritmo. Si ahora quiero colocar esta funcionalidad en una biblioteca para invocarla desde los otros módulos todas las veces que sea necesario, no es bueno tanto estado global, no es claro cómo reiniciarlo entre invocaciones, y no podría tener instancias paralelas (por ej. en el editor cuando hay varios algoritmos abiertos).</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOAxqJgyDucx4ywhSrGJWsMKn0EPEYOxLbvOYdLUY8ttsHr15MFE1y-1SxJDR_WsTisI4L8MOTsF0Dj4eDKhj_-wvYWFjFHUehTf01NUmfPIZTn7Qp1OQA3OoeenoxR2F1GIiZwYa0ieVpJYqMG0TZPD3Y0-ygk8o5DW7ZKSPOsiSa2fZrQTeUulg2LQ/s1632/111-reminiscing.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="https://www.monkeyuser.com/2018/reminiscing/" border="0" data-original-height="1632" data-original-width="1500" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOAxqJgyDucx4ywhSrGJWsMKn0EPEYOxLbvOYdLUY8ttsHr15MFE1y-1SxJDR_WsTisI4L8MOTsF0Dj4eDKhj_-wvYWFjFHUehTf01NUmfPIZTn7Qp1OQA3OoeenoxR2F1GIiZwYa0ieVpJYqMG0TZPD3Y0-ygk8o5DW7ZKSPOsiSa2fZrQTeUulg2LQ/w294-h320/111-reminiscing.gif" width="294" /></a></div><p>Los principales puntos de entrada a lo que podría ser en un futuro esta biblioteca son dos o tres funciones clave, asociadas a las distintas etapas de la interpretación. Si quiero que las funciones tengan acceso a todo lo que necesitan, la gran parte que ahora es global debería convertirse en argumentos. Pero esto es molesto, porque hay muchas cosas (habría que agregar 10 argumentos a cada llamada), y porque estas funciones a su vez usan muchas otras pequeñas funciones auxiliares que tendrían el mismo problema.</p><p>Una solución que me pareció ingeniosa a nivel código (no a nivel diseño orientado a objetos) fue convertir las funciones en métodos de una clase, y hacer que todo lo que antes era global, ahora sea atributo de la clase. De esta forma, el trabajo extra está en invocar a un constructor pasándole toda esa información, pero si los atributos tienen los mismos nombres que tenían las variables globales, las funciones al convertirse en métodos de la clase ni siquiera cambian su código. <br /></p><p>Parecía una idea simple. Intenté armar una clase para cada paso clave del intérprete: una que verifica la sintaxis, otra que ejecuta las instrucciones, otra que evalúa las expresiones, etc. Pero las interdependencias entre esas clases hicieron que se empiece a complicar. Por ejemplo, analizar la sintaxis actualmente requiere evaluar expresiones, para determinar su tipo y verificar el uso correcto de las variables y operadores. Pero el evaluador depende de la ejecución (si la expresión incluye la llamada a una función del usuario por ej). Y entonces la etapa de análisis requiere de la etapa de ejecución, cuando debería ser previa e independiente. </p><p>No me voy a meter en muchos más detalles, pero de estas relaciones circulares extrañas hay varias, y complican bastante la separación en clases y especialmente el uso (no quiero tener que declarar instancias de todas cuando necesite el resultado de solo una).</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggcfSB3A-eSK24U3kDVj2ie52fPe2VVr7jwpKhR78yw1EV_tBTDFWk7FcEOzUcxsmrYW8Yys3wec4YJL__sHjzt60QGjQvZIcu5LYP4086LIr4uEJp1rGf7UNGCEDvGC6-20JG2w0zYLQyo0-JeVD15iXODxCMMAw5WuwIY6act4anL4yTFQt3S30MUQ/s2384/127-end-of-the-line.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="https://www.monkeyuser.com/2019/end-of-the-line" border="0" data-original-height="2384" data-original-width="1500" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggcfSB3A-eSK24U3kDVj2ie52fPe2VVr7jwpKhR78yw1EV_tBTDFWk7FcEOzUcxsmrYW8Yys3wec4YJL__sHjzt60QGjQvZIcu5LYP4086LIr4uEJp1rGf7UNGCEDvGC6-20JG2w0zYLQyo0-JeVD15iXODxCMMAw5WuwIY6act4anL4yTFQt3S30MUQ/w251-h400/127-end-of-the-line.png" width="251" /></a></div><p></p><p>Así que volví a la idea de que todas las funciones reciban el estado que les interesa como parámetro. Pero, para simplificar los prototipos, escondí todo el estado global importante dentro de una clase <i>Runtime</i>, y ese es entonces el único argumento nuevo para casi todo. Cualquier cosa que vaya sacando del ámbito global, si es importante (mucho no lo era, o no ameritaba estar en todos lados), se irá mudando a ese <i>Runtime</i>.</p><p>Y por cosas globales no solo me refiero a variables. Por ejemplo, las funciones para reportar errores también eran globales. Y si quiero utilizar este código luego para mostrar los errores en tiempo real en el editor o en el diagrama, entonces necesito poder cambiar esas funciones, redireccionar los errores. Por esto dentro del <i>Runtime</i> agregué una instancia de una clase <i>ErrorHandler</i>, y todas las funciones globales para reportar errores pasaron a ser métodos virtuales en esta clase. Así, cuando el editor inicialice su propio <i>Runtime</i>, podrá inyectar su propio código para procesar los mensajes de error que se generen. Lo mismo tendré que hacer con otras cosas, como por ej la entrada/salida del usuario en la ejecución.<br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwPbbQU5ZylYNioMKzIhQpAaej7M6FrN1timyP9g7rpWSEIAvnTbEL48JrjycwamJSNIlkzgFGLIbaQyEe6DmAM6y4JdKGAAFY1QoCbGhu6UQosr-VyStjDqueKsk6ufnwRxOpW1hdLMUnkfUHE7cV_WV5bxaPeNThGlJJEd9czShnxCebdXBN4AyvqA/s1600/245-small-delights.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1600" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwPbbQU5ZylYNioMKzIhQpAaej7M6FrN1timyP9g7rpWSEIAvnTbEL48JrjycwamJSNIlkzgFGLIbaQyEe6DmAM6y4JdKGAAFY1QoCbGhu6UQosr-VyStjDqueKsk6ufnwRxOpW1hdLMUnkfUHE7cV_WV5bxaPeNThGlJJEd9czShnxCebdXBN4AyvqA/w320-h320/245-small-delights.png" title="https://www.monkeyuser.com/2022/small-delights" width="320" /></a></div><p>Y en esto estoy actualmente. Todavía trabajando solo en <i>pseint</i>, preparando las cosas para que cada modulo que necesite funcionalidad del mismo deje de tener que reimplementarla o invocarlo como proceso separado, con todo lo que eso implica (aunque no todo es malo), y pase a invocarlo como cualquier función de biblioteca.</p><p><span style="font-size: x-small;"><i>P.D.: No puedo dejar de recomendar esta pag con comics para desarrolladores que acabo de encontrar:<a href="https://www.monkeyuser.com/"> https://www.monkeyuser.com/</a></i></span><br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com8tag:blogger.com,1999:blog-164911854575895459.post-66615229087025203272022-09-29T04:51:00.002-07:002022-09-29T04:51:17.762-07:00Primeros cambios en el interior PSeInt<p><a href="https://cucarachasracing.blogspot.com/2022/09/la-metamorfosis-interna-de-pseint.html">Ya comenté</a> 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é.<br /></p><p><span></span></p><p><span></span></p><p><span></span></p><a name='more'></a> <p></p><p>Todo esto empieza, de los muchos módulos, por el que llamo <i>pseint</i> o "el verdadero intérprete", el que realmente analiza e interpreta el código.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7PnaVUb3VkgRSLC7ibWJ7Vabv6YPjQqGgY5esAf16JYbW46H--gdNb5dFvY3erH4QkDRTi3OtDmDJrRUBn0-TrVYR2VBlFkhK1jaN4NH4xjMsRPIi1cHLKSe4veQa7U7urjVQt0WRY5uavrk-eRZpw-1EGqQ0VNvh8BhlV-a0rghYERPPvRt-kdhupg/s1429/pseint-modulos.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="578" data-original-width="1429" height="161" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7PnaVUb3VkgRSLC7ibWJ7Vabv6YPjQqGgY5esAf16JYbW46H--gdNb5dFvY3erH4QkDRTi3OtDmDJrRUBn0-TrVYR2VBlFkhK1jaN4NH4xjMsRPIi1cHLKSe4veQa7U7urjVQt0WRY5uavrk-eRZpw-1EGqQ0VNvh8BhlV-a0rghYERPPvRt-kdhupg/w400-h161/pseint-modulos.png" width="400" /></a></div><p></p><p style="text-align: center;"><i><span style="font-size: x-small;">Esquema de los diferentes módulos y sus funciones tomado de <a href="https://cucarachasracing.blogspot.com/2012/12/destripando-pseint.html">un post viejo</a><br /> </span></i><i><span style="font-size: x-small;">(falta todo lo relacionado a los ejercicios autocontenidos, que se agregó después).</span></i><br /></p><p>Empecemos por el problema de la repetición de código. En este módulo había mucho código repetido, y que se repite también en otros módulos. <i>Pseint</i> primero analiza que la sintaxis sea correcta, y luego lo ejecuta. Para facilitar ese análisis, primero se <a href="http://cucarachasracing.blogspot.com/2012/10/parseo-y-mayusculizacion-en-pseint.html">normaliza el pseudocódigo</a>, luego se verifica si cada instrucción tiene la forma y las partes que debe tener. El resultado, si no hay errores, es un nuevo código, con un formato mucho más estricto y con ciertas garantías de correctitud, que lo hará mucho más fácil de analizar.</p><p>La etapa de ejecución partía de ese nuevo código, y a medida que lo iba ejecutando iba realizando un segundo análisis para interpretarlo. Ese segundo análisis implicaba averiguar otra vez cosas que ya había averiguado el primero, como qué instrucción hay en cada linea, hasta dónde llega un bucle, o por ej en un Escribir de dónde hasta dónde va cada expresión a mostrar. Entonces, si bien no es código repetido en el sentido de copy-paste, sí ocurre que se vuelven a resolver cosas que ya se habían resuelto antes.<br /></p><p>Ya logré eliminar esta
repetición haciendo que el primer análisis guarde los detalles en estructuras ad-hoc para cada instrucción, en lugar de solo strings normalizados.<br /></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdTfsX2C5yb7UXmReppVlvzBPtZJ5V1kvUj4OWR8LVOQktjEHSwB7m-1ohUVuevf6jWwGdquohk8Cz1Bz1sXWadSd1KFj3sSqnonZ0w3Mlsr6j6j5LG_UEeYJD-Vu8b4-x1vXVNy8gY2_TXPhLWZQQFiAjG7FiPReNIbq9L5S0xqgDRpmHwXFvkNDr6Q/s1042/newparser3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="739" data-original-width="1042" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdTfsX2C5yb7UXmReppVlvzBPtZJ5V1kvUj4OWR8LVOQktjEHSwB7m-1ohUVuevf6jWwGdquohk8Cz1Bz1sXWadSd1KFj3sSqnonZ0w3Mlsr6j6j5LG_UEeYJD-Vu8b4-x1vXVNy8gY2_TXPhLWZQQFiAjG7FiPReNIbq9L5S0xqgDRpmHwXFvkNDr6Q/s320/newparser3.png" width="320" /></a></div><p></p><p style="text-align: center;"> <span style="font-size: x-small;"><i>Imagen tomada de <a href="https://cucarachasracing.blogspot.com/2017/02/reescribiendo-pseint-parte-2a.html">otro post viejo</a>, donde intentaba explicar cómo iba a pensar <br />la nueva arquitectura para una reescritura desde cero que nunca pude completar.<br />Aquí se ven las etapas básicas del intérprete y cómo otros módulos aprovechan cada una.<br /></i></span></p><p>Pero ese segundo análisis está también en otros módulos. Por ej, para exportar a otros lenguajes, <i>pseint</i> verifica la sintaxis y guarda el código normalizado, para que <i>psexport</i> lo tome como entrada. Entonces, para entender ese código normalizado, <i>psexport </i>tiene que hacer más o menos lo mismo que hacía <i>pseint</i> para ejecutar. De forma similar funciona, por ej, <i>psdraw</i>, que también parte del código normalizado.<br /></p><p>Todas estas soluciones diferentes a casi el mismo problema poco se parecen y nada reutilizan unas de las otras. Es obvio que hay que unificarlas. Espero que luego de algunas correcciones más, la de <i>pseint</i> sea una buena base para todas las demás.<br /></p><p><br /></p><p>De esta explicación también se deduce que en <i>pseint</i> hay al menos dos o tres etapas: digamos que normalización, comprobación de sintaxis, y ejecución. Las 2 primeras estaban muy acopladas, pero ya las separé. Y cuanto más siga separando (si las 3 etapas se convierten en 10 mejor), más podré reutilizar en los otros módulos. Por ej, si en una pausa del paso a paso quiero evaluar una inspección, primero tengo que normalizarla.</p><p>De igual forma, las primeras etapas se pueden reutilizar en el editor para marcar los errores en tiempo real, resaltar los bloque lógicos, llenar el panel de variables y funciones, colorear la sintaxis, mejorar el autocompletado, etc. Y estoy mencionando cosas que en alguna medida ya tengo en el editor, pero que casi no comparten código. Mejorar esto también facilitará la introducción de funcionalidades nuevas.<br /></p><p><br /></p><p>Y otra vez se hizo largo el post, así que dejo el detalle de los otros dos problemas para otra ocasión. Pero el primero, al menos en <i>pseint</i>, ya está casi resuelto, y el 2do ya está empezado. Muuuy lentamente, pero esta vez el cambio avanza.</p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com2tag:blogger.com,1999:blog-164911854575895459.post-15929876364657678792022-09-02T09:05:00.004-07:002022-09-02T09:16:07.631-07:00La metamorfosis interna de PSeInt<p>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?</p><p></p><span><a name='more'></a></span><p><br /></p><p>El problema es así:</p><ul style="text-align: left;"><li>Tengo un código base que es horrible, pero funciona para lo que se le pide en este momento, y no es poco lo que se le pide. </li><li>Es tan horrible que sería menos trabajo hacerlo de nuevo desde cero, bien pensado, y aplicando toda la experiencia acumulada, antes que tratar de "corregirlo".</li><li>Pero empezar de cero implica perder de golpe toda la funcionalidad que ya se tiene, y que pase tiempo hasta que se vuelva a recuperar y esté lo suficientemente probado y estable otra vez.</li><li>Y si empiezo de cero, dado que el esfuerzo estaría en el nuevo código, mientras tanto no podría ni valdría la pena trabajar en el viejo, así que durante todo ese tiempo el software no tendría mejoras ni correcciones a ojos de los usuarios.</li><li>Lo bueno sería que pasado ese doloroso proceso, tendría una base mucho más adecuada para que las próximas mejoras salgan mucho más rápido.</li></ul><p>Si midiera el tiempo necesario, seguro que rehacerlo requiere mucho menos que corregirlo. Pero el problema es que requiere todo ese "menos" tiempo de golpe. Es menos, pero tiene que estar concentrado, porque el mientras tanto tiene los problemas que acabo de mencionar. Y por eso no me estaría funcionando. <br /></p><p>Un ejemplo claro de los amagues y las ideas está en este blog en 2018,
cuando comenté los planes para una nueva arquitectura (en estos posts: <a href="https://cucarachasracing.blogspot.com/2017/02/rehaciendo-pseint-parte-1-motivacion.html">p1</a>, <a href="https://cucarachasracing.blogspot.com/2017/02/reescribiendo-pseint-parte-2a.html">p2a</a>, <a href="https://cucarachasracing.blogspot.com/2017/02/reescribiendo-pseint-parte-2b.html">p2b</a>, <a href="https://cucarachasracing.blogspot.com/2017/03/reescribiendo-pseint-parte-2c.html">p2c</a>). También en diciembre del año pasado hice otro intento, escribí bastante código, pero luego <a href="https://cucarachasracing.blogspot.com/2022/08/y-por-que-mas-silencio.html">pasaron cosas</a> y no llegué ni a comentarlo en este blog. Después de tanto tiempo intentando encontrar más tiempo para rehacerlo y
evitando trabajar en el código viejo, es evidente que no resultó.</p><p> </p><p>En cambio, corregir lo que hay lleva más tiempo, es más doloroso en algún sentido, pero me permite ir de a poco, no perder funcionalidad en ningún momento, y poder mientras tanto hacer pequeñas mejoras o correcciones sabiendo que no son 100% descartables. En teoría no es la opción ideal (<a href="https://cucarachasracing.blogspot.com/2012/05/emparchar-vs-reescribir-que-hacer-con.html">ya escribí sobre esta disyuntiva hace muchos años</a>), pero en la práctica hay que saber adaptarse.<br /></p><p>Así que ahora voy por este otro camino, tratando de que ese código se convierta lentamente en una <a href="https://cucarachasracing.blogspot.com/2012/06/autitos-pixeles-y-tarjetas-graficas.html">hermosa cucaracha de carreras (como Mike y Bubsy)</a>, en lugar de sufrir el triste destino de Gregorio Samsa, que es a donde se dirigía. Pero quedará para otro post discutir los detalles y comentar los primeros avances.<br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com3tag:blogger.com,1999:blog-164911854575895459.post-16440667827267976782022-08-02T22:05:00.006-07:002022-08-02T22:14:11.706-07:00¿Y por qué más silencio?<p>En mi post anterior daba un par de excusas para justificar por qué el blog parecía algo abandonado, y prometía tratar de retomar el ritmo. Sin embargo, desde entonces pasó todo lo contrario, y tanto el blog como los proyectos quedaron todavía más abandonados. Hubo una muy buena justificación para esto:</p><span><a name='more'></a></span><p style="text-align: center;"><span style="font-size: x-small;"><i></i></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-size: x-small;"><i><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkMAAzw3WmBPxptsjsIKuk_GdNmM6TyrCuJc8XRpUjtALbmxMnJhX0FxHhpKXMXKYa1Nn1rXafCWowl3y9wwhJgfUAULs83XnKqTFNQumIaQf9mPMnInf75RbovB7Xjg2J_RmElAQkV50vxZVwmIlqCOGAFW85Q68RzGQubDW0jihRNkwh4PaQqFXyIQ/s1280/petu1.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1280" data-original-width="960" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkMAAzw3WmBPxptsjsIKuk_GdNmM6TyrCuJc8XRpUjtALbmxMnJhX0FxHhpKXMXKYa1Nn1rXafCWowl3y9wwhJgfUAULs83XnKqTFNQumIaQf9mPMnInf75RbovB7Xjg2J_RmElAQkV50vxZVwmIlqCOGAFW85Q68RzGQubDW0jihRNkwh4PaQqFXyIQ/s320/petu1.jpeg" width="240" /></a></i></span></div><span style="font-size: x-small;"><i></i></span><p></p><p style="text-align: center;"><span style="font-size: x-small;"><i>Les presento a Antonella Valentina (aunque por razones extrañas le suelo llamar "la Petu").</i></span> <br /></p><p>Resulta que con Naty nos convertimos en padres hace muy poquito. Además, nos tocó transitar un embarazo con algunas complicaciones, así que empecé a disponer de cada vez menos tiempo ya desde antes de que ella naciera. Por todo esto es que casi no me pude dedicar al blog o a los proyectos, y apenas pude mantener a flote lo mínimo de mi trabajo.</p><p>Ahora, ya finalizada la licencia y tratando de retomar el ritmo de trabajo real, voy de a poco intentando descubrir cuál va a ser esta nueva nueva normalidad. Una en la que duermo muy poquito y en cuotas, trabajo de madrugada con los horarios cambiados, dispongo de mucho menos tiempo libre, cambio pañales mientras espero que compile, preparo mamaderas mientras corren los casos de prueba, necesito el triple de café que antes para no suspenderme, y donde según los comentarios alentadores de mis amigos con hijos, la rutina ya no converge nunca. Nada que no le pase a todo padre reciente, supongo.</p><p style="text-align: center;"><i><span style="font-size: x-small;"></span></i></p><div class="separator" style="clear: both; text-align: center;"><i><span style="font-size: x-small;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEKLCom57bDLwiGauR6UJG-3KGnz1i5XV2BJQ9Wfkpi8CFdNKL6VJEQ636-b1NhkYGqkfkQ1aJ5ISvPxU2PedetTvN4dBlGr_R4_5F7_Zqm_rCGzfovFC_lQBCsRnqSm5TsveeVubKubhtaqwaKiC5_L8qs4-0-F3QWwIUtl6ktU_bRm5P2wtQJ1sjew/s1600/cowabunga3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1200" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEKLCom57bDLwiGauR6UJG-3KGnz1i5XV2BJQ9Wfkpi8CFdNKL6VJEQ636-b1NhkYGqkfkQ1aJ5ISvPxU2PedetTvN4dBlGr_R4_5F7_Zqm_rCGzfovFC_lQBCsRnqSm5TsveeVubKubhtaqwaKiC5_L8qs4-0-F3QWwIUtl6ktU_bRm5P2wtQJ1sjew/s320/cowabunga3.png" width="240" /></a></span></i></div><i><span style="font-size: x-small;"></span></i><p></p><p style="text-align: center;"><i><span style="font-size: x-small;">Díganme si la cabecita redonda con sus cachetotes no tiene forma de tortuga</span></i>.<br /></p><p>En fin, este contexto fue lo que trastocó todos mis planes ya desde las vacaciones de verano, cuando se suponía que más tiempo iba a disponer. Si bien casi no avancé con lo prometido (o sí, pero finalmente voy a tirar lo que hice, así que es casi como si nada), sí me sirvió para encontrar una estrategia alternativa que haga que el plan de avance sea más largo y lento, pero más firme y realista. De esta forma, confío en poder ir viendo/publicando el progreso, aunque sea de a poco. </p><p>Espero en breve tener otro ratito para escribir un poquito más y explicar en otro(s) post(s) cosas tan interesantes como cuál será este nuevo enfoque, los por qués del cambio, cómo quedaron los planes para ZinjaI y PSeInt, o cómo saber si la mamadera está demasiado caliente.<br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com9tag:blogger.com,1999:blog-164911854575895459.post-41570201122165772502022-01-17T11:30:00.001-08:002022-01-17T12:38:40.119-08:00¿Y por qué tanto silencio?<div><p>He llegado a la conclusión de que esto de "la nueva normalidad" tiene todo que ver con que últimamente escriba tan poco en el blog. Es obvio que trabajar desde casa le cambia a uno completamente la rutina. Y entre los efectos de este cambio, hay muchas cosas buenas y muchas malas. Para el blog, tocaron malas.</p><p><span></span></p><p><span></span></p><a name='more'></a><p></p><p>Cuando tenía que ir todos los días a la universidad, yo tenía cierta rutina. <a href="http://cucarachasracing.blogspot.com/2015/08/la-zona-del-programador.html">Alguna vez mencioné</a> lo que implica el proceso de puesta en marcha y entrada en calor del cerebro de un programador (inspirado en <a href="https://alexthunder.livejournal.com/309815.html">este excelente artículo</a>). La pandemia rompió eso (entre muuuchas otras cosas más importantes, obvio).</p><p><br /></p><p>Yo vivo a unos 25km de la universidad, lo cual implicaba un pequeño
viajecito de 40min todas las mañanas. Solía despertarme y salir bastante derecho, para desayunar allá, porque así combinaba el tiempo de desayuno con mi proceso de puesta en marcha mental. La cafetería me parecía un lugar muy piola para esto. Es un lugar neutral, ya que no es todavía la oficina, y a la mañana temprano solía tener poca gente, por lo cual el ruido ambiente era el justo como para ver algo de movimiento y una mínima socialización, pero sin que me impida enfrascarme en mis ideas de ser necesario. </p><p>En ese contexto, es donde comenzaba el día lentamente y entrando en calor con actividades que no eran las tediosas y/u obligatorias, sino las que hacía porque quería; como trabajar un poco en cosas nuevas de ZinjaI o PSeInt, o escribir un post para este blog. La mayoría de los posts fueron escritos durante algún desayuno en la cafetería.</p><p>Una idea para un post puede surgir en cualquier momento. Cuando surge, genero un borrador oculto en el blog donde dejo un título, o un par de lineas, para luego acordarme por dónde iba la cosa. Pero, desarrollarla, escribirla, revisarla 10 veces, y todo eso, es algo que hacía otro día (o varios otros) mientras tomaba un café.</p><p>Lo que perdí con la pandemia es ese proceso de booteo. Sigo necesitando un proceso, pero en casa se ejecuta distinto. Me tomo mi café a la mañana, pero ya no hay dos ambientes como para separar ese relax inicial antes del trabajo. De hecho, ya no se separa mucho el trabajo de casi ninguna otra cosa. Estando en casa, puedo hacer cosas como cortar un rato para salir a hacer ejercicio, pasear al perro, cortar el pasto, hacer un trámite, intentar cocinar, etc. Cosas que antes, teniendo 40min entre trabajo y casa eran impensables.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhJ7jRskKYXeBnJlZ33-42i0O5hA_ErODqJeFDCXAJB9Uk6HKMJhuYInzTOYUUT3yNeBcX9-GEghGT3r19LoCd0gG8xFKHaOttR7ee-v8dNgEuFOowgV19EjdPKnt2liLmS-cialu8a83cFnUxcOI-HEKCe2YhosyhnaU4ZrnuBGWVjihIEWx7ePW0UkA=s979" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="737" data-original-width="979" height="301" src="https://blogger.googleusercontent.com/img/a/AVvXsEhJ7jRskKYXeBnJlZ33-42i0O5hA_ErODqJeFDCXAJB9Uk6HKMJhuYInzTOYUUT3yNeBcX9-GEghGT3r19LoCd0gG8xFKHaOttR7ee-v8dNgEuFOowgV19EjdPKnt2liLmS-cialu8a83cFnUxcOI-HEKCe2YhosyhnaU4ZrnuBGWVjihIEWx7ePW0UkA=w400-h301" width="400" /></a></div></div><div style="text-align: center;"><i><span style="font-size: x-small;">Nunca es fácil trabajar desde casa con tantas distracciones. <br /></span></i></div><div style="text-align: center;"><i><span style="font-size: x-small;">(¿Mi primer comic original? Podría ser peor...)</span></i><br /></div><div><p>Entonces, entre las cosas buenas de esta nueva normalidad, está por ejemplo que puedo hacer más ejercicio físico, o que ya no me separo de mi
perro (aunque el hiper-apego que hemos desarrollado no va a ser bueno
para ninguno de los dos cuando me toque volver a lo presencial; Como
siga así, el perro va a ir a dar clases conmigo).</p><p>Pero por otro lado, de alguna manera, este alt+tab mental más frecuente y agresivo que permite el trabajo desde casa, a mi no me ayuda tanto. No soy de los que tienen la mayor disciplina para auto encasillarse en una rutina de horarios de trabajo razonables. Me entrego fácil a ese ida y vuelta; y así el tiempo ya no rinde lo mismo.</p><p>Sumando entonces que cambió ese momento de la mañana que más productivo
le resultaba al blog, y que los "huecos" ahora se pueden llenar con
otras actividades que antes no; el resultado es que se me van acumulando
decenas de borradores con ideas de posts, y casi ninguno encuentra el
tiempo para concretarse. </p><p>Cuando termine de agarrarle la mano a esta situación, seguramente ya vamos a estar en la universidad en persona otra vez*.</p><p><span style="font-size: x-small;">*dado que publiqué esto mucho después de que escribí el primer borrador, ya casi puedo asegurar que la profecía se cumplió.</span><br /></p><p></p><p><br /></p></div>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com13tag:blogger.com,1999:blog-164911854575895459.post-26087764607384847302021-06-10T11:02:00.000-07:002021-06-10T11:02:31.167-07:00El problema de los foros de PSeInt<p>No se qué hacer con los foros de PSeInt, pero tarde o temprano voy a tomar alguna decisión drástica. </p><p></p><p>Al principio de los tiempos, cuando recién empezaba con el proyecto, recibía con alegría cada mensaje individual que alguno de los pocos usuarios se tomaba el trabajo de enviar. Sin importar lo útil, o inútil que fuera el contenido, leía y respondía a cada uno, en cada foro.</p><p>Con el tiempo pasaron dos o tres cosas bastante malas que me llevaron al estado actual: casi no miro los foros, ignoro el 95% de los mensajes. ¿Qué pasó?</p><p><span></span></p><a name='more'></a><p></p><p>Antes de empezar a hablar del contenido, tenemos el tema de la cantidad. En los dos últimos meses, por ejemplo, recibí 990 mensajes!!! No hay forma de que me tome el tiempo de ver y responder cada uno. PSeInt es solo una parte de lo que llega a mi bandeja de entrada. No parece un nro gigante, pero sí es mucho para el tiempo libre (no es mi ocupación principal, ni mi único "hobby") de una sola persona. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-1GhirYsycQDRpSIZVgph_FYY7uy9PkqNTLS1x2-W4n-deID2kIxD5pQLl6NsnxGEq_5MqSLmVtFusPJPDZUjapEgkIcElvvPhz848tZzzlCtBk6cDWApSvl39ZEip2NL_JHb_AaDTnQO/s1284/mails-pseint.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="549" data-original-width="1284" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-1GhirYsycQDRpSIZVgph_FYY7uy9PkqNTLS1x2-W4n-deID2kIxD5pQLl6NsnxGEq_5MqSLmVtFusPJPDZUjapEgkIcElvvPhz848tZzzlCtBk6cDWApSvl39ZEip2NL_JHb_AaDTnQO/w400-h171/mails-pseint.png" width="400" /> </a></div><div class="separator" style="clear: both; text-align: center;"><span style="font-size: x-small;"><i>Captura de pantalla de mi gestor de correos. Marqué como no leídos a los de abril.<br />Y ya que estamos, vean además los asuntos de los mensajes.</i></span><br /></div><br /><p></p><p>Ahora sí, sobre el contenido, hay un primer problema que tiene que ver con que el 99% de los mensajes son pedidos de ayuda para resolver un ejercicio de programación. Algo que no me corresponde; sino que le corresponde al docente de cada uno (los qué sí me corresponden, los de mis "propios" alumnos, me llegarán por el aula virtual, y no por el sitio del PSeInt). Pero el problema se agrava por varios motivos.</p><p>Hay un foro específico para "ayuda con los algoritmos", y para llegar desde la ayuda o el sitio de PSseInt, hay que pasar por una página donde intento advertir claramente para qué es cada foro y cómo preguntar. La evidencia demuestra que muchos usuarios simplemente <strike>se c@#% en</strike> ignora esa organización, y escriben en cualquiera de los 4 foros por igual.</p><p>Para seguir, la mayoría de los asuntos alternan entre "AYUUUDA" (o algo similar, usualmente acompañado de palabras como "URGENTE" o "YA!"), "PSeInt" (u otros títulos igualmente genéricos, poco amables, e inútiles para clasificar el mensaje) y enunciados completos (sí, hay mucha gente que pone todo el enunciado de un ejercicio en el asunto). Sin contar que la mitad GRITA, tanto en el asunto como el contenido; y demuestra en el mensaje no haber hecho el menor esfuerzo por intentar resolverlo primero.</p><p>Hace mucho que ignoro intencionalmente esos mensajes. En su momento <a href="https://cucarachasracing.blogspot.com/2013/03/ayuda-lo-necesito-urgente.html">intenté comunicar mejor lo que esperaba de los mensajes</a>, pero al final solo lo dejé abierto para que los usuarios se respondan entre sí. Porque por suerte hay varios buenos samaritanos que se divierten recorriendo los foros y resolviendo los ejercicios de otros.<br /></p><p> </p><p><a href="https://cucarachasracing.blogspot.com/2012/03/su-consulta-no-nos-molesta.html">Los mensajes que sí me interesan</a> son los que tienen que ver con el desarrollo de PSeInt. Los que intentan reportar errores; o proponer mejoras. Para empezar quedan perdidos entre la gran maraña de gritos de ayuda y resultan difíciles de filtrar. Por ej, otro asunto habitual es "Error nro XX", y en el 90% de los casos el error es del algoritmo/usuario, y no de PSeInt (y no es que PSeInt no los tenga).</p><p></p><p>Para seguir, rara vez los reportes son completos. Por ej, si un algoritmo falla, no debería faltar el algoritmo, la entrada que usaron, y el mensaje de error o la salida que obtuvieron. Si el programa revienta, es importante incluir los pasos para que ocurra, la versión, el sistema operativo, etc. Así hay cientos de cosas que no logro ver claramente a través de mi bola de cristal. Nuevamente, en algún momento intenté comunicar mejor cómo reportar un bug y parece que tampoco funcionó.</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl4MeQWF1Nq9NWlw_MbIP7wSltLry6WtKSsp966cELB1J0HJLp5DKxmA14_d_cA6mVNeSKKdy01Ae4YEJ9LWSzw3kRC0iEwDKQmTWFjhrMhGZPLjAG_D4M-43Tvn_Ayl8sX4h-iG88fOc2/s1495/foros-pseint-errores.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="700" data-original-width="1495" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl4MeQWF1Nq9NWlw_MbIP7wSltLry6WtKSsp966cELB1J0HJLp5DKxmA14_d_cA6mVNeSKKdy01Ae4YEJ9LWSzw3kRC0iEwDKQmTWFjhrMhGZPLjAG_D4M-43Tvn_Ayl8sX4h-iG88fOc2/w400-h188/foros-pseint-errores.png" width="400" /></a></div><p></p><p style="text-align: center;"><span style="font-size: x-small;"><i>El link al foro de errores de la sección "Foros" del sitio,<br />en realidad lleva primero a esta página.</i></span><br /></p><p>Siempre preferí dejar la redacción del bug libre para que la complejidad de un formulario más detallado (y el inglés tal vez) no frenen a los usuarios de hacer un reporte. Por eso elegí los foros (y hasta permití posteos anónimos para ahorrarles el registro); en lugar de usar un issue-tracker o alguna cosa más específica. Creo que fue un exceso de confianza.</p><p><br /></p><p>Y por último, respecto a las sugerencias.... Muchas son repetidas y me canso de responder una y otra vez lo mismo. Y muchas van en una dirección que no es la que quiero que valla PSeInt. Por ej: "estaría bueno agregar una forma de controlar con pseudocódigo una constelación de N drones que tengan montadas cámaras y sables láser". Este tipo de cosas (hacer más completo o potente al lenguaje) implica agregar o complicar la sintaxis. Más funcionalidades es igual a más instrucciones o instrucciones con más variantes. Y un objetivo fundamental del uso del pseudocódigo es reducir la sintaxis necesaria al mínimo para que el alumno se concentre en la lógica, y no en la sintaxis.</p><p>También intenté comunicar mejor las <a href="https://cucarachasracing.blogspot.com/2013/05/algunos-porques-del-pseudocodigo.html">ideas que motivan</a> <a href="https://cucarachasracing.blogspot.com/2012/08/hacia-donde-debe-ir-el-pseudolenguaje.html">el diseño y la evolución</a> del pseudocódigo; responder en detalle <a href="https://cucarachasracing.blogspot.com/2015/08/preguntas-frecuentes-sobre-pseint.html">preguntas frecuentes</a>; <a href="https://cucarachasracing.blogspot.com/2015/08/donde-consigo-un-anti-antivirus.html">explicar</a> <a href="https://cucarachasracing.blogspot.com/2017/09/falsos-posivitos-otra-vez.html">algunos</a> <a href="https://cucarachasracing.blogspot.com/2017/10/sobre-las-advertencias-al-descargar.html">problemas</a> <a href="https://cucarachasracing.blogspot.com/2018/10/beeping-anti-virus.html">insolucionables</a>, etc, etc. Pero sigo recibiendo esos mismos mensajes una y otra vez por usuarios nuevos (y viejos también). Claramente, otra vez fallo en mi comunicación.<br /></p><p><br /></p><p>La conclusión es que los foros así no me sirven para casi nada. Porque casi no los leo, porque llega un volumen de mensajes mucho mayor al que puedo manejar, porque así me es imposible separar la paja del trigo. Y no quiero dar una idea falsa y luego desilusionar a los que tienen aportes valiosos para hacer (¿no hablé mil veces de <a href="https://cucarachasracing.blogspot.com/2012/03/su-consulta-no-nos-molesta.html">lo importante que son los usuarios</a>?). Es una pena, porque hay varios usuarios que escriben cosas realmente útiles. Aún en esta situación, en las últimas dos versiones logré corregir varios errores gracias a estos usuarios. Pero lamentablemente son 1 de cada <i>N</i>(<i>t</i>), siendo <i>N</i>(<i>t</i>) una función no-decreciente.<br /></p><p>Así que redondeando, se necesita un cambio drástico. Cualquier día que esté un
poquito más de mal humor y con algo de tiempo (estimo que para 2030), podría llegar a borrar
los foros completos y empezar de cero, con muchas más restricciones (buh!). No lo he hecho simplemente porque todavía no tengo claro el método para evitar que vuelva a ocurrir lo mismo. Pero lo estoy pensando...</p><p>Quisiera evitar todo esto, pero que siga siendo "fácil" reportar algo. No se si pueda. Escucho ideas en los comentarios.<br /></p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com13tag:blogger.com,1999:blog-164911854575895459.post-44817926570785843792021-01-25T06:56:00.002-08:002021-01-25T07:10:20.962-08:00¿Qué estuve haciendo el 2020? <p></p><p>Como para sacudir un poco el polvo que se acumuló en este blog arranco tarde el año con un post más relacionado a la docencia que a los proyectos de software que le dieron origen. Pero esta vez escribo mirando hacia atrás. Digamos que este 2020 que al fin terminó fue cuanto menos "complicado", y todo lo que pasó derivó en un silencio de radio para este blog, y en casi nulas actualizaciones para esos proyectos.<br /></p><p></p><p><span></span></p><a name='more'></a><p></p><p>La cuarentena, el distanciamiento y todo eso, por desgracia, le impidió o dificultó trabajar a mucha gente. Pero para los informáticos fue todo lo contrario. Buena parte de nuestras actividades ya eran remotas, las otras nos resultaron más fáciles de "remotizar" que al resto por razones obvias, y en muchos casos hasta subió la demanda. Por eso, y otras razones a veces más personales, para varios de nosotros todo esto se sintió como un aumento de trabajo. En lo personal, por el lado del dictado de clases virtuales, sin duda que lo fue, y me resultó agotador. </p><p> </p><p>De pronto hubo que producir nuevo material y nuevas metodologías para reemplazar a las clases que ya no íbamos a poder dar en el aula. No se trata solo de dictar la misma teoría ahora en frente de una cámara. Por un lado, si uno se preocupa por sus clases, intentará sacarle el jugo al nuevo medio y ver qué me permite hacer mejor que en lo presencial. Pero por otro lado, a la hora de hacer práctica, evaluar trabajos y tomar exámenes, la virtualidad sí que complica las cosas. Por ej, ya no puedo pasar banco por banco a ver cómo trabajan con las guías de práctica y eso me quita un feedback importante y me complica la evaluación continua; o no puedo controlar que no se copien durante una evaluación, y eso me obliga a pensar exámenes diferentes, o a agregar instancias orales que llevan muuucho tiempo y cansan; etc.</p><p style="text-align: left;">Por suerte, en dos de las tres materias ya tenía las clases de teoría grabadas en video; por lo que el trabajo se concentró más en las clases práctica y evaluaciones (que es la parte más difícil). En el 1er cuatrimestre, por ej, grabé videos explicando el desarrollo de las soluciones para casi todos los ejercicios de todas las guías de práctica de una materia. Son videos cortos y parecen simples, pero fueron más de 80 videos que preparar, grabar varias veces hasta que salga más o menos bien, limpiar y subir. Un montón de horas que se comieron mi primer cuatrimestre, muchas noches trabajando hasta las 3 o 4 am.</p><p style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU8Yj7YnZUY0EwRQtyba1hSFJjIsvYjDZvg46Zt5PrPJNIRhpOzXdmRMn9CBoyLUQw74f8wNj268aZkcRLi2EbVtTmDRVGducPMaAFYi-yMbYDRwEQrVuRzKsjH6j0HMukL7fC13k9S1UD/s529/therapy1.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="370" data-original-width="529" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU8Yj7YnZUY0EwRQtyba1hSFJjIsvYjDZvg46Zt5PrPJNIRhpOzXdmRMn9CBoyLUQw74f8wNj268aZkcRLi2EbVtTmDRVGducPMaAFYi-yMbYDRwEQrVuRzKsjH6j0HMukL7fC13k9S1UD/w400-h280/therapy1.gif" width="400" /></a><span style="font-size: x-small;"><i> </i></span></p><p style="text-align: center;"><span style="font-size: x-small;"><i>Lo bueno de la cuarentena fue poder compartir más tiempo con </i></span><span style="font-size: x-small;"><i><span style="font-size: x-small;"><i>la familia y </i></span>el perro.</i></span></p><p>Para el 2do cuatrimestre ya no pude grabar tantos videos, tuve 2 materias a la par. Una que puede funcionar más o menos en autopiloto porque no cambia casi nada de un año a otro, y ya teníamos bastante material. Pero otra mucho más difícil, en la que estoy a cargo desde hace relativamente poco, y que por esa razón todavía no terminó de converger. Eso hace que de por sí cambie todos los años, aún en modo presencial, y entonces requiera mucho más tiempo de preparación. Más ahora con este agregado 2020. <br /></p><p></p><p> </p><p>Sumando ambas cosas, volvió a ser un cuatrimestre muy muy agitado, donde me costó mucho llegar a preparar las clases y las evaluaciones. Al final, creo que el dictado salió bastante bien, y que la evaluación fue razonable
para un primer intento virtual. Pero la peor parte se las llevaron las consultas, ahí
necesito un fuerte replanteo porque es donde no llegué a cumplir del
todo.</p><p>Si bien disponemos de plataformas virtuales para las materias (un moodle), por distintos motivos, muchos alumnos envía consultas por correo en lugar de utilizar los foros. Y entonces volumen de correos de el cuatrimestre pasado se hizo mucho más grande que lo habitual, y en muchos casos terminé respondiendo tarde o a veces ni respondiendo. Estuve al borde de declarar la <a href="https://en.wikipedia.org/wiki/Email_bankruptcy">bancarrota</a> más de una vez. No lo hice por principios y exceso de optimismo, pero el resultado no estuvo lejos. <br /></p><div class="separator" style="clear: both; text-align: center;"></div><p> </p><p>Volviendo al hilo principal de este blog, el corolario es que si no llegué con esas consultas, imaginen las cuestiones sobre ZinjaI o PSeInt, que se supone que son proyectos para el tiempo "libre" (¿qué era eso?). Sin embargo, tampoco es que no hice nada de nada. No publiqué nada (es diferente), pero hay cosas hechas o al menos empezadas que van a ir viendo la luz de a poco en este 2021. Por lo pronto, espero publicar nuevas versiones de PSeInt y ZinjaI en lo que resta de enero, pero dejo los detalles para otro post.</p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com6tag:blogger.com,1999:blog-164911854575895459.post-46959155269568010592020-09-28T04:55:00.001-07:002020-09-28T05:14:43.246-07:00Probando otros sabores de GNU/Linux<p>Hace algo más de un mes cambié el disco de mi notebook y tuve que reinstalar todo. Y por primera vez desde que uso GNU/Linux, instalé en mi PC principal algo que no es <a href="http://www.slackware.com/">Slackware</a>. Pasé más de 20 años usando exclusivamente Slackware en mis PCs de trabajo. Probé otras distros en otras PCs o en máquinas virtuales, pero con ninguna había trabajado demasiado en serio. Ahora intenté con <a href="https://getfedora.org/">Fedora</a> 32. Esperaba encontrar problemas durante la transición, y lo hice. Pero no fueron del tipo de problemas que esperaba.</p><span><a name='more'></a></span><p></p><p>Slackware es una distro increíblemente simple. Todo está a una linea de distancia, casi sin intermediarios ni parches raros (todo es <a href="https://en.wikipedia.org/wiki/Vanilla_software">vanilla</a>). Pero eso no es sinónimo de fácil. No hay bonitas interfaces gráficas para instalación y configuración. No hay paquetes precompilados ni gestión de dependencias. Los repos no suelen tener todo lo que uno busca (aunque <a href="https://www.sbopkg.org/docs.php">sbopkg</a> y los repos de <a href="http://www.slackware.com/~alien/">Alien Bob</a> te cambian a vida). Cuando algo falla, las respuesta de google no suelen ser para esa distro. Pero yo a esta altura la conocía de pies a cabeza y ya casi no renegaba porque le sabía todas las mañas. Y además lo tenía muy muy personalizado.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM2puX8RPcgPvUzRarQzn8ayw-jnRFqrEYyqs4FptZUGY4udCWdg88-tcTMS4PcY9kE4pextisZqUHIL7NjS7FGNF2DWx8YYjA17cnyxqkg0bh26gmZJbm7YZ4sufm4xpm9-iekQdY6uKf/s1463/slack71vs14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="935" data-original-width="1463" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM2puX8RPcgPvUzRarQzn8ayw-jnRFqrEYyqs4FptZUGY4udCWdg88-tcTMS4PcY9kE4pextisZqUHIL7NjS7FGNF2DWx8YYjA17cnyxqkg0bh26gmZJbm7YZ4sufm4xpm9-iekQdY6uKf/s320/slack71vs14.png" width="320" /></a></div><div style="text-align: center;"><span style="font-size: x-small; text-align: center;">Mi primer y mi último Slackwares. A la izquierda, versión 7.1, del año 2000; a</span><span style="font-size: x-small; text-align: center;"> la derecha, 14.2, de 2016 (actualizada a <br /></span><span style="font-size: x-small; text-align: center;"><span style="font-size: x-small; text-align: center;">"current" en 2020, se ve</span> igual). Arriba, el instalador. Abajo,el 1er booteo luego de instalado. </span><span style="font-size: x-small; text-align: center;">Cero sorpresas, 100% confiable.</span></div><p></p><p>Quise probar por un tiempo con una distro más popular, algo más orientada a seres humanos convencionales, y no tanto a programadores u otros ejemplares altamente especializados. Me viene bien hacer algo de experiencia con una experiencia de uso más usual (valgan las redundancias), y ver además que tal andan o se ven ZinjaI y PSeint allí. Evité Ubuntu porque a ese ya lo conocía de tanto usar en VMs. Y elegí Fedora porque además hace tiempo que viene de serie con <a href="https://wayland.freedesktop.org/">Wayland</a> en lugar del viejo <a href="https://www.x.org/wiki/">XOrg</a>, y eso era algo con lo que me interesaba experimentar particularmente.<br /><br /><br />Al principio todo parecía fluir sin problemas, pero se empezaron a notar las costuras en muy poco tiempo. Me pasaban cosas como que el gestor de ventanas dejaba de responder al mouse (y a la touchscreen), aunque sin estar "colgado" (sí respondía al teclado). Eso no es un detalle menor. Y estaba en un Fedora recién instalado, sin adicionales, y en un hardware bastante común y no demasiado nuevo. En muchas aplicaciones no me funcionaban correctamente los atajos de teclado (empezando por ZinjaI, PSeInt, y casi cualquier cosa sobre wxWidgets). Al querer personalizar los atajos del sistema, había combinaciones que no me aceptaba porque ya estaban asignadas a otra cosa; pero esa "otra" cosa no estaba en ninguna lista de atajos a configurar (los encontré solo con dconf). Etc...<br /><br />Luego, la filosofía de GNOME y sus criterios es algo que nunca entendí. Cosas como ponerle adrede un delay al Alt+Tab, del que parece que todo el mundo se queja, y no ofrecer una opción simple para sacar ese delay. O que el teclado en pantalla aparezca cada vez que toco la pantalla, aunque tenga un teclado físico. Son cosas muy muy molestas y que solo se arreglan con extensiones. Unas 10 extensiones más tarde, GNOME estaba más o menos como quería... Hasta que una actualización de GNOME deja fuera de servicio alguna extensión clave. Sumando esto a los problemas del párrafo anterior, se terminó de agotar mi paciencia.<br /></p><p><br />Cambié a Xorg (sobre la misma distro y con el mismo escritorio) y la mayoría de los problemas graves desaparecieron. No dejan de sorprenderme esos errores, porque es una distro muy muy usada y que utiliza Wayland+GNOME por defecto desde hace años. Y no le hecho casi nada de culpa a Wayland, sino a la combinación, faltan ajustar varias tuercas.<br /><br />Pero volviendo a la filosofía del escritorio de GNOME, las diferencias son insalvables. No permite configurar cosas que para mí son básicas, y sí cosas que no veo a quién le importe. Todo lo contrario a dos escritorios que siempre me gustaron por tener justo todo lo que necesito y nada más: <a href="https://www.xfce.org/">xfce</a> y <a href="https://en.wikipedia.org/wiki/Cinnamon_(desktop_environment)">Cinnamon</a>. Eran los que usaba en Slackware desde que KDE se volvió monstruosamente grande (desde los días de kde3). Instalé Cinnamon en Fedora, y usándolo sobre XOrg en lugar de Wayland, ahora sí todo parece fluir sin problemas. Tanto que no terminé de configurar el Slackware que quedó a la par como era mi plan original (además no tuve tiempo), sino que estoy usando exclusivamente este Fedora a diario y ya no me quejo.</p><p> </p><p>No voy a negar que es muy lindo y fácil acostumbrarse a tener todo en los repos, a los paquetes binarios (no tener que compilar cada cosa extra), y la gestión de dependencias automática. Así que por el momento voy a seguir con Fedora, en mi nueva configuración, y dejaré el escritorio por defecto para testear PSeInt, ZinjaI y mis otras herramientas cada tanto. Me sirvió por ejemplo para notar que el editor de diagramas de flujo no funcionaba en Wayland, y eso ya está emparchado para la próxima release de PSeInt. O que si se instala wxFormBuilder mediante <a href="https://flatpak.org/">flatpack</a>, no es nada fácil configurar su integración en ZinjaI. Y así, varios "detalles" que hay que mejorar.<br /><br />Me falta terminar de configurar cosas en mi entorno de desarrollo para que pueda volver a automatizar las compilaciones para todas las plataformas de PSeInt y ZinjaI, y por eso se han demorado los lanzamientos. El toolchain para MacOS (que es el más complicado) ya está listo, me faltan Windows y las dos variantes de GNU/Linux (aunque estas van en VMs, así que salen derecho). Y de paso estoy pensando en agregar alguna opción sobre ARM (para Raspberry Pi y Chromebooks). Veremos qué más aporta con el tiempo esta variedad, y si consigo finalmente superar mi adición a Slackware.</p>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com1tag:blogger.com,1999:blog-164911854575895459.post-87777067986747884332020-07-29T05:40:00.000-07:002020-07-29T05:40:22.707-07:00Sin comentarios<div><div>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.</div><div><br /></div></div><span><a name='more'></a></span><div>Hay dos o tres aspectos importantes que deben quedar claros sobre un algoritmo para luego poder depurarlo, mantenerlo, extenderlo, etc. <i>¿Qué hace? ¿Cómo lo hace? y ¿Por qué lo hace así?</i> Los comentarios están ahí para decir o aclarar lo que el código no puede... Pero la verdadera pregunta del post es: <i>¿estás seguro que el código no puede?<br /></i></div><div><br /></div><div><br /></div><div>Por ejemplo, si en una parte de un programa hay que buscar el mayor de un vector de promedios, un alumno de primer año podría escribir algo como:</div><div></div><div><span style="font-family: "courier";"> double m = 0;</span></div><div><span style="font-family: "courier";"> for (size_t i=0; i<v.size(); ++i)</span></div><div><span style="font-family: "courier";"> if (v[i]>m)</span></div><div><span style="font-family: "courier";"> m = v[i];</span><br /></div><div>Ya se, no es una buena solución, pero iteremos.... Mirando solo este código, no es tan difícil saber qué hace, y qué representan las variables como <span style="font-family: "courier";">i</span> o <span style="font-family: "courier";">m</span>. Pero si esto está enredado en medio de un algoritmo más grande, puede no ser tan trivial y tomar un poquito más de esfuerzo individualizarlo. Entonces, ¿agregamos un comentario?</div><div><br /></div><div>NO! En lugar de agregar antes del código un comentario que diga "<span style="font-family: "courier";">// buscar el mayor elemento de v</span>" o al lado de la declaración de <span style="font-family: "courier";">m</span> uno "<span style="font-family: "courier";">// m guarda el promedio máximo</span>"; es mucho mejor envolver ese código en una función y reemplazarlo algo parecido a:</div><div><span style="font-family: "courier";"> double mayor_prom = buscar_mayor(v);</span><br /></div><div>¿Ven como ya no se necesitan comentarios? El código ya dice todo, no hay mucho que agregar (solo faltaría renombra <span style="font-family: "courier";">v</span>). Mucho mejor aún sería usar directamente <span style="font-family: "courier";">std::max_element</span>, pero solo para dar pie a otros ejemplos, sigamos con nuestra función.<br /></div><div><br /></div><div>Ahora discutamos un detalle menor que se pueden evitar: el indice <span style="font-family: "courier";">i</span>. El <span style="font-family: "courier";">for</span> de esta función es para recorrer todos los elementos, y me importan solamente los valores, pero no sus posiciones. Entonces, creo que el <span style="font-family: "courier";">for</span> basado en rangos es mejor, porque me evita (o esconde) esa variable auxiliar que no es más que un detalle de implementación (o un mal necesario) de la recorrida:</div><div><span style="font-family: "courier";"> double m = 0;</span></div><div><span style="font-family: "courier";"> for(doble x : v)</span></div><div><span style="font-family: "courier";"> if (x>m) m = x;</span><br /></div><div>Así queda claro que la posición no me importa, y no tengo ni que pensar en cómo denominar al contador, o cuáles son sus límites, porque simplemente ya no existe. Y hasta podría reemplazar <font face="courier">x</font> por algo más descriptivo como <span style="font-family: "courier";">una_nota</span>. Se pueden simplificar muchos algoritmos con este <span style="font-family: "courier";">for</span>, o con funciones como <span style="font-family: "courier";">std::for_each</span>, <span style="font-family: "courier";">std::transform</span> y <span style="font-family: "courier";">std::accumulate</span>.</div><div><br /></div><div>Vamos ahora a casos donde la función falla: cuando los valores son negativos. Para que funcione con negativos, hay que cambiar la inicialización de <span style="font-family: "courier";">m</span>. Una solución puede ser:</div><div><span style="font-family: "courier";"> double m = std::numeric_limits<double>::lowest();</span></div><div>Y ojo aquí, que existe <span style="font-family: "courier";">std::numeric_limits::min()</span> pero no es lo que queremos (min es el más cercano a cero, no el más negativo). En casos así (exagerando un poco), donde hay una función que tal vez por su nombre parezca la que naturalmente va, pero no lo sea, es válido agregar un comentario para aclarar el detalle que no se ve y el por qué de la elección. <br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLWkFWPRolCCdYGnjd35zHb90pmhnbyjrNASq0UaA8Uj0MOXjx0flJnmzhO-Vi_hH_YzxNiOrVcYzlUR9bacwSTJySgxP3b7hYDaYdua83EZY62qyVIlx4MCy0UNDAiu1F8iUEb3_uU9_i/s1009/boa.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="637" data-original-width="1009" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLWkFWPRolCCdYGnjd35zHb90pmhnbyjrNASq0UaA8Uj0MOXjx0flJnmzhO-Vi_hH_YzxNiOrVcYzlUR9bacwSTJySgxP3b7hYDaYdua83EZY62qyVIlx4MCy0UNDAiu1F8iUEb3_uU9_i/s320/boa.png" width="320" /></a></div><div><br /></div><div>Volviendo entonces a las 3 preguntas iniciales: <i>¿Qué hace?</i> se responde en el nombre de la función y su prototipo, <i>¿Cómo lo hace?</i> es lo que debe mostrar con claridad el código de su implementación, y <i>¿Por qué lo hace así?</i> (y no de otra forma tal vez más obvia) es el detalle invisible que probablemente valga la pena comentar.</div><div><br /></div><div>Ahora, para preparar el último ejemplo, generalicemos la función (<span style="font-family: "courier";">template</span>) así podemos reutilizarla para vectores de otras cosas. Si las otras cosas no son números, no vamos a poder resolver la inicialización de <span style="font-family: "courier";">m</span> con <span style="font-family: "courier";">std::numeric_limits</span>. Una solución alternativa es usar el 1er elemento del vector. ¿Pero qué pasa si el vector está vacío?</div><div><br /></div><div>Uno podría inferir del prototipo de la función:</div><div><span style="font-family: "courier";"> template <typename T> <br /></span></div><div><span style="font-family: "courier";"> T buscar_mayor(const std::vector<T> &v);</span><br /><div>que no sirve para vectores vacíos, pues el valor de retorno no admite esa posibilidad (como sí lo haría un iterador o un puntero por ej). Pero supongamos que fuera una función donde no es tan obvio. ¿Qué hacemos? ¿Agregamos un comentario a la función que diga "<span style="font-family: "courier";">// el argumento v no puede estar vacío</span>"? <br /></div><div><br /></div><div>Mejor aún, podemos buscarle la vuelta para documentar este requisito de la función de forma que el compilador lo entienda y lo verifique (recuerdo a Stroustroup diciendo <i>"el compilador no lee los comentarios, y yo tampoco"</i>). Yo uso un par de macros en espíritu similares a las <a href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-pre"><span style="font-family: "courier";">Expects</span></a> y <a href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-post"><span style="font-family: "courier";">Ensure</span></a> de las <a href="https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md">CppCoreGuidelines</a> para esto:<br /></div></div><span style="font-family: "courier";"> Expects(!v.empty());</span><div><span style="font-family: "courier";"> T m = v[0];</span><br /></div><div>Entonces, cuando esa condición no se cumpla, en lugar de entrar en los reinos tenebrosos del <a href="https://en.cppreference.com/w/cpp/language/ub">comportamiento indefinido</a> (donde la más tenebroso es que nadie note nada), el programa reventará y el depurador nos marcará la precondición exacta que no se cumplió. Sería todavía mejor ver esto en el prototipo de la función (antes que en el interior) y hacia allí van los <a href="https://www.modernescpp.com/index.php/c-core-guidelines-a-detour-to-contracts">contracts</a> (C++23?), pero de momento me las arreglo con estas macros (buh! macros), y creo que son una de las mejores costumbre que adquirí en los último años.</div><div><br /></div><div><br /></div><div></div><div>Vale aclarar para finalizar que de ninguna manera quiere decir que no debe haber documentación. Mi proyecto no tiene casi comentarios en el código, pero sí hay varias páginas describiendo la arquitectura general, los patrones de diseño utilizados y justificando su elección, la división de responsabilidades entre clases, los puntos que se dejan abiertos previendo extensiones, los mecanismos de desacople entre las diferente partes del sistema, etc. Aspectos generales que hay que tener en claro para que luego el detalle en el código pueda resultar más obvio, o para que uno sepa dónde buscar entre tantos cientos de archivos, clases y funciones.<br /></div><div><br /></div><div><div>En conclusión, cuando tengan que agregar un montón de comentarios a
un código porque no se entiende, piensen primero si no habría una mejor
forma de escribirlo para que sí se entienda. Esto empieza con conceptos
y lineamientos relativamente simples. Cosas como seleccionar los
nombres a conciencia y seguir reglas de estilo; respetar principios como
el de única responsabilidad y único nivel de abstracción al diseñar
funciones; o usar más la biblioteca estándar. Pero en proyectos grandes
es donde se torna desafiante y lleva mucho programar encontrarle la
vuelta. <br /></div><div><br /></div></div><div>Parece fácil hablar de esto con ejemplos simples y aislados de pocas
lineas como los que mostré, pero la generalización no lo es; y obliga a
reescribir una y otra vez el mismo código hasta eliminar toda la
complejidad accidental y converger a una implementación suficientemente clara. Ya lo dijo el gran Antoine de Saint-Exupéri: <i>"La perfección no se alcanza cuando no hay nada más que añadir, sino cuando no hay nada más que quitar"</i>. Creo que estaba reescribiendo un algoritmo cuando dijo eso.</div>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com7tag:blogger.com,1999:blog-164911854575895459.post-9292253288987004422020-07-08T07:51:00.001-07:002020-07-08T07:51:52.624-07:00Embeber recursos dentro de un ejecutable en GNU/Linux<div>Trabajando en un nuevo proyecto que tiene que ver con visualización y usa OpenGL me encontré con el problema de cómo distribuir los shaders. Los shaders son, para este caso, simplemente archivos que el ejecutable necesita encontrar. Mi problema es que quiero un ejecutable que no requiera de otros archivos, porque eso puede generarme algunos inconvenientes. <br /></div><div><br /></div><div>La pregunta es entonces ¿cómo embeber recursos en un ejecutable en GNU/Linux? ¿Cómo hago que esos archivos extra estén dentro del ejecutable, y cómo los recupero desde mi código C++? La respuesta es simple, nada de otro mundo; pero creo que poco conocida. No
es la primera vez que tengo este problema, pero en las anteriores no
sabía ni había podido encontrar la respuesta.<br /></div><div><br /></div><div><span><a name='more'></a></span><div><br /></div><div><div><b>El problema</b><br /></div><div><br /></div>En mi aplicación quiero que el ejecutable sea autosuficiente y no deba cargar otros archivos, principalmente para poder invocarlo desde cualquier lado. Si no, si el ejecutable busca recursos en su directorio, al invocarlo desde otro lado hay que tomar recaudos. Y esto de invocarlo desde otro lado es muy frecuente si estamos acostumbrados a trabajar en consola. En cualquier directorio donde tengo un archivo a visualizar, quiero poder escribir "bfr <el_archivo>" y listo (bfr es el ejecutable). Además, quiero que si durante la ejecución cambia el directorio de trabajo, siga encontrando todo.</div><div><br /></div><div>Hay dos soluciones habituales: que el ejecutable determine su ubicación absoluta al inicio, y a partir de ahí fije todas las rutas de recursos relativas a esa ubicación; o que los recursos estén en una ruta fija y conocida en el sistema. Lo primero no es imposible, pero no es trivial. No alcanza con analizar los argumentos del main (argv[0] en particular) para saber la ruta del ejecutable. Por ejemplo, si se invocó a través de un enlace simbólico, encontraremos la ruta del enlace, no del ejecutable. O si el directorio estaba en la variable PATH, tampoco lo vamos a encontrar. Lo segundo es lo habitual en GNU/Linux al instalar un programa: que las cosas estén por ejemplo en "/usr/local/share/foo/...". Pero esto obliga a instalar (y entonces requiere permisos y solo puede haber una versión), o a definir de alguna manera una ruta alternativa en tiempo de compilación, etc.<br /></div><div><br /></div><div><br /></div><div><b>La solución</b><br /></div><br /></div><div>En Windows es normal definir un archivo de recursos (un .rc), que se compila con un paso especial (con mingw, la herramienta es windres) y se enlaza al ejecutable. Es lo que hace ZinjaI, por ejemplo, cuando definimos un ícono para el exe, o un manifest.xml para los proyectos con wxWidgets. Sin embargo, no conocía hasta hace poco alternativa en GNU/Linux. Recientemente me cruzé con <a href="https://stackoverflow.com/questions/4864866/c-c-with-gcc-statically-add-resource-files-to-executable-library">una respuesta en stackoverflow</a>, y ahora surgió la ocasión para ponerla en práctica.</div><div><br /></div><div>El funcionamiento de la solución es realmente muy simple: primero se genera una biblioteca que tiene dentro el archivo como si fuera una constante (como en un xpm, imaginen "const uint_8 el_archivo[] = ...tooodo el archivo...;"), y luego se enlaza al ejecutable. Pero la gracia está en que no hay que convertir al archivo a embeber en un código fuente (como con <a href="https://linux.die.net/man/1/xxd">xxd</a>) para luego compilarlo, sino que la herramienta <a href="https://linux.die.net/man/1/objcopy">objcopy</a> puede generar directamente el equivalente ya "compilado". Y luego solo hay que saber qué símbolos se definen ahí dentro para poder referirse a ese contenido desde el programa cliente.</div><div><br /></div><div>Supongamos que quiero embeber los archivos "foo.vert" y "foo.frag". Primero, genero un objeto para cada uno con "objcopy" (creo que "<a href="https://linux.die.net/man/1/ld">ld</a>" también podría hacer este mismo trabajo), y luego junto ambos objetos en una biblioteca estática con "<a href="https://linux.die.net/man/1/ar">ar</a>". Esa biblioteca se podrá enlazar luego al ejecutable:</div><div><br /></div><div><font size="2"><span style="font-family: "courier";"> objcopy --input binary --output elf64-x86-64 \</span></font></div><div><font size="2"><span style="font-family: "courier";"> --binary-architecture i386 foo.vert foo_vert.o<br /></span></font></div><div><div><font size="2"><span style="font-family: "courier";"> objcopy --input binary --output elf64-x86-64 \</span></font></div><div><font size="2"><span style="font-family: "courier";"> --binary-architecture i386 foo.frag foo_frag.o</span></font></div><div><font size="2"><span style="font-family: "courier";"> ar -rcs foo.a foo_frag.o foo_frag.o</span></font></div><div><br /></div><div>Y con eso tenemos el resultado en "foo.a". Si fuese para 32bits, hay que cambiar el "--output elf64-x86-64" por "--output elf32-i386". En cualquier caso, en ZinjaI podemos esconder esto en algunos pasos de compilación personalizados (o si son muchos archivos como en mi caso, un solo paso que invoque a un script de bash).</div><div><br /></div><div>Ahora, la parte de C++. En este caso, voy a convertir esa info a dos strings porque era lo que necesitaba en mi aplicación (porque cada shader es un código fuente, un archivo de texto):</div><div><br /></div><font size="2"><span style="font-family: "courier";"> extern const char </span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">_binary_foo_frag_start</span></font>[];<br /> extern</span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";"> const</span></font> </span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">char</span></font> </span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">_binary_foo_frag_end</span></font>[];</span></font></div><div><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";"> std::string foo_frag(</span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";"></span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">_binary_foo_frag_start,</span></font></span></font></span></font><font size="2"><span style="font-family: "courier";"></span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">_binary_foo_frag_end);</span></font></span></font></span></font><div><font size="2"><span style="font-family: "courier";"><br /></span></font></div></div><div><font size="2"><span style="font-family: "courier";"> extern</span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";"> const</span></font> </span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">char</span></font> _binary_foo_vert_start[];<br /> extern</span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";"> const</span></font> </span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">char</span></font> _binary_foo_vert_end[];<br /></span></font></div><div><font size="2"><span style="font-family: "courier";"> std::string foo_vert(</span></font><font size="2"><span style="font-family: "courier";"><font size="2"><span style="font-family: "courier";">_binary_foo_vert_start</span></font>,</span></font><font size="2"><span style="font-family: "courier";">_binary_foo_vert_end);</span></font></div><div><br /></div><div>Hay que respetar los nombres de los punteros. Por cada archivo embebido, tenemos una etiqueta "_binary_[nombre]_start" que apunta al comienzo de su contenido, y otra "_binary_[nombre]_end" que apunta al final de su contenido. A estos nombres los fija objcopy. Para el tipo, lo más genérico es<font size="2"> </font><font size="2"><span style="font-family: "courier";">uint_8</span></font>, pero en este caso se que es solo texto ascii. La respuesta original de stackoverflow usaba inline assembler, aparentemente para renombrarlos, pero parece no ser necesario. Mejor evitarlo, el inline asm no es estándar, y entonces aquella solución compilaba con gcc pero no con clang; mientras a este ejemplo lo probé con ambos y funciona sin problemas.<br /></div><div><br /></div><div><br /></div><div>En conclusión, comparto este detalle porque aunque una vez conocido es simple, me costó encontrarlo la primera vez. Así de paso también me queda este post a mi como documentación del truco para cuando lo vuelva a necesitar.<br /></div><div><br /></div>zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com1tag:blogger.com,1999:blog-164911854575895459.post-5623312764956088262020-06-11T11:12:00.000-07:002020-06-11T11:12:50.621-07:00Actualización de PSeInt: las mejoras que se ven<div>Ya escribí en lineas <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos_11.html">generales</a> sobre los <a href="https://cucarachasracing.blogspot.com/2020/05/finalmente-el-paso-wx3-y-unicode-en.html">problemas</a> y las soluciones de la actualización de wx en PSeInt y ZinaI. Ahora seguramente vendrán cada tanto posts con detalles de cosas más particulares. Nuevas funcionalidades, o nuevos problemas específicos de alguno de los dos que valga la pena comentar. En este, el detrás de escena de las dos diferencias más notorias en la última versión de PSeInt: las anotaciones, y los operadores unicode.<br /></div><div><br /></div><span><a name='more'></a></span><div><br /></div><div><b>Anotaciones en PSeInt</b><br /></div><div><br /></div><div>Scintilla es el componente que muestra y permite editar el código fuente en PSeInt y en ZinjaI (y también en muchos otros programas, como en notepad++ por ejemplo). Wx ofrece un wrapper sobre scintilla. Al actualizar wx, recibo dentro las actualizaciones de scintilla. Y desde la época de wx 2.8, son muchas. Una de ellas es la disponibilidad de "anotaciones". Las anotaciones son textos adicionales que pueden asociarse a las líneas del código, para que scintilla las muestre de forma especial. Un uso obvio, y es el que hago en PSeInt, son los mensajes de error (o cualquier diagnóstico en general):</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGPbmsM_iZQ3jGO8RiJdJbG1FIaChNebiDw5q1roBqEiF20YeHBd9nkyVBzmKRjsNqtGmg0r0lJSC-Mu8AFImII-aGs3E-NBmQRDYl_VeswRVRzzaxOfQHGYR6ay_yleQqFw5AHvzGTb5h/s950/annotations_new1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="490" data-original-width="950" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGPbmsM_iZQ3jGO8RiJdJbG1FIaChNebiDw5q1roBqEiF20YeHBd9nkyVBzmKRjsNqtGmg0r0lJSC-Mu8AFImII-aGs3E-NBmQRDYl_VeswRVRzzaxOfQHGYR6ay_yleQqFw5AHvzGTb5h/w400-h206/annotations_new1.png" width="400" /></a></div><div></div><div><br /></div><div>Lo había comentado <a href="https://cucarachasracing.blogspot.com/2016/03/el-paso-wxwidgets-3x.html">hace muuucho</a> cuando empecé a jugar con wx3. Es un detalle que se ve muy bien en la captura de pantalla; pero ahora que se hizo habitual, es algo molesto durante la edición. Durante la edición, a mitad de escribir una linea o un estructura de control, seguramente el código incompleto no es correcto. Si tardamos en completarlo, PSeInt analiza lo que va por la mitad y comienza a señalar los errores. Si los errores se intercalan con el código, esto hace que el código salte de lugar todo el tiempo; porque las líneas incompletas se separan para dar lugar a los errores, y luego se juntan nuevamente al completarlas (corregirlos). Eso es molesto. Por eso ahora hay un ícono en la barra de herramientas para activar/desactivar las anotaciones:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZsBLcURID3Js0qvFHuWEHbiUQ4SLUNELS9wDeabQLtFbaMVTJjcnmjef2fK9F7wULKJiFRQ98m_0eVw9lyCI_2aJJfyaMtYkRPo-acziG80-hs1YP2D2xyDrMKvfiSJO0ZdHH-D4higDG/s750/annotations_new2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="323" data-original-width="750" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZsBLcURID3Js0qvFHuWEHbiUQ4SLUNELS9wDeabQLtFbaMVTJjcnmjef2fK9F7wULKJiFRQ98m_0eVw9lyCI_2aJJfyaMtYkRPo-acziG80-hs1YP2D2xyDrMKvfiSJO0ZdHH-D4higDG/w400-h172/annotations_new2.png" width="400" /></a></div><div><br /></div><div></div><div>Con las anotaciones desactivadas, los lugares con errores se siguen señalando (el subrayado rojo o amarillo), pero los mensajes no aparecen.<br /></div><div><br /></div><div><br /></div><div><b>Caracteres Unicode en PSeInt</b></div><div><b><br /></b></div><div>Otra ventaja del cambio es que ahora puedo utilizar caracteres unicode. Allí hay todo tipo de símbolos, muchos de los cuales representan mejor a ciertos operadores que la combinación original. Por ejemplo, están los símbolos matemáticos para distinto, mayor o igual, menor o igual, conjunción, disyunción, etc. Hasta hay flechas reales para usar para la asignación. La nueva versión de PSeInt aprovecha esto, y va reemplazando al vuelo los operadores que ingresamos como antes por estos nuevos caracteres:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb5fbetTGsnm9E23ddyrItDKIy1_IgtE2RKEUY-CgDuXi9qHoNb1c5b09hDElcDrfWrc4taKFfwQr44pIoH9w5sUttuxE7qcpjeU6CAxMnfdcfoLJfXYXQBTivNo47Kn3u0_166IqBODi4/s534/unicode-demo1.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="387" data-original-width="534" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb5fbetTGsnm9E23ddyrItDKIy1_IgtE2RKEUY-CgDuXi9qHoNb1c5b09hDElcDrfWrc4taKFfwQr44pIoH9w5sUttuxE7qcpjeU6CAxMnfdcfoLJfXYXQBTivNo47Kn3u0_166IqBODi4/w400-h290/unicode-demo1.gif" width="400" /></a></div><div></div><div><br /><b></b></div><div>Visualmente, el resultado final es muy bonito. Y el cambio solo se hace al momento de mostrar el código en scintilla. Pero los archivos se guardan como antes, si el código se copia al portapapeles se copia como antes, si va de un módulo a otro de PSeInt también va como antes. Así que implica estar atento a todos los posibles escenarios que requieran la conversión. Cubrí los que se me ocurrieron, los más habituales, pero seguramente con el uso iremos encontrando algunos casos en donde falte y habrá que ir corrigiendo.</div><div><br /></div><div>El problema más grande fue que cómo se ven los caracteres unicode varía mucho de una fuente a otra. Por ej la flecha a la izquierda común, en muchas fuentes es muy chiquita, y hasta en algunas parece estar ubicada más abajo de lo esperado (tirando a subíndice). Entonces busqué otro símbolo unicode, una flecha adicional más grande que se veía mejor con mi fuente. Pero cuando voy a Windows veo que ese caracter adicional ni siquiera está en la fuente por defecto (en esos casos se ve un cuadradito genérico).</div><div><br /></div><div>Como no podía encontrar un común denominador que funcionara bien, terminé cayendo en otra nueva posibilidad de wx: cargar fuentes que no sean las del sistema. Así que busqué una linda fuente con caracteres adecuados para los operadores que quería (<a href="https://raph.levien.com/type/myfonts/inconsolata.html">Inconsolata, de Raph Levien</a>), y la incluí en PSeInt. Así que de rebote ahora todas las instalaciones, al menos configuradas por defecto, van a usar la misma fuente. La excepción es MacOS, ahí no está disponible la funcionalidad de wx para agregar fuentes; pero por suerte MacOS trae por defecto una fuente muy adecuada (Monaco).</div><div><br /></div><div>Sigue siendo posible cambiar luego la configuración de la fuente, y hasta desactivar los operadores unicode. Pero creo que nadie lo hará, parece que la nueva configuración por defecto funciona muy bien. Además, generé la textura para usar la misma fuente (<a href="https://cucarachasracing.blogspot.com/2019/11/renderizado-de-texto-en-el-editor-de.html">casi</a>) en el editor de diagramas, y también configuré la misma por defecto para la terminal de ejecución. Así que ahora todo es más uniforme.</div><div><br /></div><div><br /></div><div>En conclusión, lo primero que se ve sobre las nuevas versiones son obviamente los detalles estéticos. Hay varios que wx trajo de rebote (o el paso de gtk2 a gtk3), pero estos son los más importantes e intencionales. Esperemos que con el tiempo los cambios (estos y los que vendrán) resulten más funcionales que estéticos.<br /></div><div><br /></div><br />zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com3tag:blogger.com,1999:blog-164911854575895459.post-50138053970672223342020-05-04T06:46:00.000-07:002020-05-04T06:46:53.816-07:00Finalmente, el paso a wx3 y unicode en Zinjai y PSeInt<a href="https://cucarachasracing.blogspot.com/2016/03/el-paso-wxwidgets-3x.html" target="_blank">Hace años</a> que me quejo de mí mismo por estar atado a la versión 2.8.12 en modo ansi de wxWidgets. Siempre decía que el cambio a las versiones 3.x, junto con el paso al modo unicode, requería muuucho trabajo. Necesario, sí, pero demasiado para el poco tiempo que disponía. El <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos.html" target="_blank">problema de macOS y los 64bits</a> me obligó a concretar en parte la tan postergada <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos_11.html" target="_blank">migración de PSeInt</a>. Quedaron conviviendo ambas versiones, y de a poco la versión 3 fue ganando terreno. Sin embargo, culpa de estos cambios aparecieron errores nuevos, algunos bastante notorios. <br />
<br />
<a name='more'></a><br />
MacOS me obligó a publicar su versión basada en wxWidgets 3 y, ya que estaba en el baile, en modo unicode (a partir de ahora, wx3 a secas). Pero para evitar muchos problemas todavía no detectados, al principio para GNU/Linux y Windows mantuve como preferencia a wx2. El código, mediante montones de #ifdefs y macros (buuu!), permitía compilar con ambas. Mientras tanto, yo en mi notebook donde daba clases y desarrollaba, usaba wx3, como para ir probándola. Como parecía andar bastante bien (después de ajustados unos cuantos detalles), en algún punto publiqué una actualización que usaba wx3 también en Windows y GNU/Linux de 64bits.<br />
<br />
Eso fue algo prematuro. De a poco fui encontrando más problemas ocultos en la migración; y especialmente en Windows, donde hay más usuarios, pero menos lo había probado, ya que en mi notebook uso GNU/Linux. Por ejemplo, había un error que hacía inusable en muchos casos la funcionalidad de ejercicios autocontenidos en GNU/Linux, y otro peor que hacía desastres con los caracteres de fin de linea en Windows. Cosas como la 2da no las esperaba, ya que ni siquiera dependen de ansi vs. unicode (que es la raiz del 99% de los nuevos problemas).<br />
<br />
Y hay que agregar a la bolsa que wx2 en GNU/Linux funcionaba sobre gtk2, pero hoy en día <a href="https://cucarachasracing.blogspot.com/2019/12/reduciendo-las-dependencias-de-pseint.html" target="_blank">sería más lógico compilar wx3 contra gtk3</a>, que es bastante diferente, así que a los cambios propios y genéricos de wx hay que agregarles los específicos de gtk. Por ejemplo, la última versión estable de wx3, si se compila sobre gtk3, no aplica bien el escalado de fuentes que ofrecen algunos escritorios para pantallas hidpi. Como en la máquina virtual donde pruebo no uso escalado, y en la mía que sí, usaba gtk2, no me había dado cuenta. Encontré la solución en la versión trunk de wx y me pasé varias horas identificando el commit responsable en el git (la bisección no fue tan simple porque en el repo hay muchísimas versiones que no compilan porque alguien se olvidó de agregar algún fuente nuevo).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggVOTun82mL_qGJXmbTsw0JCk-EGRzSYoHNieHL1S0PeXULU2WbRMP-GYbMuA7ucmg0u8IuzODFUJ_69wH1f4JCB-ZsgqbL1UNdsg4KMKGW5G3-Oh5v77OWIDWwIqIXgSanwmw1hiAujWv/s1600/gtk2-vs-gtk3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="627" data-original-width="896" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggVOTun82mL_qGJXmbTsw0JCk-EGRzSYoHNieHL1S0PeXULU2WbRMP-GYbMuA7ucmg0u8IuzODFUJ_69wH1f4JCB-ZsgqbL1UNdsg4KMKGW5G3-Oh5v77OWIDWwIqIXgSanwmw1hiAujWv/s320/gtk2-vs-gtk3.png" width="320" /></a></div>
<div style="text-align: center;">
<span style="font-size: x-small;">La diferencia en el tamaño del texto entre los botones del cuadro "Buscar" </span></div>
<div style="text-align: center;">
<span style="font-size: x-small;">y los de los paneles apareció al pasar de gtk2 a gtk3.</span></div>
<br />
Y el problema de estos problemas, es que el código compila. Si al cambiar el funcionamiento de una api, el código no compila, es bueno porque nos permite detectar el cambio. Pero si compila igual, el bug puede quedar escondido un buen rato y saltar en el peor momento. Y dado que no tengo un departamento de control de calidad, casi seguro que les salta a ustedes, usuarios, que son mi equipo de <a href="https://cucarachasracing.blogspot.com/2013/03/la-importancia-del-testing.html" target="_blank">testing</a> trabajando en negro y sin saberlo :). Sobre todo en los casos de Windows y macOS.<br />
<br />
Y por este camino probar se hace cada vez más difícil. Si con andar teniendo que tener cuidado con las diferencias entre los 3 sistemas operativos no es suficiente; encima hay que agregar ambas versiones de wx, ambas versiones de gtk, etc. Sumando además que los compiladores que uso para las versiones 2.x en GNU/Linux y macOS, me atan a C++98. Creo que ha llegado el momento de abandonar por completo la versión 2, y simplificar el código y el desarrollo.<br />
<br />
<br />
A la par, y aprovechando la primera experiencia con PSeInt, comencé a
migrar ZinjaI, donde el cambio es mucho más grande y delicado. Lograr
que compile no fue tan difícil como esperaba. Pero una vez compilado casi nada funcionaba bien. Ahora lo básico está migrado y parece funcionar,
pero sigo encontrando errores ocultos todos los días. Sin embargo, dado
que en ZinjaI el cambio en el código es más grande, y que le puedo sacar
mucho más provecho a C++14, opté por cambiar drásticamente a
wx3/C++14 y ni intentar mantener ambas versiones.<br />
<br />
En conclusión, les pido disculpas por las "molestias ocasionadas" por
los errores de las últimas versiones de PSeInt. Hace unos días publiqué una
actualización con muuchas correcciones, y espero que a partir de ahora
volvamos de a poco a la estabilidad. El cambio era necesario, y además
de dolores de cabeza, trae sus muchas ventajas. Pero dejo las cosas
buenas y funcionalidades nuevas para otros posts.zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com6tag:blogger.com,1999:blog-164911854575895459.post-42017705928369574532020-04-25T07:12:00.000-07:002020-04-25T07:12:31.024-07:00Un volante virtual para GNU/LinuxEn épocas de aislamiento obligatorio hay que invertir el tiempo libre en casa. No es que tenga más tiempo libre, solo que funciona distinto. Entre las cosas que empecé (o que volví) a hacer a cambio de las que temporalmente no puedo está jugar videojuegos. Tenía desde hace años una deuda pendiente con el fantástico Grand Prix 4 de Geoff Crammond. El problema fue que mi viejo joystick comenzó a fallar a mitad del campeonato. En un simulador, es muy importante que la entrada sea analógica; el teclado degrada mucho la experiencia. Y tenía que resolverlo sin salir de casa.<br />
<a name='more'></a><br />
Buscando alternativas me encuentro con la idea de usar un smartphone como joystick. Estos aparatos tienen sensores que les permiten conocer su inclinación. Entonces, parece una idea factible usar eso para que nosotros podamos agarrarlo como si fuera un pequeño volante y girarlo en el aire. Googleando un poco llego a una aplicación llamada <a href="https://touchracer.com/" target="_blank">TouchRacer</a>. Funciona así: una app de Android le envía a la PC los datos por wifi o bluetooth; en la PC un programa recibe esos datos y se los pasa a un joystick virtual; y los juegos creen que están hablando con un joystick regular.<br />
<br />
No le tenía mucha fe a esta solución. A pesar de que me pareció una gran idea, pensé que no iba a ser tan cómodo, y que mi teléfono de gama baja baja no iba a colaborar. Pero igual, sin otras alternativas, me dispuse a probarlo. Y aquí aparece el 2do problema: la app para la PC es solo para Windows. Vale notar que el juego en cuestión también es para Windows, pero funciona de maravillas con <a href="https://www.winehq.org/" target="_blank">wine</a>. La app de TouchRacer, no. Depende de un componente llamado <a href="http://vjoystick.sourceforge.net/" target="_blank">vjoy</a>, que a su vez depende de un componente de Windows que no está reimplementado en wine.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/UlgmAMpSccM/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/UlgmAMpSccM?feature=player_embedded" width="320"></iframe></div>
<br />
Y entonces surgió la pregunta ¿será difícil hacer algo equivalente para GNU/Linux? Rehacer la app de Android no se me pasó por la cabeza, dado que tengo cero experiencia en programación para Android, y no se justificaba invertir demasiadas horas solo para jugar. Peeero... clonar la app de Windows en GNU/Linux parecía más accesible. Analizando el tráfico wifi con wireshark, veo que la comunicación es muy simple y directa. La parte que no tenía ni idea, y parecía complicada, era la de traducir eso en algo que el sistema crea que es un joystick. Googleando un poco me encuentro con que en GNU/Linux esto es muy fácil. Hay módulo llamado <a href="https://www.kernel.org/doc/html/v4.12/input/uinput.html" target="_blank">uinput</a> para hacer esto, y muchos <a href="https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/" target="_blank">ejemplos</a> en internet. Copiando y pegando un poco de aquí, un poco de allá, en un par de horas tenía una prueba de concepto funcionando.<br />
<br />
Y funcionó mucho mejor de lo que esperaba. Si hay buena conexión (juego cerca del router, o conecto la PC directamente al hotspot de Android), realmente funciona. Lleva unas vueltas acostumbrarse, pero al final es mucho mejor que el joystick que tenía (en mi experiencia, hace falta muy poquito para ser mejor que un joystick Genius), y aún jugando con uno de los teléfonos más baratos del mercado (Nokia 1).<br />
<br />
Así que me puse en contacto con el desarrollador original, que se interesó en la propuesta, y me dio el visto bueno para compartirlo. Ahora pueden encontrar <a href="https://touchracerlinux.sourceforge.io/" target="_blank">la nueva app para GNU/Linux en sourceforge</a>. Se usa en combinación con la app para Android original. Y esa no es código abierto :), pero por el momento sí es gratuita y no tiene publicidad, cosa que se agradece mucho.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLeBas5k5oZK-IFHZwFah18cb4kZBTLYiHnQWchoRWQZhrAkM_toyu2dGaeAbWnx-WAZMike9ML1gBYip4uk6KsDQlG-tusS7W5ww3N1VJ4Z52cIsmqciopSt0hyphenhyphen03eirv51QcwytyJne_/s1600/base-tlr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="782" data-original-width="1600" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLeBas5k5oZK-IFHZwFah18cb4kZBTLYiHnQWchoRWQZhrAkM_toyu2dGaeAbWnx-WAZMike9ML1gBYip4uk6KsDQlG-tusS7W5ww3N1VJ4Z52cIsmqciopSt0hyphenhyphen03eirv51QcwytyJne_/s400/base-tlr.png" width="400" /></a></div>
<br />
Tan bien funcionó la cosa que el fin de semana emprendí un proyecto de hardware: armarle una base al teléfono para que quede como un volante. Juntando un rulemán viejo que alguna vez hubo que cambiarle a mi bici, la base pesada de un trofeo, unas chapitas que compré para colgar algo que nunca colgué, unos cartones bastante firmes, una tapita de botella, algunos precintos, pegamento de silicona, y un par de banditas elásticas en homenaje a MacGyver, salió ese engendro de la foto. Realmente ayuda, porque el celu en el aire no es tan estable, y uno sin darse cuenta lo mueve mucho además de girarlo. Sujeto a una base, reduce bastante ese ruido y transmite más confianza.<br />
<br />
En conclusión, les comparto una pequeña contribución para los linuxeros que deban quedarse en casa y les gusten las carreras. Además de jugar, también le dediqué tiempo a ZinjaI y PSeInt, hay cambios muy interesantes en camino, pero eso será tema para otro post.<br />
<br />zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com5tag:blogger.com,1999:blog-164911854575895459.post-43433310640297277722020-02-21T07:13:00.003-08:002020-02-21T07:13:59.653-08:00La docencia, el paso del tiempo y el sentido común<div class="separator" style="clear: both; text-align: center;">
</div>
Como todos los años, cuando están por empezar las clases, paso por una etapa de introspección durante la preparación de ese primer contacto con los nuevos ingresantes. Ya llevo casi 15 años "enseñando". Y ya he pasado también más de 15 "aprendiendo", la mayoría de ellos formalmente entre las carreras de grado y posgrado. ¿Quiere decir esto que soy un gran docente? ¿Que la tengo reee-clara en esto de la educación? Terminantemente NO! Hacerlo muchas veces no implica hacerlo bien si no hay mejora entre una y otra vez, si se repiten todos los años los mismos errores.<br /><a name='more'></a><br />¿A qué viene esto? Varias cosas. Por un lado, creo que sí soy mejor docente que hace 15 años (lo cual no dice gran cosa, no sabemos cuan malo fue el punto de partida :), porque sí me he preocupado por aprender algo en el camino y no limitarme a repetir una y otra vez la misma clase. Pero eso no es garantía, y no se correlaciona siempre con los resultados. El año pasado, con alguna materia me fue muy bien, con otra no tanto.<br /><br />Cuando empecé a dar clases, tenía casi la misma edad que muchos de los alumnos, y eso ayudaba mucho en la comunicación. Luego de 15 años, la brecha generacional ha ido creciendo y ya no es lo mismo. No es que sea viejo, pero en algunos aspectos sí empiezo a parecerme al abuelo Simpson contando sus historias de antaño que a nadie le interesan. Por otro lado, he recopilado en 15 años montones de recursos como ejemplos, abordajes, metáforas, experiencias, anécdotas, etc; que hacen que ahora tenga muchas más alternativas a la hora de presentar un tema, y que las pueda pesar y combinar con mejor criterio. Así que una de cal y una de arena, pero ¿cuál pesa más? ¿cómo potenciar lo segundo?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoNrtnTl49VM-Rvz-dWGAmKMHVgEKGMX3Fy2y8YZyCtkSuiaA_3K7Oyf68OqCln0Vc9xa-16gbWVy5VQrepLQZpHEmeTvmEusujsu-wJ0xGA6KTfMX5q-WvzkAK8O-2HPhfJ-9y-7ipHSL/s1600/ttotf-comic1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="769" data-original-width="1600" height="191" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoNrtnTl49VM-Rvz-dWGAmKMHVgEKGMX3Fy2y8YZyCtkSuiaA_3K7Oyf68OqCln0Vc9xa-16gbWVy5VQrepLQZpHEmeTvmEusujsu-wJ0xGA6KTfMX5q-WvzkAK8O-2HPhfJ-9y-7ipHSL/s400/ttotf-comic1.png" width="400" /></a></div>
<div style="text-align: center;">
<span style="font-size: x-small;"><i>Cualquier parecido con mis clases es pura coincidencia<br />(tomado del libro que recomiendo al final del artículo)</i></span></div>
<br />
No voy a responder a todos estos interrogantes, no creo que tener autoridad para hacerlo. Pero es igualmente bueno sembrar la duda, sobretodo en aquellos lectores que estén también ligados a la docencia. Porque lamentablemente no tenemos, como decía el gran Nestor, un "policía" en el aula que controle que demos bien las clases (en todos los sentidos). Los alumnos son los responsables de hacer ruido cuando la cosa no va bien; pero usualmente eso ocurre naturalmente solo cuando hay grandes problemas; pero no sirve para hacer pequeños ajustes. Tenemos, nosotros docentes, que incentivar a los alumnos a darnos feedback (y convencerlos de que eso no tiene impacto en sus calificaciones).<br />
<br />
Pero aún así, el sistema no es perfecto. El alumno tiene un punto de vista muy muy valioso, pero tampoco tiene conocimiento de pedagogía, ni siquiera del tema que está aprendiendo, y a veces hasta le cuesta la comunicación; y entonces hay que saber filtrar y traducir sus comentarios y eso no es nada fácil. Sería muy útil el feedback de un buen docente. Pero un buen docente está dando su clase, no analizando la mía. Qué bueno sería que tuviéramos la costumbre de ir de tanto en tanto a escuchar clases ajenas. Para señalarle constructivamente sus errores al colega, y para copiarle sus virtudes para nuestras propias clases.<br /><br /><br />Por otro lado, en las universidades argentinas, y en particular en las ingeniería hay toda una tendencia hacia el "aprendizaje por competencias". Se trata de armar carreras y materias empezando por "qué necesitará saber hacer el egresado", y a partir de allí definir los contenidos que hay que dar para lograr eso; en contraposición al modelo tradicional que parte con mucha inercia de "qué contenidos debe conocer" y luego a tratar de justificar para qué sirven. El 2do enfoque suele ser más cómodo para el docente, sobre todo si podemos armar el curso alrededor de un libro clásico; pero carece de "sentido común".<br /><br />Y a eso iba, al "sentido común". Hice un curso sobre este tema de las competencias. Lo empecé por sugerencia de la gestión, aunque no estaba tan convencido de su utilidad. Cuando empecé con el material pensaba a cada rato que los de las ciencias sociales dan demasiadas vueltas para decir las cosas, y que al final, el quid de la cuestión termina siendo algo de "sentido común". Claro que es mejor para el alumno armar su educación en función de lo que va a necesitar hacer; y no de lo que a mí me gusta enseñar, de lo que yo sé, o de lo que ya tengo cómodamente diagramado en el libro X. ¿Hacen falta tantos papers para decirme eso?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWsFivCW2QsXCn8oqZq57O0vmqGCw38MdS5HDyDsCdbGS7PVNtXPGEDU_fLVQFEF_YWbBiblxemtUfoA8uNNlszlrOiONgRUWQXfmaPmwg63fcinSC6huWXTBp2mF9BmiZoJMYLnx9xu81/s1600/common_sense.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="285" data-original-width="900" height="126" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWsFivCW2QsXCn8oqZq57O0vmqGCw38MdS5HDyDsCdbGS7PVNtXPGEDU_fLVQFEF_YWbBiblxemtUfoA8uNNlszlrOiONgRUWQXfmaPmwg63fcinSC6huWXTBp2mF9BmiZoJMYLnx9xu81/s400/common_sense.gif" width="400" /> </a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;"><i>Dilbert arroja muuchos resultados al buscar "common sense".</i></span></div>
<br />
Es una sensación similar a la que me da gran parte de la ingeniería de software; que define con nombres pomposos prácticas que deberían ser evidentes para cualquier programador con algo de experiencia. Pero, <a href="https://cucarachasracing.blogspot.com/2017/04/el-arte-de-nombrar-parte-2.html" target="_blank">como dije alguna vez</a> luego de leer el fascinante libro de patrones de diseño de la banda de los 4, esto de ponerle nombre es mucho más profundo de lo que parece. Tiene que ver con hacer un análisis consiente de estas cosas de "sentido común", con sistematizarlas para facilitárselas a otros con menos experiencia, compilarlas para acortar el camino, definir un lenguaje común y conciso sobre el cual construir luego conceptos más complicados, y más. Lo mismo me pasó con esto de las competencias. Y por el mismo motivo, terminé pensando que el curso fue muy valioso, y que seguramente tendrá un impacto positivo en mis próximos dictados.<br /><br />Por que el "sentido común" a veces parece ser el menos común de los sentidos. Tuve profesores que hacían lo que dicen estas nuevas tendencias al pie de la letra, sin saber los nombres, sin tanto meta-análisis; que lo hacían naturalmente por el simple hecho de preocuparse por su trabajo, por sus alumnos, y de tener buen criterio para resolver esas preocupaciones. Sin embargo, son los menos. Y aún para los buenos docentes con este potencial nato, el proceso de adquirir este know-how por cuenta propia es largo, e incluye muchos errores de los cuales aprender en el camino. Así que, bienvenido el material que compile este conocimiento y nos acelere el aprendizaje.<br />
<br />
<br />
Como para ir cerrando, y dejar algo más que algunas conclusiones dudosas y personales, voy a recomendar dos lecturas que van en este sentido. Primero una que descubrí hace muy poquito, por un comentario de un alumno avanzado de la carrera, y que apunta derecho a las buenas prácticas del docente, explicitando cientos de recomendaciones concretas y pragmáticas. Un verdadero compilado de buenas prácticas y sentido común que todo docente joven debería leer: <a href="https://ocw.mit.edu/resources/res-18-004-the-torch-or-the-firehose-a-guide-to-section-teaching-spring-2009/index.htm" target="_blank">The Torch Or The Firehose.</a><br /><span id="goog_606746356"></span><span id="goog_606746357"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOTCrDizyph1UvIrWEvWSG4Bcg0GJiP3jpD-5Bydwdb7x1hl2ilYfWRlzC7wvyWLufdscY54oqRX4-Ez2PN3yueVqzj1pU0gnznp_3uHlhG-0xrSZf4zvx-1Ogtko1moR9r-qGBw8es6pR/s1600/ttotf.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="692" data-original-width="592" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOTCrDizyph1UvIrWEvWSG4Bcg0GJiP3jpD-5Bydwdb7x1hl2ilYfWRlzC7wvyWLufdscY54oqRX4-Ez2PN3yueVqzj1pU0gnznp_3uHlhG-0xrSZf4zvx-1Ogtko1moR9r-qGBw8es6pR/s320/ttotf.png" width="273" /></a></div>
<br />La mayoría de lo que hay allí son cosas que he ido notando y descubriendo de a poco a lo largo de los años. Pero si quisiera resumirlas yo, probablemente me olvidaría de muchas. Por eso ahora siempre tengo un gestor de notas a mano, para escribir esos pequeños descubrimientos que voy haciendo al enseñar o al aprender, en el momento en que aparecen, de modo de no olvidarme de profundizarlos y aplicarlos más tarde.<br />
<br />El otro libro que me fascina y en su momento me abrió mucho la cabeza, ya lo conocen porque <a href="https://cucarachasracing.blogspot.com/2013/02/ser-o-no-ser-una-tortuga.html" target="_blank">ya escribí</a> sobre él aquí, y es el "Mindstorms: Children, Computers, and Powerful Ideas" del grandísimo Seymor Papert. Es más denso, menos directo, más profundo, pero te atrapa desde la primer página y no deja de sorprender hasta la última.zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com1tag:blogger.com,1999:blog-164911854575895459.post-13856125440864291662019-12-06T05:24:00.001-08:002019-12-06T05:26:01.571-08:00Reduciendo las dependencias de PSeInt en GNU/LinuxAsí como me vi obligado a <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos_11.html" target="_blank">repensar</a> <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos_19.html" target="_blank">el "paquete"</a> de PSeInt para macOS a raíz del <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos.html" target="_blank">problema de los 64bits</a>; tuve problemas similares en GNU/Linux. Para empezar, una de las principales bibliotecas del sistema que necesito ya no aparece en todas las distribuciones. Por seguir, una nueva que agregué por el tema del <a href="https://cucarachasracing.blogspot.com/2019/11/renderizado-de-texto-en-el-editor-de.html" target="_blank">renderizado de texto</a> también trajo sus problemas.<br />
<br />
<a name='more'></a><br />
La primer dependencia a la que me refería es GTK. Las distros no han dejado de incluir GTK; pero sí ocurre que han empezado a dejar de lado la serie 2.x, en favor de la 3.x. De forma similar a como me pasó a mi con wx; GTK 3 está desde hace rato pero no es tan trivial migrar un programa desde GTK 2. Entonces, muchos paquetes todavía dependen de GTK 2, por lo que se sigue manteniendo desde hace años e incluyendo en la mayoría de las distribuciones.<br />
<br />
Programa muy importantes para el ecosistema del software libre, como el GIMP, aún están en proceso de migración. Por esto, en cualquier distro se puede instalar GTK 2 con cierta facilidad. Pero el hecho de que haya que instalarla es un pequeño escalón más para los usuarios principiantes, que sería mejor no tener.<br />
<br />
En mi caso, no me importa tanto hacérselas instalar a los usuarios de ZinjaI (y de hecho de momento no tengo otra salida); pero sí a los de PSeInt, ya que esos se supone que son mucho menos experimentados. E instalar una biblioteca no es siempre tan amigable como instalar un programa. En Ubuntu 19, por ejemplo, GTK 2 no se instala con el sistema base. Desde una consola, con un "simple" (nótense las comillas) apt-get se consigue; pero no todos saben que las consolas no muerden.<br />
<br />
<br />
Y ¿de dónde sale esta dependencia? Pues bien, wxWidgets, en GNU/Linux, es como un wrapper sobre GTK. Obviamente wx 2 funcionaba sobre GTK 2. Pero ahora que PSeInt funciona con wx 3, puedo compilar wx 3 de modo tal que funcione sobre GTK 3.<br />
<br />
Al igual que cuando paso de un sistema a otro, si paso de un GTK a otro aparecen pequeñas asperezas que limar. Algo de eso estuve haciendo, y parece que ahora todo anda con normalidad. El detalle es que si me quedo con esta versión, en muchas distros viejas dejará de funcionar, porque no solo usa un GTK más nuevo, sino que está compilada sobre todo un sistema base más nuevo. <br />
<br />
Entonces, para no elevar los requerimientos, lo que voy a hacer por un tiempo es incluir en el paquete para GNU/Linux (solo en el de 64bits) ambas versiones: un PSeInt con wx2 sobre GTK2, y otro con wx3 sobre GTK3. El lanzador tratará primero de lazar el nuevo, y si falla lanzará el viejo. Así, a todos debería funcionarles sin instalar nada. Si tienen un sistema no taaan viejo y GTK 3, verán los nuevos detalles de wx3. Si no, seguirá como siempre.<br />
<br />
<br />
La otra biblioteca problemática fue glew. La agregué inicialmente para poder usar las extensiones de OpenGL, porque necesitaba cargar shaders para el antialiasing del texto en psdraw. Por suerte no soy el único que pensó que era poca cosa para justificar toda una dependencia, y buscando en google aparecen algunas alternativas. Terminé optando por una pequeña biblioteca que es parte de <a href="https://github.com/ApoorvaJ/Papaya" target="_blank">Papaya</a> (un editor de imágenes que hace uso intensivo de la GPU), llamada gl_lite, que hace lo que necesito y no mucho más. Así que glew ya no será un requisito adicional.<br />
<br />
De forma similar, ya que estaba saqué también freeglut (otra que no viene de serie). La usaba solo para algunos textos particulares, pero ahora uso <a href="https://cucarachasracing.blogspot.com/2019/11/renderizado-de-texto-en-el-editor-de.html" target="_blank">el nuevo mecanismo</a> para todo y me la evito.<br />
<br />
Con todo esto, aproveché para también para "refrescacar" los scripts que uso para compilar y empaquetar todo, en sus varias versiones. De rebote, ahora debería ser un poco más fácil que un usuario intente compilar PSeInt por su cuenta. Aunque esto sigue sin ser necesario para el 99% de los mismos. A esa gran mayoría, espero que la nueva versión les funciones sin problemas, sea cual sea su sistema. Hay mucho tiempo invertido para esto en las últimas semanas.zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com9tag:blogger.com,1999:blog-164911854575895459.post-7625926568029153552019-11-25T06:08:00.000-08:002019-11-25T06:08:31.925-08:00Renderizado de Texto en el Editor de Diagramas de FlujoEncontré casi por <a href="https://www.youtube.com/watch?v=Xkpgsg6jU-4" target="_blank">casualidad</a> una <a href="https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf" target="_blank">idea</a> para renderizar fuentes con OpenGL aparentemente mucho mejor que la que venía usando en el editor de diagramas de flujo de PSeInt (psdraw). Mi sorpresa fue grande. Conocía el concepto, pero no implementado de esta forma. Para esa aplicación (texto), resulta muuuy simple y los resultados son muy buenos.<br />
<a name='more'></a><b><br />Método original</b><br />
<br />
Respecto al texto en psdraw, hasta ahora hacía lo que hacen muchos: tener la fuente (todos los caracteres) en una textura, y dibujar quads, uno por cada letra de una cadena, cada uno con una partecita de la textura. Esto es relativamente rápido y eficiente, pero tiene un problema: en el caso del texto se nota mucho el filtrado de la textura. ¿A qué me refiero? A que cuando la letra se muestra de un tamaño tal que no coincide la resolución de la textura con la de la ventana (no coincide pixel con téxel), a la textura se le aplica algún escalado rápido que la blurea o pixela.<br />
<br />
La solución sería tener varias texturas, distintos tamaños, pero eso es más memoria, más trabajo. Y además en psdraw el nivel de zoom es continuo, no salta de un tamaño a otro sino que barre todos los intermedios, así que los tamaños posibles son infinitos. Sí, podría tener unos cuantos e interpolar, o algo tipo mipmaps, pero parece demasiada memoria (aunque nunca se note) para una tarea tan simple, y además cada interpolación usualmente implica más bluring artificial.<br />
<br />
Hasta ahora tenía dos versiones de la fuente (de los 256 caracteres que uso), una en una textura de 1024x1024, otra de 2048x2048. En general con eso sobra, nadie mira los diagramas tan de cerca. <br />
<br />
<br />
<b>Método nuevo</b><br />
<br />
El problema del 1er método se da cuando queremos muestrear la textura en un punto intermedio entre 2 téxeles. Allí se promedian los colores de los dos. Si estábamos en el borde entre interior y exterior de la fuente (supongamos blanco y negro), nos quedamos con un gris que no es ni una cosa ni otra, que no existiría en la fuente a mayor resolución. Ahora, supongamos que en lugar de color, lo que hay en cada posición de la textura es un indicador de cuan cerca o lejos está el borde (con un signo que depende de estar adentro/afuera). Ahora el promediado sí tiene sentido como aproximación de esa distancia. Y entonces diremos que donde la distancia sea 0 está el borde, así que si el signo del promedio es positivo estamos adentro, y si no afuera, no más grises.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga7UbSR5tMlxjOVJQnZ9p_KGdyAC3Jka63pIS2lT4ZekcdZkP0MmibLwBBmGFz44JYooX0Goj5e4Trhmu3XngbYkRZJ6JSXIaOcg5yw2aDAlhf4lk237NpzUxlxXzdT5FhgJBrmcFM84zJ/s1600/pseint_fd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="822" data-original-width="1600" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga7UbSR5tMlxjOVJQnZ9p_KGdyAC3Jka63pIS2lT4ZekcdZkP0MmibLwBBmGFz44JYooX0Goj5e4Trhmu3XngbYkRZJ6JSXIaOcg5yw2aDAlhf4lk237NpzUxlxXzdT5FhgJBrmcFM84zJ/s400/pseint_fd.png" width="400" /></a></div>
<div style="text-align: center;">
<span style="font-size: x-small;"><i> A la izquierda, capturas del método original; a la derecha, del nuevo.<br />La nueva textura tiene 1/4 de la resolución de la original.</i></span></div>
<br />
Esto se llama SDF, que no es Super-Dimensional Fortress, sino Signed Distance Fields. La implementación es muuuy simple hasta en OpenGL 1. Los valores de distancia se mapean al intervalo [0;1], dejando la distancia 0 original ahora en el .5, y activando el alpha-test (literalmente tres lineas de código) la gpu hará la inteporlación y el corte por nosotros. Respecto a la textura, usé la misma que antes, reducida a solo 512x512, y con un desenfoque gaussiano aplicado con gimp. Ese no es el verdadero campo de distancias, pero el desenfoque da un efecto parecido.<br />
<br />
El problema de este método es el antialiasing, que no existe. Los métodos que ofrece OpenGL sin tener que programar extra solo funcionan en los bordes reales de los polígonos, y no en los bordes artificiales que genera el alpha-test en el interior. Así que para solucionar eso (más o menos) tuve que programar un pequeño fragment-shader que haga un muestreo sub-pixel del alpha-test para determinar opacidades intermedias donde corresponda (y como "daño" colateral, ahora tengo una dependencia más: glew).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM-cD-3GFx99-Ni1jQuHy_zhcBFIcqOFuqxYO6RO23dUuT_RjsqmgR1pVtbiMam_DyOVY4GRw0x_GgYk3mO5g1qzxdfKJqPatIBUWzbKvINYOC4rK27AXORu3NG5s-qFiL5h09lUJb_rrA/s1600/pseint_df_aa.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="317" data-original-width="867" height="146" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM-cD-3GFx99-Ni1jQuHy_zhcBFIcqOFuqxYO6RO23dUuT_RjsqmgR1pVtbiMam_DyOVY4GRw0x_GgYk3mO5g1qzxdfKJqPatIBUWzbKvINYOC4rK27AXORu3NG5s-qFiL5h09lUJb_rrA/s400/pseint_df_aa.png" width="400" /></a></div>
<div style="text-align: center;">
<span style="font-size: x-small;"><i>Izquierda: solo alpha-test; Derecha: antialiasing mediante shader</i></span></div>
<br />
El resultado es bastante bueno. No es el mejor texto del mundo ni mucho menos. Por un lado, no representa fielmente la forma de la fuente (se redondean los vértices). Para resolver eso hay técnicas, pero involucran alguito más de trabajo en el shader y en la generación de la textura, y cómo los detalles de la tipografía a mi no me hacen mucha diferencia, lo dejé así por ahora. Por otro lado el antialiasing tampoco es tan bueno, esto se nota más en textos pequeños en pantallas no HiDPI. Pero no quería complicarlo ni encarecerlo tanto, y ya con esto se ve mucho mejor que antes, así que en cualquier caso es un avance.<br />
<br />
<br />
En conclusión, me sorprendió la relación costo/beneficio de esta técnica, que además sirve para cualquier forma (son como máscaras binarias, <a href="https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf" target="_blank">más detalles aquí</a>). Es de esas cosas que no sé cómo no encontré antes. Y aunque no tenía previsto trabajar en las fuentes de psdraw, pareció el lugar más simple para probarla. Así que en la próxima versión de PSeInt, el editor de diagramas se verá un poquito mejor.zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com1tag:blogger.com,1999:blog-164911854575895459.post-63178631287653207252019-11-19T03:47:00.000-08:002019-11-19T03:47:06.978-08:00Los desafíos de portar PSeInt a macOS Catalina (3/3): Los últimos detallesYa conté los problemas y soluciones para <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos.html" target="_blank">poner en marcha un toolchain</a> para las nuevas versiones de macOS, y la <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos_11.html" target="_blank">migración del código de wx 2 ansi a wx 3 unicode</a>, en GNU/Linux. Solo me faltaba combinar todo esto para ver si andaba en macOS, si tenía un paquete para publicar en el sitio. Por supuesto que no anduvo a la primera. He aquí los últimos detalles de esta historia.<br />
<br />
<a name='more'></a><b><br />El problema de los dylibs</b><br />
<br />
Una vez que pude compilar tanto wx, como los distintos módulos de PSeInt con el nuevo toolchain, armé un paquete con la estructura que le gusta a macOS y me dispuse a probarlo en una máquina virtual: no podía ni iniciar PSeInt porque los binarios de PSeInt no encontraban a los binarios de wxWidgets.<br />
<br />
Los dylibs en mac son más o menos como los dlls en Windows, o como los .so en GNU/Linux. Antes, con IMCROSS, había compilado wx para enlazado estático. Esto es, para ponerlo dentro de cada ejecutable. El problema es que PSeInt en realidad está formado por varios ejecutables, y entonces esto hace al paquete final innecesariamente grande. Lo mejor en este caso es enlazado dinámico: que estén una sola vez, fuera de los ejecutable, y que todos ellos busquen esos mismos archivos.<br />
<br />
En el paquete para mac, hay un directorio <i>Contents</i>, que adentro tenía dos directorios: <i>MacOS</i> y <i>Resources</i>. En <i>MacOS</i> está el ejecutable principal; en <i>Resources</i> todo los demás: los otros ejecutables, la ayuda, los íconos, etc. Ahora hay un tercero: <i>Frameworks</i>, donde está wxWidgets. El problema es que luego de compilar, me quedó en el ejecutable la ruta al binario de la biblioteca tal cual se la di al compilador. Esto es, la ruta donde yo los tengo en mi GNU/Linux. En la Mac esta ruta no existe ni tiene sentido. Las rutas deberían llevar a <i>Frameworks</i>, y ser relativas. <br />
<br />
Después de googlear un rato, llego a que una herramienta <i>otool</i> me puede mostrar las rutas que tiene adentro cada binario, y otra herramienta <i>install_names_tool</i> me deja modificarlas. Entonces, desde un script de bash, uso la primera para encontrar las dependencias, y la segunda para corregirlas (recursivamente, también entre los propios archivos de la biblioteca). El detalle de color fue que en uno de los binario los paths nuevos no entraban y entonces tuve que dar con un argumento de compilación más para agregar un relleno a los paths, de modo que luego <i>install_names_tool</i> tenga espacio de sobra.<br />
<br />
<br />
<b>GNU/Linux vs. macOS</b><br />
<br />
Una vez compilado, empaquetado, y ejecutándose, era hora de ver si la ejecución hacía lo que debía. Se supone que el objetivo de wx es independizarnos de la plataforma, que programemos contra wx y eso se compile y comporte igual en cualquier sistema operativo. No siempre funciona así al 100%, a veces varían sutilmente cosas como el orden de los eventos que reciben los controles, o el tipo de evento que generan ciertas acciones. <br />
<br />
Antes no podía quejarme de esas diferencias por no usar el wx que corresponde, ahora puedo! Pero igual, hasta que se resuelvan, tengo que identificarlas yo y aplicar algún parche a mi código. Me pasó algo de esto con el editor de diagramas de flujo. La forma de detectar las teclas me funcionaba perfectamente en GNU/Linux, pero no en macOS. <br />
<br />
Los detalles de este tipo que eran funcionales fueron corregidos. Algunos llevaron mucha prueba y error. Los que son estéticos todavía no, esos tienen por el momento baja prioridad. Por ejemplo, si usan un tema de fondo oscuro en macOS, algunos elementos de la interfaz se ven "feos". Feos pero andan que es lo más importante. La estética se irá arreglando luego de a poco.<br />
<br />
<br />
<b>Otras, varios y miscelánea</b><br />
<br />
Finalmente, ahora que PSeInt funciona con wx 3 unicode, no solo tengo una versión para los usuarios de macOS Catalina. Sino que también de a poco me voy despegando de un compilador y una biblioteca muy viejos. Un compilador más nuevo me permite migrar a C++11 o C++14 y limpiar mucho el código. Y una biblioteca más nueva agrega funcionalidad. Por ejemplo, ahora scintilla ofrece <i>annotations</i>, que puedo usar para mostrar los errores de una forma algo mejor que la anterior.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJYAi0e6Ob6osTNsyqueCI4ZXzoojNW0oxl-1ZzDYB-d24uSRyPK13WjxP_IXtM5hub8etphqCwdIoMgMdLelkTIkzdEH_IaWN8eHwtePgVAjGU_je4yznA-A80_vLuUb2jRVQYWrf4Qoq/s1600/pseint-annotations.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="817" data-original-width="873" height="299" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJYAi0e6Ob6osTNsyqueCI4ZXzoojNW0oxl-1ZzDYB-d24uSRyPK13WjxP_IXtM5hub8etphqCwdIoMgMdLelkTIkzdEH_IaWN8eHwtePgVAjGU_je4yznA-A80_vLuUb2jRVQYWrf4Qoq/s320/pseint-annotations.png" width="320" /></a></div>
<div style="text-align: center;">
<span style="font-size: x-small;"><i>Comparación entre mostrar los errores como annotations (arriba, wx3), o mediante tooltips (abajo, wx2).</i></span></div>
<br />
Por otro lado, usar otro compilador distinto al habitual y más nuevo hizo que aparecieran muchos warnings. Algunos me sirvieron para corregir pequeños errores, otros tuve que silenciarlos o dejarlos para más adelante. Pero en cualquier caso, más información de diagnóstico siempre es buena noticia.<br />
<br />
<br />
Toda esta experiencia me servirá cuando intente migrar ZinjaI a wx3. ZinjaI puede sacarle mucho más jugo a las nuevas funcionalidades de wxWidgets, pero también es mucho más difícil de portar. Es mucho más grande, y hay mucha más interacción entre la interfaz y todo tipo de cosas ajenas (el compilador, el depurador, el parser, el diseñador, los profilers, el sistema de archivos, mis propias clase, etc). Por eso llevará mucho más tiempo, pero que PSeInt ya funcione es una muy buena señal. <br />
<br />zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com7tag:blogger.com,1999:blog-164911854575895459.post-45438143784864205192019-11-11T04:03:00.000-08:002019-11-11T04:03:33.170-08:00Los desafíos de portar PSeInt a macOS Catalina (2/3): PSeInt vs. wxWidgetsSiguiendo con la serie de problemas y soluciones relacionados a la compilación de PSeInt para macOS Catalina, ahora toca hablar de la relación entre PSeInt y la biblioteca wxWidgets. Resumiendo: PSeInt estaba escrito para una versión muuuy vieja de wx; y además cuando lo empecé había dos formas de compilar wx, y PSeInt usaba la que ya casi no existe. Por esto, <a href="https://cucarachasracing.blogspot.com/2019/11/los-desafios-de-portar-pseint-macos.html" target="_blank">aún teniendo un toolchain nuevo</a> que puede compilar una wx nueva, el código de PSeInt requirió de muchísimos cambios y ajustes.<br />
<a name='more'></a><br />
<br />
<b>Los comienzos: wxWidgets 2.8 ansi</b><br />
<br />
Cuando empecé con wx, las versiones estables eran de la serie 2.x, y había 2 formas de compilación que parecían tener más o menos el mismo soporte. Por formas de compilación me refiero a banderas que cambian el código generado. Por soporte, me refiero a que se usen efectivamente y que los desarrolladores de la biblioteca mantengan.<br />
<br />
La diferencia pasaba por el manejo de cadenas: en un caso estarían codificadas según el código ascii (modo ansi), en el otro serían utf-8 (modo unicode). La versión ansi mapea de forma directa con la clase std::string de C++, y las constantes de texto simples escritas sin muchas precauciones. Por esto es más fácil de usar. La versión unicode requería de muchas conversiones al entrar o salir de wx, y de tener cuidado al guardar en strings o cstrings (los caracteres especiales, como acentos, en utf8 ocupan más de un byte cada uno). Así que parecía menos trabajo ir por el modo ansi.<br />
<br />
Con el tiempo, el modo unicode ganó la batalla, casi todos los sistemas pasaron a usar utf8 o similares (por ej, en GNU/Linux para los archivos de texto, o hasta para los nombres de los archivos). Entonces, wx se enfocó en el modo unicode, y el modo ansi quedó algo relegado. Wx 3 está pensada directamente para unicode, es su codificación "natural", y la versión "ansi" es la forzada que hace algunas cosas raras para tratar de ajustarse solo a nivel API. Ya no se recomiendo usar ansi, y ya no tiene buen soporte.<br />
<br />
Hasta ahora yo venía evitando actualizar wx por este problema. Pero no actualizar también es un gran problema. Las versiones viejas a veces no se pueden ya ni compilar en sistemas nuevos (como en las nuevas macs). Obviamente no me puedo quejar de eso ni de ningún otro error a los desarrolladores porque me van a decir (con toda lógica y razón) que me actualice. Así que venía aguantando aplicando con mis propios parches sucios a esa vieja wx 2.8 para tratar de mantenerla viva. Hasta que con esto del toolchain nuevo para macOS ya se hizo imposible.<b> </b><br />
<br />
<br />
<b>El futuro: wxWidgets 3.x unicode</b><br />
<br />
Hace un tiempo ya empecé a experimentar con wx 3.0 y wx 3.1, primero probando los modos ansi, luego los unicode. Donde antes asignaba algo a un [c]string o wxWstring, ahora la asignación ya no es compatible sin conversión de por medio. Y entonces esto implica modificar practicamente todos lo usos de cadenas que haga la interfaz. Para salir del paso armé unas pocas macros para realizar las conversiones. Si ya sé que me voy a ir al infierno de los programadores por usar macros, pero es la solución más rápida para mantener funcionales ambas versiones. <br />
<br />
Sin embargo, no es hacer una macro que ponga la función de conversión en el medio y aplicarla por todos lados. Hay que analizar cada caso. Si una cadena viene de la gui y va a mis clases internas debe convertirse desde utf8 hacia ascii. Si es al revés, convertirse al revés. Si se mueve entre elemento de gui y elemento de gui, o solo entre mis clases, no va conversión. Entonces, hay que ver en cada caso, cuando aplicar la conversión, rastreando el ciclo de vida de la cadena. Y más aún, en mac, suele haber una tercera codificación diferente para la fuente.<br />
<br />
Y en esta diferencia de modos a veces hace que el código simplemente no compile. Esos son los casos buenos, porque así es fácil ver donde faltan conversiones (luego habrá que decir "cual" es la que falta para cada "donde"). Pero en muchos casos es automática, y no siempre la automática es la que necesito. Y hay otros casos más especiales todavía. Por ejemplo, cuando llegan datos a un socket, llega un flujo de bytes en "crudos" que la aplicación debe decidir cómo tratar. Por su naturaleza de "montón de bytes a reinterpretar arbitrariamente", aunque no haga las conversiones adecuadas, es algo que compila igual. Y entonces es un error más difícil de identificar y diagnosticar para poder corregir.<br />
<br />
<br />
Finalmente, además del tema de las cadenas, hay más cosas varias que van cambiando de a poco en la API de wx. Si yo fuera actualizándome regularmente, sería poco trabajo, y las notas de cada versión me lo indicarían. Pero si de golpe quiero saltear 8 años de desarrollo, se van a haber acumulado muchos cambios, y muchas cosas van a empezar a andar raro hasta que me de cuenta.<br />
<br />
<br />
Por suerte, ya antes de toda esta historia de macOS había estado haciendo pruebas con wx 3 unicode en mi GNU/Linux, y entonces muchos de estos problemas ya estaban detectados y algunos hasta medio resueltos. Lo había hecho solo con el editor principal. Ahora tuve que replicarlo en los demás: el editor de diagramas, el generador y evaluador de ejercicios autocontenidos, y la terminal de ejecución propia. Después de varios días de trabajo, PSeInt parecía funcionar bien con wx 3 unicode, al menos en mi GNU/Linux. Faltaban ajustar detalles que solo se manifestarían en un macOS, y lidiar con el empaquetado de todo esto. Pero eso quedará para la tercera parte.zaskarhttp://www.blogger.com/profile/10849946311739196317noreply@blogger.com0