15 febrero 2024
A primera vista, puede parecer que el traductor solo tiene una forma de usarse: alimentándolo con código C#, esperamos obtener código C++ equivalente como salida. De hecho, esta forma es la más común, pero está lejos de ser la única. A continuación se presentan otros modos proporcionados por el marco de traducción de código y las utilidades relacionadas.
Los productos son desarrollados por programadores que necesitan aprender los detalles del procedimiento de traducción de código a otros idiomas y las limitaciones asociadas con él. Como resultado, surgen situaciones en las que los cambios correctos desde el punto de vista de C# realizados por los desarrolladores del producto interrumpen el proceso de lanzamiento para otros idiomas.
Durante el desarrollo del proyecto, hemos intentado varias formas de automatizar la detección de tales problemas:
Limitar la versión del lenguaje C# requiere disciplina por parte de los programadores de C# y a menudo es inconveniente. Para sortear estas limitaciones, la traducción se puede realizar en dos etapas: primero, reemplazar las construcciones de las versiones modernas del lenguaje C# con análogos compatibles de estándares anteriores y luego proceder directamente a la traducción.
Al usar un traductor basado en un analizador obsoleto, la reducción solo se puede hacer usando herramientas externas (por ejemplo, utilidades escritas en base a Roslyn). Por otro lado, los traductores basados en Roslyn realizan ambas etapas secuencialmente, lo que permite usar el mismo código tanto al traducir código por ellos como al preparar código para la traducción por herramientas más antiguas.
Esto es similar a traducir el código del producto, pero implica requisitos algo diferentes. Al traducir una biblioteca de decenas de millones de líneas, es importante, ante todo, seguir lo más estrictamente posible el comportamiento del código C# original, incluso a expensas de la legibilidad; el código más simple pero con efectos diferentes tendrá que depurarse más tiempo. Por otro lado, los ejemplos de uso del código traducido deben parecer lo más simples posible, dejando claro cómo usar el código en C++, incluso si no corresponde al comportamiento de los ejemplos originales escritos en C#.
Por ejemplo, al crear objetos temporales, los programadores de C# a menudo usan la declaración using para evitar fugas de recursos y establecer estrictamente el momento de su liberación, sin depender del GC. La traducción estricta de using da como resultado un código C++ bastante complejo debido a los muchos matices del tipo "si se lanza una excepción en el bloque de la declaración using
y Dispose()
también lanza una excepción, ¿cuál termina en el contexto de captura?". Tal código solo confundirá al programador de C++, creando la impresión de que usar la biblioteca es difícil, pero de hecho es suficiente tener un puntero inteligente en la pila, que en el momento adecuado elimina el objeto y libera los recursos.
Las bibliotecas que proporcionan una API pueden documentarse a través de comentarios XML de acuerdo con las prácticas de C#. Transferir comentarios a C++, por ejemplo, en formato Doxygen, no es una tarea trivial. Además del marcado, es necesario reemplazar las referencias a tipos (ya que en C# los nombres completos se escriben con un punto, en C++ con un par de dos puntos), sus miembros y, en el caso de usar propiedades, también entender si es un getter o un setter. Además, traducir fragmentos de código que carecen de semántica y pueden estar incompletos.
Esta tarea se resuelve tanto por medio del propio traductor como por utilidades externas, por ejemplo, analizando la documentación XML generada y preparando adicionalmente fragmentos, como ejemplos de uso de métodos.
Como podemos ver, un marco profesional para la conversión de código, además de la traducción de alta calidad del código C# a C++, debería poder determinar la traducibilidad del código fuente, reducir la versión del lenguaje si es necesario, traducir ejemplos de uso de las bibliotecas convertidas y su documentación.