15 Februar 2024
Auf den ersten Blick mag es so aussehen, als hätte der Übersetzer nur eine Möglichkeit zur Verwendung: Indem wir ihm C#-Code zuführen, erwarten wir äquivalenten C++-Code als Ausgabe. Tatsächlich ist dies der häufigste Weg, aber bei weitem nicht der einzige. Im Folgenden sind weitere Modi aufgeführt, die vom Code-Übersetzungssystem und verwandten Dienstprogrammen bereitgestellt werden.
Produkte werden von Programmierern entwickelt, die die Details des Verfahrens zur Übersetzung von Code in andere Sprachen und die damit verbundenen Einschränkungen kennen müssen. Dadurch entstehen Situationen, in denen aus Sicht der C#-Entwickler vorgenommene Änderungen den Freigabeprozess für andere Sprachen beeinträchtigen.
Während der Projektentwicklung haben wir verschiedene Möglichkeiten ausprobiert, um die Erkennung solcher Probleme zu automatisieren:
Die Begrenzung der C#-Sprachversion erfordert Disziplin von C#-Programmierern und ist oft umständlich. Um diese Einschränkungen zu umgehen, kann die Übersetzung in zwei Schritten erfolgen: Zuerst werden die Konstrukte moderner Versionen der C#-Sprache durch unterstützte Analoga aus vergangenen Standards ersetzt, und dann erfolgt die direkte Übersetzung.
Bei Verwendung eines Übersetzers auf Basis eines veralteten Parsers kann die Herabsetzung nur mithilfe externer Tools (z. B. Dienstprogramme, die auf Roslyn basieren) erfolgen. Andererseits führen Übersetzer auf Basis von Roslyn beide Schritte nacheinander aus, sodass derselbe Code sowohl beim Übersetzen durch sie als auch beim Vorbereiten des Codes für die Übersetzung durch ältere Tools verwendet werden kann.
Dies ähnelt der Übersetzung von Produktcode, erfordert jedoch etwas unterschiedliche Anforderungen. Bei der Übersetzung einer Bibliothek mit zig Millionen Zeilen ist es wichtig, zunächst das Verhalten des ursprünglichen C#-Codes so genau wie möglich zu befolgen, selbst auf Kosten der Lesbarkeit – einfacher, aber unterschiedlicher Code muss länger debuggt werden. Andererseits sollten Beispiele zur Verwendung des übersetzten Codes so einfach wie möglich aussehen, um klarzustellen, wie der Code in C++ verwendet werden kann, auch wenn er nicht dem Verhalten der ursprünglichen Beispiele entspricht, die in C# geschrieben wurden.
Beispielweise verwenden C#-Programmierer beim Erstellen temporärer Objekte oft die using-Anweisung, um Ressourcenlecks zu vermeiden und den Zeitpunkt ihrer Freigabe strikt festzulegen, ohne sich auf den Garbage Collector zu verlassen. Eine strenge Übersetzung von “using” ergibt jedoch recht komplexen C++-Code aufgrund vieler Nuancen wie "Wenn eine Ausnahme im Block der using-Anweisung ausgelöst wird und Dispose()
ebenfalls eine Ausnahme auslöst, welche gelangt in den Catching-Kontext?". Solcher Code könnte den C+±Programmierer irreführen und den Eindruck erwecken, dass die Verwendung der Bibliothek schwierig ist. Tatsächlich reicht es jedoch aus, einen intelligenten Zeiger auf dem Stack zu haben, der das Objekt zum richtigen Zeitpunkt löscht und die Ressourcen freigibt.
Bibliotheken, die eine API bereitstellen, können gemäß C#-Praktiken durch XML-Kommentare dokumentiert werden. Die Übertragung von Kommentaren nach C++, beispielsweise im Doxygen-Format, ist keine triviale Aufgabe. Neben der Markierung müssen Verweise auf Typen ersetzt werden (da in C# vollständige Namen mit einem Punkt geschrieben werden und in C++ mit einem Doppelpunkt), ihre Mitglieder und im Fall von Eigenschaften muss auch verstanden werden, ob es sich um einen Getter oder einen Setter handelt. Darüber hinaus sollten Codefragmente übersetzt werden, die keine Semantik haben und unvollständig sein können.
Diese Aufgabe wird sowohl durch die Mittel des Übersetzers selbst als auch durch externe Dienstprogramme gelöst, z. B. durch die Analyse der generierten XML-Dokumentation und die zusätzliche Vorbereitung von Fragmenten wie Beispielen zur Verwendung von Methoden.
Wie wir sehen können, sollte ein professionelles Framework für die Codekonvertierung neben einer hochwertigen Übersetzung von C#-Code nach C++ auch in der Lage sein, die Übersetzbarkeit des Quellcodes zu bestimmen, die Sprachversion gegebenenfalls herabzusetzen und Beispiele zur Verwendung der konvertierten Bibliotheken sowie deren Dokumentation zu übersetzen.