Cpp2Java


Das ambitionierteste Projekt, das jemals mit dem TextTransformer unternommen wurde, ist die Übersetzung von einer in C++ verfassten Firmensoftware nach Java. C++ ist eine sehr komplexe Programmiersprache und es gibt bisher keinen öffentlich verfügbaren, brauchbaren Parser dafür. Einen solchen zu bauen war auch nicht Ziel des Projekts. Es wurde vielmehr von vornherein darauf beschränkt, dass es lediglich der Lage sein sollte die ca. 120000 Zeilen des vorhandenen Quellcodes zu parsen. An der automatischen Konversion des geparsten Codes sollte mit vertretbarem Aufwand gearbeitet werden. Ziel war, dass eine abschließende manuelle Nachbearbeitung möglichst gering sein sollte.
Rein äußerlich betrachtet scheint eine solche Übersetzung gar nicht so schwierig, da sich der Code der beiden objektorientierten Sprachen oft sehr ähnlich sieht. Mit einer direkten Reproduktion des Quellcodes tappt man aber leicht in die Falle. Z.B. gibt es in Java keine überladenen Operatoren wie in C++. Die Zuweisung eines string-Werts an ein anderes string-Objekt, die in C++ automatisch über dessen Zuweisungsoperator verläuft, würde in Java schlicht das Kopieren der Objekt-Referenz selbst bedeuten. Stattdessen wäre die explizite assign-Methode des Objekts aufzurufen. Eine Zuweisung von Pointern hingegen wäre korrekt. Ähnlich muss zum Test der Äquivalenz der Werte zweier Objekte die "equals"-Methode aufgerufen werden statt des "=="-Operators. Derartige Tücken gibt es viele zu beachten und es war demnach erforderlich bei der Ausgabe des Java-Codes die Typinformationen der jeweiligen Objekte parat zu haben und zu beachten. In Zweifelsfällen wurde nicht etwa versucht kompilierbaren Java-Code zu generieren, sondern der Java-Compiler sollte genötigt werden entsprechende Fehlermeldungen zu erzeugen.

Im Folgenden wird in groben Zügen auf die einzelnen Stufen der Übersetzung eingegangen.


Ersetzung der Präprozessor-Direktiven

Bevor mit einer Übersetzung der C++ Quelldateien begonnen werden kann, müssen die Präprozessor-Direktiven darin ersetzt werden. Das sollte in einer Weise geschehen, die den Sinn dieser Direktiven bewahrte.

  • für definierte Konstanten wurden "echte" C++ Konstanten in den Code eingefügt
  • etliche Makros wurden nicht aufgelöst, sondern durch Funktionen ersetzt
  • Kommentare wurden im Code belassen
  • Header der System- und Bibliotheks-Dateien wurden nicht eingeschlossen. Ihr Inhalt sollte direkt durch Java-Analoga substituiert werden.
  • für jeden Firmen-Header wurde ein entsprechender vorverarbeiteter Header erzeugt und die Include-Anweisungen für diese Header wurden deshalb in den Quellcode-Dateien belassen.

Den C-Präprozessor ohne diese Spezialbehandlungen gibt es hier:

http://www.texttransformer.org/c_pp_en.html


C++-Parser

Das Projekt basiert auf einer C++-Grammatik für ANTLR von David Wigg und anderen:

http://www.antlr.org/grammar/1132152279791/CPP_parser_v_3.1.zip

Das Projekt wurde halbautomatisch in den TextTransformer importiert und dort von Hand soweit ergänzt, dass es alle Quelldateien parsen konnte. Auf besondere Weise wurde mit den erkannten Typdefinitionen, Variablendeklarationen etc. verfahren.


Typdefinitionen

Sobald in einer Typdefinition ein Bezeichner erkannt wird, der für einen Typ steht, wird er einem entsprechenden Platzhalter-Token "TYPE" hinzugefügt. Wenn der Typ dann an späterer Stelle im Code verwendet wird, wird er direkt als solcher erkannt. Wenn z.B.
durch:

typedef unsigned int uint;

der neue Typ "uint" eingeführt wird, wird an späteterer Stelle die Deklaration:

uint ui;

geparst als: TYPE ID ";".

Ebenso verhält es sich mit den Bezeichnern für Klassen, Namensräumen oder "typenames"
in Template-Deklarationen.

Damit dieses dieses Verfahrens funktionieren kann, müssen stets synchron mit dem Parsen des Codes auch die jeweiligen Scopes aktualisiert werden, in denen die Typen etc. existieren, oder auch nicht existieren, Diese Aktualisierung hat nicht nur im Hauptparser zu erfolgen, sondern auch, wenn in einer Vorausschau neue Typen erkannt werden.

Die C++-Bibliotheken, insbesondere die stl wurden nicht übersetzt, sondern soweit nötig simuliert. D.h. in einer Initialisierungsfunktion wurden die benötigten Deklarationsinformationen in entsprechende Tabellen (Baumstrukturen) eingetragen.


Baumstruktur

Der Quellcode der Firmensoftware war durchgängig so strukturiert, dass es für jede Klasse eine einzelne Datei mit zugehörigem Header gab. In Java gibt es für jede Klasse nur eine Datei. Für die Übersetzung wurden daher zunächst ein Baum aus der Klassendeklaration des Headers aufgebaut, in den dann die Funktions-Implementationen der C++-Datei eingehängt wurden. Die C++-Dateien werden also in den Header integriert.

Schematisches Beispiel:

class CClass
{
public:

CClass(int i);

void setter(int i);

private:
int m_i;
};

CClass::CClass(int i)
 : m_i(i) {}

void CClass::setter(int value)
{
  m_i = value;
}

=> Java

public class CClass
{
public CClass(int i) { m_i = i; };
public void setter(int value) { m_i = value; }
private int m_i;
};

An dem Beispiel ist zu sehen, dass bei diesem Verfahren auf die Namen der Funktionsparameter achtzugeben ist. Falls es Defaultwerten in den Funktionsdeklarationen gibt, wird die Funktion in eine Reihe gleichnamiger Funktionen mit verschiedener Anzahl von Parametern aufgesplittet, die allesamt in den Baum eingefügt werden.





Generierung des Java-Codes

Wenn der Baum so erzeugt ist, wie eben beschrieben, erfolgt die Java Ausgabe in der richtigen Ordnung, indem der Baum von oben nach unten durchlaufen wird und die jeweiligen Blätter dabei ausgegeben werden. Beim Schreiben müssen allerdings, wie im Vorspann erwähnt, die Merkmale der jeweiligen Typen beachtet werden. Das geschieht folgendermaßen:

Im TextTransformer kann man "Funktionstabellen" anlegen, in denen jedem Label eines Baumknotens eine Funktion zugewiesen wird, die ausgeführt wird, wenn dieser Knoten durchlaufen wird. Z.B. kann den Knoten die Äquivalenzausdrücke umfassen eine solche Funktionen zugewiesen werden.

m_ftPrint.add("equality_expression", Print_equalitiy_expression);


In "Print_equalitiy_expression" werden nun die Hilfsfunktionen "IsObjectType" und "IsNonObjectType" verwendet, um zwischen dem Aufruf einer "equals"-Funktion und der einfachen Verwendung des Operators zu unterscheiden. Die Eigenschaften zur Ermittlung des Objekttyps sind im Baum und in externen Tabellen als Knotenattribute gespeichert. Das Cpp2Java-Projekt ist in diesem Punkt allerdings noch nicht fertig gestellt. Die Ermittlung resultierenden Typs ist für komplexe Ausdrücke noch nicht implementiert.


Inzwischen ist die Auswertung von Typinformationen im Programm Delphi2Cpp sehr viel weiter entwickelt worden. Delphi2Cpp konvertiert Delphi-Code nach C++.



   english english

 
Letzte Neuigkeiten
10.08.10
TextTransformer 1.7.5 Verbesserte Navigation [mehr...]

28.01.10
TextTransformer 1.7.4 node-Sortierung [mehr...]



"...Fantastic!!!!

... You have exceeded my expectations and I love your product. We will get a lot of use out of it in the future for other projects."


Charles Finley
xformix 23-02-07


top_prize_winner.png

I was extremely impressed with your components and tools. Not only extremely powerful but very professionally done and well documented, etc. Really quality work, congratulations

mouser (First Author, Administrator)


 
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