Inhalt
Topic:.SourceCodeGen.
Generierte Quelltexte, Quellcodes oder Sourcecodes sind eigentlich keine Quellen, sondern Generate. Da sie aber meist nicht anders aussehen als handgeschriebene Quellen und sich im Generierprozess auch daneben einordnen, als Quelle für die weitere Generierung (Compiler, Linker) dienen, ist die Bezeichnung Quelltext nicht grundfalsch. Für eine eindeutige Bezeichnung kann von Sekundärquellen sprechen.
Topic:.SourceCodeGen.C2Asm.
Ein C-Compiler übersetzt nicht direkt in Maschinencode des Zielprozessors, sondern immer zuerst in Assembler. Danach erfolgt der Assemblerlauf, meist verbunden mit einer nochmaligen Codeoptimierung. Den Assember-Sekundärquelltext kann man sich ansehen, im List-File. Dieser Vorgang ist meist verborgen, es sieht so aus, als dass C direkt in einen Object-File übersetzt. Niemand würde heutzutage auf die Idee kommen, den vom C-Compiler erzeugten Assemblercode nochmal nachzuarbeiten. Es handelt es sich hier um eine langjährig bewährte Praxis einer Quellcode-Generierung. Man kann diese Praxis betrachten und daraus Schlussfolgerung für Quellcodegenerierungen ziehen, die noch nicht gängige Praxis sind aber es werden können.
C und Assembler sind relativ gut aufeinander abgebildet. Das ist eine These. Gemeint ist folgendes: Eine Zuweisung in C ist auch eine Zuweisung in Assembler (zu Registern oder Beschreiben von Speicherbereichen), wird nur etwas anders geschrieben. Ein Subroutinenaufruf in C hat auch in Assembler sein entsprechendes Gegenstück: Ein paar mehr Befehle, Argumente in den Stack laden, dann ein call-Befehl, aber im Grunde ist es das gleiche. Man kann C programmierend gut darüber nachdenken, was die CPU in etwa für Maschinenbefehle bekommt.
C ist aber viel eleganter schreib- und lesbar als Assembler. Das ist einer der Gründe für eine Assembler-Quellcodegenerierung aus der höheren Form C. Es ist gegebenfalls der wichtigste Grund für den Programmierer, aber nicht der Hauptgrund dieser Entwicklung.
Ein C-Quellcode ist unabhängig von der Ablaufumgebung, Assembler-Sourcen sind es nicht. Dieser Grund ist ein wichtiger. Mit C gelingt die portable Programmierung. Ein C-Quelltext kann funktionell erstmal am PC vorgetestet werden, bevor er in eine Embedded-Control-Zielhardware eingebracht wird. Der selbe Algorithmus kann mit unverändertem Quelltext woanders wiederverwendet werden. Die Anpassung an die Ziel-Plattform leistet der zielsystemspezifische C-Compiler.
In Assembler müssen Dinge bedacht und durchdacht werden, die der C-Compiler automatisch übernehmen kann. Das sind im wesentlichen die Entscheidung, was in welche CPU-Register geladen wird. In der Assemblerprogrammierung nehmen diese Überlegungen einen nicht unbedeutenden Teil der Programmierarbeit in Anspruch. Ein C-Compiler hat Algorithmen, mit denen er diese Dinge selbst und gegebenenfalls besser entscheiden kann. Ein weiteres Beispiel ist die Reihenfolge von Maschinenbefehlen, um möglichst optimal zu arbeiten. Auch hier ist ein Algorithmus meist besser als ein durchschnittlich guter Assemblerprogrammierer.
C kennt wichtige programmierunterstützende Regeln, der C-Compiler testet diese, Assembler nicht. Gemeint sind hier die Regeln der Strukturierten Programmierung und die der Sichtbarkeit und Strukturierung von Variablen (auch an C++ gedacht). In Assembler ist das alles sehr frei, man kann sonstwas für verzwackte Dinge programmieren.
Die Gründe, eine andere Sprache zu verwenden und die notwendige Zwischensprache (hier Assembler) daraus zu generieren sollten sind als gegeben. Der Generator (hier C-Compiler) enthalt einiges an Intelligenz, die sonst per Hand eingebracht werden müsste. Man kann aber nicht sagen, dass damit die Programmierung beschränkt sei. Die Freiheiten, die man in Assembler hätte gegenüber C, braucht man in der Regel nicht. Andererseits bietet C genügend Spielraum zur Lösung anstehender Probleme. Letzlich ist es möglich und nicht ungewöhnlich, dass bestimmte Dinge in einer C-Umgebung doch noch in Assembler formuliert werden, dort wo notwendig.
Topic:.SourceCodeGen.Requirements.
Es darf weder notwendig sein noch ein gewisser Wunsch aufkommen, den generierte Sekundär-Quellcode nachbearbeiten zu müssen oder wollen. Der primäre Quellcode muss alle Sprachmittel enthalten, der Generator muss diese umsetzen, um die Erfordernisse der Programmierung zu erfüllen. Für Bereiche, in denen das nicht möglich ist, muss man direkt im sonst sekundärem Quellcode primär programmieren (so wie man in C-Programmierumgebungen auf Treiber- und Systemebene schon mal auf Assembler zurückgreift).
Ein funktionaler Test muss vollständig auf Basis des primären Quellcodes ohne Betrachtung des sekundären Quellcodes möglich sein. Bei C versus Assembler ist das so. Die gängigen Entwicklungsumgebungen zeigen beim Debuggen immer unmittelbar den C-Quelltext und Werte in C-Daten an. Freilich geht das intern über eine Zuordnung Maschinencode - C-Quelltext-Zeilennummer. Für einen Anwender ist das aber nicht wesentlich und nicht sichtbar.
Die Abbildung des primären Quelltextes auf den generierten Sekundär-Quellcode muss bei Bedarf sichtbar und kontrollierbar sein. Geht man über einen funktionalen Test hinaus, möchte man bis auf Maschinenebene herunter, so ist das auch bei C / Assembler in der Regel möglich.
Die programmtechnischen notwendigen Quelltextformulierungen müssen im primären Quelltext ausgedrückt werden können. Ansonsten
hätte man eine Mogelpackung. Man stelle sich vor, in C ist es notwendig, immer wieder inline-Assembleranweisungen asm(...)
verwenden zu müssen. Dann wären einige Vorteile hinweg.
Topic:.SourceCodeGen.ApplicationScopes.
Als erstes sind hierbei Spezial-Progammiersprachen - Domain-Specific Languages (DSL) zu nennen: Für bestimmte Programmieraufgaben wird ein Set von Anweisungen definiert, mit dem ausgedrückt werden kann was für die Anwendungen notwendig ist. Solch ein Set ist viel weniger universell als beispielsweise C, C++ oder Java, aber der Anwender kann damit auch nicht beliebige Fehler machen. Sehr oft ist damit auch eine grafische Programmierung verbunden, da aus einer gezeichneten Grafik Datengehalte gelesen und in das spezielle Set konvertiert werden können. Um auf ein Zielsystem zu kommen, wird dann meist C oder C++ generiert, was unbesehen dann dem C-Compiler angeboten wird.
Eine Quellcodegenerierung aus UML-Modellen soll nicht zu den Anwendungsgebieten für Sekundär-Quellcodegenerierung dazugezählt werden. Denn: Zwei bis drei wichtige Forderung sind nicht erfüllt:
Im UML-Modell müssen an vielen Stellen Formulierungen in der Zielsprache erfolgen. Man hat hier alle Probleme, Offenheiten und Fehleranfälligkeiten der Zielsprache des Sekundär-Quellcodes (etwa C oder C++) wieder am Bein.
Daher ist es auch notwendig, auf der Ebene des Sekundär-Quellcodes zu testen.
Möglicherweise muss man auch noch entweder im UML-Modell durch Beeinflussung spezifischer Properties oder Generierungsregeln oder durch Abschalten der Modell-Kontrolle bestimmte Tricks anwenden, um eine notwendige Ziel-Quellcodeform zu bekommen.
Die Quellcodegenerierung aus UML scheitert grundsätzlich daran, dass UML eben nicht eine andere Programmiersprache oberhalb der textuell-prozeduralen Sprachen ist, sondern UML beziehungweise die Modellbasierende Softwareentwicklung öffet andere Betrachtungsebenen der Software, die parallel zur Quelltext-Programmierung stehen.
Eine Umsetzung Java2C soll hier nicht unerwähnt bleiben. Der Vorteil hier ist: Man kann vollständig objektorientiert in Java programmieren und testen, um dann auf einer Zielplattform diese Algorithmen in einer C-Umgebung zu implementieren. Typische C-Programmierfehler sind ausgeschlossen, da der der Java2C-Translator solche Fehler nicht vorsieht bzw. an Stellen, an denen etwa Type-Castings erfolgen, entweder die Richtigkeit des Castings algorithmisch im Java vorgetestet ist oder Befehle zur Überprüfung adäquat Java auch in C eingebaut werden. Man kann dies nicht vergessen oder nicht wollen, der Translator macht es. Bestimmte für C wichtige Dinge wie eine C-gemäße Datenorganisation werden in Java über Annotations in den Kommentaren mitgeteilt, die Richtigkeit wird wiederum vom Java2C-Translator gewährleistet beziehungsweise überprüft. Mit Einsatz dieser Technik kann ein erheblicher Testaufwand eingespart werden, wenn ansonsten eine Implementierung in C oder C++ notwendig ist.
Eine Generierung ist auch angebracht, wenn gemeinsame Schnittstellen in zwei Programmiersprachen notwendig sind. Man kann
gegebenfalls die Schnittstellen quellenmäßig in der einen Sprache pflegen, um sie nach Generierung auch in der anderen Sprache
verfügbar zu haben. Ein Beispiel dafür ist der Übersetzer C-Headerfiles zu Java-Code, der javadoc:_org/vishia/byteData/ByteDataAccess implementiert. Man kann auf diesem Weg Byte-Datenorganisationen in Telegrammen oder Binärfiles in als C-struct
beschreiben und über die daraus generiertem Java-Sourcen auch in Java direkt darauf zugreifen.
Interessant ist es oft, einen Mix aus generiertem Quellcode plus ergänzend manuellem primären Quellcode in einer niedrigeren Programmiersprache zu vereinen.