lunes, 11 de noviembre de 2019

Los desafíos de portar PSeInt a macOS Catalina (2/3): PSeInt vs. wxWidgets

Siguiendo 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ún teniendo un toolchain nuevo que puede compilar una wx nueva, el código de PSeInt requirió de muchísimos cambios y ajustes.


Los comienzos: wxWidgets 2.8 ansi

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.

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.

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.

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. 


El futuro: wxWidgets 3.x unicode

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.

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.

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.


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.


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.

No hay comentarios:

Publicar un comentario