texttransformer.jpg

Januar 2021 ist Nokia Solutions & Networks (Bangalore, India) auf das Angebot einer Zusammenarbeit*) eingegangen. Innerhalb des zweimonatigen Supports wurde ein Programm einer Netzwerksoftware von Delphi nach C++ übersetzt. Die gesamte Software bestand aus mehreren Programmen, die gemeinsamen Hilfscode von ca. 1,2 MB verwendeten. In etwa die erste Hälfte der Zeit wurde zur Korrektur des automatisch übersetzten Codes verwendet, um das Programm kompilieren und ausführen zu können. In der restlichen Zeit wurde das Programm dann debuggt. Parallel dazu wurde mit den gewonnenen Erkenntnissen Delphi2Cpp 2.x verbessert.

Im Code werden viele verschachtelte Routinen verwendet und dabei des öfteren auch solche, die sich selbst in ihrem Funktionskörper aufrufen. Dafür mussten manuelle Korrekturen vorgenommen werden. Diese sind mit dem neuen Updates von Delphi2Cpp 2.x nun nicht mehr nötig.

Die Software verwendet die Komprimierungsbibliotheken zlib und libbz2. Mittels einer Übersetzung von "System.ZLib" gelingt nun die nahtlose Integration dieeser Bibiotheken in den übersetzten Code.

Nachbearbeitungen sind an den Stellen nötig, wo WINAPI-Befehle oder Assembler-Code ausgeführt wird. Die Konvertierung des Assmbler-Codes wurde vom Auftraggeber geleistet.

Einige kritische Spezialfälle, die bei der Übersetzung weiterhin nicht automatisch korrekt behandelt werden können, sollen im Folgenden dargestellt werden.


"String*" und "TByte*" anstelle des einfachen Delphi "Pointer"

Vor zentraler Bedeutung in der zu konvertierenden Delphisoftware ist eine eigene Listencontainer-Klasse deren Elemente von einer abstrakten Elementklasse abgeleitet sind. In dieser Elementklasse gibt es sowohl eine abstrakte Methode, die einen Schlüssel vom Typ "Pointer" liefert, als auch eine abstrakte Methode zum Vergleich dieser Schlüssel. Die jeweiligen Vergleichsfunktionen vergleichen nicht die die Zeiger selbst, sondern jeweils das, worauf sie Zeigen. Je nach Listenelement können dies verschiedene Integertypen sein, aber auch Strings oder TBytes. Hier erwies es sich als notwendig nicht die enthaltenen Zeiger auf die jeweilengen Arrays zu speichern, sondern einen Zeiger auf einen String - String* - bzw auf ein TBytes - TBytes* - zu verwenden. Insbesondere im letzteren Fall lässt sich in C++ vector<Byte>::data() nicht der Container vector<Byte> restituieren. Auf der anderen Seite gibt es diverse Records die im varianten Teil verschiedene Typen speichern können. Auch hier war es erforderlich den in Delphi verwendeten abstrakten "Pointer"-Typ durch konkrete "String*" bzw. TByte*" Typen zu ersetzen.
Die Möglichkeit automatischer Übersetzung findet hier ein Ende. Ein automatischer Übersetzer kann natürlich nicht erkennen, wofür ein "Pointer" tatsächlich steht.


Konstruktion eines Objekts aus einer Klassenreferenz bei Übergabe eines Parameters

Das zweite Beispiel für eine notwendige manuelle Nachbearbeitung betrifft erneut die schon genannte Listencontainer-Klasse und die Hierarchie der Listenelemente. Im Konstruktor des Conainers wird ein Element durch Aufruf der Konstruktormethode einer Klassenreferenz des Elements erzeugt.

constructor TContainer.Create(S : ...; ObjectType : TElementClass);
var
i : Integer;
begin

Items[i] := ObjectType.Create(S);


Solche Klassenreferenzen gibt es in C++ aber nicht. Durch den trickreichen Hilfscode für Delphi2Cpp 2.x ist es lediglich möglich Klassenreferenzen zu simulieren, deren Defaultkonstruktor aufgerufen kann.

Items[i] := ObjectType.Create();

Um den Delphi-Code dennoch zu reproduzieren, muss der Teil des Konstuktorcodes, der vom übergebenen Parameter abhängt (im aktuellen Fall eine Stream-Klasse), manuell in eine gesonderte Funktion extrahiert werden. Der Element-Typ wird dann durch Aufruf des Default-Konstruktors erzeugt und im Anschluß daran wird die Initialisierung mithilfe der abgespaltenen Funktion ausgeführt.

TElement* p = ObjectType->Create();
p->Create(s);

Diese Aufspaltung des Konstruktors muss für alle aus der Element-Klasse abgeleiteten Klasse manuell durchgeführt werden.


Aufruf einer virtuellen Klassenfunktion innerhalb von Konstruktoren

Im Delphi Quellcode gibt es eine Hierarchie von Klassen mit einer virtuellen Klassenfunktion "Id", die einen Bezeichner zurückliefert, der spezifisch für die jeweilige Klasse ist:

class function Id : string; virtual; abstract;

Im Konstruktor der Basiklasse wird nun in Abhängigkeit vom Bezeichner bestimmter Code ausgeführt. In C++ gibt es keine virtuellen statischen Methoden. Daher wird diese Methode als einfache virtuelle Methode übersetzt. Wird eine abgeleitete Klasse konstruiert, so wird dabei auch der Konstruktor der Basisklasse ausgeführt. In Delphi liefert dir Id-Funktion hier den Bezeichner der abgeleiteten Klasse. In C++ hingegen wird der Bezeichner der Basisklasse geliefert.

In diesem Fall musste wiederum händisch für die Basiklasse ein ergänzender Konstruktor mit dem Bezeichner als Parameter geschrieben werden. Im Konstruktor der abgeleiteten Klasse wird nun der neue Konstruktor der Basisklasse mit dem Bezeichner der abgeleiteten Klasse als Parameter aufgerufen.

TDerived(): inherited(Id()) {}



*) Das Angebot zur Zusammenarbeit besteht weiterhin, nun aber mit reduzierter Supportzeit.


   english english

 

 
Letzte Neuigkeiten
29.01.24
Aurora2Cpp: Delphi 7 Konverter [more...]

19.10.23
Delphi2Cpp 2.3: Konvertierung von DFM-Dateien [more...]



[aus Fallstudie...]

"Eine Meisterleistung -- Delphi2Cpp hat alle meine Erwartungen weit übertroffen."


Tony Hürlimann
virtual-optima 29.08.2011



"Ich muss mich nochmal für deinen Einsatz und die Qualität deiner Arbeit bedanken, das ist absolut überdurchschnittlich ..."


Gerald Ebner


 
Diese Homepage ist aus einfachen Texten mit [Minimal Website ]generiert.

Minimal Website
Minimal Website ist mit Hilfe des TextTransformers hergestellt.

TextTransformer
Der TextTransformer ist gemacht mit dem Borland CBuilder

  borland