18 Dezember 2023
Kunden schätzen Aspose-Produkte, die die Manipulation von Protokollen und Dateien in beliebten Formaten ermöglichen. Die meisten von ihnen wurden ursprünglich für .NET entwickelt. Gleichzeitig laufen Geschäftsanwendungen für Dateiformate in verschiedenen Umgebungen. Dieser Artikel beschreibt, wie es uns gelungen ist, die Veröffentlichung von Aspose-Produkten für C++ einzurichten, indem wir einen Rahmen für die Codeübersetzung von C# aus aufgebaut haben. Die Aufrechterhaltung der Funktionalität der .NET-Versionen für diese Produkte war technisch anspruchsvoll.
Wir haben die notwendige Infrastruktur selbst entwickelt, um die Codeübersetzung zwischen Sprachen und die Emulation von .NET-Bibliotheksfunktionen zu ermöglichen. Dadurch haben wir ein Problem gelöst, das normalerweise als akademisch betrachtet wird. Dies ermöglichte es uns, monatlich .NET-Produkte für die C+±Sprache zu veröffentlichen, wobei der Code für jede Version aus dem entsprechenden C#-Code stammt. Darüber hinaus werden die Tests, die den ursprünglichen C#-Code abdeckten, zusammen mit ihm übersetzt, um die Funktionalität der resultierenden Lösung zu überwachen, auf Augenhöhe mit speziell geschriebenen Tests in C++.
Der Erfolg des C#-zu-C+±Codeübersetzers basiert auf den erfolgreichen Erfahrungen, die das CodePorting-Team bei der Einrichtung der automatisierten C#-zu-Java-Codeübersetzung gemacht hat. Der erstellte Rahmen wandelte C#-Klassen in Java-Klassen um und ersetzte dabei systeminterne Bibliotheksaufrufe ordnungsgemäß.
Für den Rahmen wurden verschiedene Ansätze in Betracht gezogen. Die Entwicklung reiner Java-Versionen von Grund auf würde zu viele Ressourcen erfordern. Eine Möglichkeit bestand darin, die Aufrufe vom Java-Code in die .NET-Umgebung zu marshalling, aber dies würde den Satz von Programmierplattformen einschränken, die wir in Zukunft unterstützen könnten. Damals war .NET nur auf Windows vorhanden. Das Marshalling von Aufrufen ist bequem, wenn selten auftretende Aufrufe weit verbreitete Datentypen tragen. Es wird jedoch überwältigend, wenn man mit vielen Objekten und benutzerdefinierten Datentypen arbeitet.
Stattdessen fragten wir uns, wie wir vorhandenen Code vollständig auf eine neue Plattform übersetzen können. Dies war ein aktuelles Problem, da die Code-Migration monatlich und für alle Produkte durchgeführt werden musste, was einen synchronisierten Fluss von ähnlich ausgestatteten Versionen produzierte.
Die Lösung wurde in zwei Teile aufgeteilt:
Die folgenden Argumente bestätigten, dass der Plan technisch machbar war:
System.Net
und System.Drawing
waren.Wir werden nicht weiter auf den C#-zu-Java-Übersetzer eingehen, da dies spezielle Artikel erfordern würde. Zusammenfassend lässt sich sagen, dass die Umwandlung von C#-Produkten in Java dank des erstellten Codeübersetzers zur regelmäßigen Praxis des Unternehmens geworden war. Der Übersetzer hatte sich von einem einfachen regelbasierten Texttransformator zu einem komplizierten Codegenerator entwickelt, der mit der AST-Repräsentation des Quellcodes arbeitet.
Der Erfolg des C#-zu-Java-Übersetzers half uns, den Java-Markt zu betreten, und es wurde die Idee aufgebracht, auch für C++ nach dem gleichen Szenario zu veröffentlichen.
Um die Veröffentlichung einer C+±Version unserer Produkte zu ermöglichen, musste ein Framework erstellt werden, das es uns ermöglichte, C#-Code in C++ zu übersetzen, zu kompilieren, zu testen und an den Kunden zu senden. Der Code bestand aus einer Reihe von Bibliotheken, von denen jede mehrere Millionen Zeilen Code enthielt. Die Bibliothekskomponente des Codeübersetzers musste Folgendes abdecken:
Viele Leser werden sich wahrscheinlich fragen, warum wir keine vorhandenen Lösungen wie das Mono-Projekt in Betracht gezogen haben. Es gab mehrere Gründe dafür:
Theoretisch könnten wir unseren Übersetzer verwenden, um eine vorhandene Lösung in C++ zu konvertieren. Dies würde jedoch erfordern, dass von Anfang an ein voll funktionsfähiger Übersetzer vorhanden ist, da es unmöglich ist, jeden übersetzten Code ohne eine Systembibliothek zu debuggen. Außerdem würden die Optimierungsprobleme noch wichtiger als für den übersetzten Produktdaten-Code, da Systembibliotheksaufrufe zu Engpässen tendieren.
Kehren wir zu unseren Anforderungen an den Code-Übersetzer zurück. Aufgrund der Unfähigkeit, .NET-Typen auf STL-Typen abzubilden, haben wir uns entschieden, benutzerdefinierte Bibliothekstypen als Ersatz zu verwenden. Die Bibliothek wurde als Satz von Adaptern entwickelt, die es ermöglichen, Funktionen von Drittanbieterbibliotheken über eine .NET-ähnliche API zu nutzen (wie auch in Java).
Da wir die Bibliotheken mit bestehender API übersetzten, war es eine wichtige Anforderung, dass der übersetzte Code innerhalb jeder Kundenanwendung laufen sollte. Daher konnten wir keine Garbage Collection für den übersetzten Code verwenden, da sie die gesamte Anwendung abdecken würde. Stattdessen musste unser Speicherverwaltungsmodell für C+±Entwickler verständlich sein. Die Verwendung von Smart Pointern wurde als Kompromiss gewählt. Wir werden beschreiben, wie es uns gelungen ist, das Speichermodell in einem separaten Artikel zu ändern.
CodePorting hat eine starke Testabdeckungskultur, und die Fähigkeit, die für C#-Code geschriebenen Tests auf C+±Produkte anzuwenden, würde die Fehlersuche erheblich vereinfachen. Der Code-Übersetzer musste auch in der Lage sein, die Tests zu übersetzen.
Anfangs erlaubte die manuelle Korrektur des übersetzten Java-Codes, die Entwicklung und die Produktveröffentlichungen zu beschleunigen. Langfristig erhöhte dies jedoch die Ausgaben, die benötigt wurden, um jede Version für die Veröffentlichung vorzubereiten, da jeder Übersetzungsfehler jedes Mal behoben werden musste, wenn er auftrat. Dies könnte handhabbar sein, indem der resultierende Java-Code mit Patches gefüttert wird, die als Differenz zwischen den Ausgaben des Übersetzers für zwei aufeinanderfolgende C#-Code-Revisionen berechnet werden, anstatt ihn jedes Mal von null zu konvertieren. Dennoch wurde entschieden, die Behebung des C+±Frameworks gegenüber der Behebung des resultierenden Codes zu priorisieren, sodass jeder Übersetzungsfehler nur einmal behoben wurde.