Ist die Generierung von Quellcode ein Anti-Pattern?


118

Wenn etwas generiert werden kann, dann sind das Daten, kein Code.

Ist diese Idee der Quellcode-Generierung nicht ein Missverständnis? Das heißt, wenn es einen Codegenerator für etwas gibt, warum dann nicht eine ordnungsgemäße Funktion daraus machen, die die erforderlichen Parameter empfangen und die richtige Aktion ausführen kann, die der "erzeugte" Code ausgeführt hätte?

Wenn es aus Performancegründen gemacht wird, dann klingt das nach einem Manko des Compilers.

Wenn es darum geht, zwei Sprachen zu verbinden, dann klingt das nach einem Mangel an Schnittstellenbibliothek.

Vermisse ich hier etwas?

Ich weiß, dass Code auch Daten sind. Was ich nicht verstehe ist, warum Quellcode generieren ? Warum nicht daraus eine Funktion machen, die Parameter akzeptiert und auf diese einwirkt?


11
Ein mit der Codegenerierung verbundener Begriff ist Metaprogramming
UselesssCat

4
en.wikipedia.org/wiki/Code_as_data , Lisp, FP, Scripting, Metaprogramming, Von Neumann / modifizierte Harvard-Architektur usw. Es wurde ad nauseam behandelt . Die Unterscheidung zwischen "Quellcode" und "Ausgabecode", "Code" und "Daten" usw. soll die Sache vereinfachen . Sie sollten niemals dogmatisch sein .
Vaxquis

9
@Utku, die besseren Gründe für die Codegenerierung liegen häufig darin , dass Sie eine Beschreibung auf höherer Ebene bereitstellen möchten, die Ihre aktuelle Sprache nicht ausdrücken kann . Ob der Compiler effizienten Code erstellen kann oder nicht, hat eigentlich nichts damit zu tun. Betrachten Sie Parser-Generatoren - ein Lexer, der von flexoder ein Parser, der von generiert bisonwird, ist mit ziemlicher Sicherheit vorhersehbarer, korrekter und oft schneller auszuführen als in C handgeschriebene Äquivalente. und aus weitaus weniger Code erstellt (was auch weniger Wartungsaufwand bedeutet).
Charles Duffy

1
Vielleicht kommen Sie aus einer Sprache, die nicht viele funktionale Elemente hat, aber in vielen Sprachen sind Funktionen erstklassig - Sie können sie weitergeben. In diesen Arten von Sprachen sind Code Daten und Sie können sie einfach so behandeln.
Restioson

1
@Restioson in einem funktionalen Sprachcode sind keine Daten. Erstklassige Funktionen bedeuten genau das: Funktionen sind Daten. Und nicht unbedingt besonders gute Daten: Sie können sie nicht unbedingt nur ein wenig mutieren (wie zum Beispiel alle Additionen innerhalb der Funktionen in Subtraktionen mutieren). Code ist Daten in homoikonischen Sprachen. (Die meisten homoikonischen Sprachen haben erstklassige Funktionen. Das Gegenteil ist jedoch nicht der Fall.)
Lyndon White

Antworten:


150

Ist die Generierung von Quellcode ein Anti-Pattern?

Technisch gesehen ist Code, der generiert wird, keine Quelle, selbst wenn es sich um Text handelt, der von Menschen gelesen werden kann. Quellcode ist Originalcode, der von einem Menschen oder einer anderen wahren Intelligenz generiert wurde, nicht mechanisch übersetzt und nicht unmittelbar (direkt oder indirekt) aus einer (wahren) Quelle reproduzierbar ist.

Wenn etwas generiert werden kann, sind das Daten, kein Code.

Ich würde sagen, alles sind Daten . Sogar Quellcode. Vor allem Quellcode! Quellcode besteht nur aus Daten in einer Sprache, die für die Ausführung von Programmieraufgaben entwickelt wurde. Diese Daten müssen übersetzt, interpretiert, kompiliert und nach Bedarf in andere Datenformen umgewandelt werden, von denen einige möglicherweise ausführbar sind.

Der Prozessor führt Anweisungen aus dem Speicher aus. Derselbe Speicher, der für Daten verwendet wird. Bevor der Prozessor Befehle ausführt, wird das Programm als Daten in den Speicher geladen .

So sind alle Daten , auch Code .

Ist diese ganze Idee der Codegenerierung angesichts der Tatsache, dass [generierter Code Daten sind], nicht ein Missverständnis?

Es ist vollkommen in Ordnung, mehrere Schritte in der Kompilierung zu haben, von denen einer eine Zwischencodegenerierung als Text sein kann.

Das heißt, wenn es einen Codegenerator für etwas gibt, warum dann nicht eine ordnungsgemäße Funktion daraus machen, die die erforderlichen Parameter empfangen und die richtige Aktion ausführen kann, die der "erzeugte" Code ausgeführt hätte?

Das ist eine Möglichkeit, aber es gibt noch andere.


Die Ausgabe der Codegenerierung ist Text, der für die Verwendung durch einen Menschen entwickelt wurde.

Nicht alle Textformen sind für den menschlichen Verzehr bestimmt. Insbesondere ist generierter Code (als Text) in der Regel für den Compilerverbrauch und nicht für den menschlichen Verbrauch gedacht .


Der Quellcode wird als das Original betrachtet: der Master - was wir bearbeiten und entwickeln; Was wir mit der Quellcodeverwaltung archivieren. Generierter Code wird in der Regel aus dem ursprünglichen Quellcode neu generiert, auch wenn er für Menschen lesbar ist . Generierter Code muss im Allgemeinen nicht der Quellcodeverwaltung unterliegen, da er während der Erstellung neu generiert wird.


1
Kommentare sind nicht für eine längere Diskussion gedacht. Diese Unterhaltung wurde in den Chat verschoben .
maple_shaft

65

Praktisches Denken

OK, ich weiß, dass Code auch Daten sind. Was ich nicht verstehe ist, warum Quellcode generieren?

Ich gehe davon aus, dass Sie in dieser Ausgabe eher praktische Fragen stellen, nicht theoretische Informatik.

Der klassische Grund für die Generierung von Quellcode in statischen Sprachen wie Java war, dass Sprachen wie diese einfach nicht mit benutzerfreundlichen In-Language-Tools für sehr dynamische Aufgaben ausgestattet waren. Zum Beispiel war es in den Anfangszeiten von Java einfach nicht möglich, eine Klasse mit einem dynamischen Namen (der einem Tabellennamen aus einer Datenbank entspricht) und dynamischen Methoden (der Attributen aus dieser Tabelle entspricht) mit dynamischen Datentypen (Matching) zu erstellen die Arten der genannten Attribute). Zumal Java sehr viel Wert darauf legt, dass Tippfehler bei der Kompilierung abgefangen werden können.

In einer solchen Umgebung kann ein Programmierer nur Java-Code erstellen und viele Codezeilen manuell schreiben. Oft wird der Programmierer feststellen, dass er bei jeder Änderung einer Tabelle zurückgehen und den Code entsprechend ändern muss. und wenn er das vergisst, passieren schlimme Dinge. Daher wird der Programmierer an den Punkt gelangen, an dem er einige Werkzeuge schreibt, die dies für ihn tun. Und damit beginnt die Straße zu immer intelligenterer Codegenerierung.

(Ja, Sie könnten den Bytecode im Handumdrehen generieren, aber eine solche Programmierung in Java wäre nichts, was ein zufälliger Programmierer tun würde, wenn er nur ein paar Zeilen Domänencode geschrieben hätte.)

Vergleichen Sie dies mit Sprachen, die sehr dynamisch sind, z. B. Ruby, die ich in den meisten Hinsichten als Gegensatz zu Java betrachten würde (beachten Sie, dass ich dies sage, ohne einen der beiden Ansätze zu bewerten; sie sind einfach unterschiedlich). Hier ist es 100% normal und üblich, zur Laufzeit dynamisch Klassen, Methoden usw. zu generieren, und vor allem kann der Programmierer dies trivial direkt im Code tun, ohne auf eine "Meta" -Ebene zu wechseln. Ja, Dinge wie Ruby on Rails kommen mit der Codegenerierung, aber wir haben in unserer Arbeit festgestellt, dass wir dies im Grunde genommen als eine Art fortgeschrittenen "Tutorial-Modus" für neue Programmierer verwenden, aber nach einer Weile wird es überflüssig (da es so wenig Code gibt) Um in diesem Ökosystem zu schreiben, dass, wenn Sie wissen, was Sie tun, das manuelle Schreiben schneller als das Aufräumen des generierten Codes ist).

Dies sind nur zwei praktische Beispiele aus der "realen Welt". Dann haben Sie Sprachen wie Lisp , wo der Code sind Daten, buchstäblich. Auf der anderen Seite gibt es in kompilierten Sprachen (ohne eine Laufzeit-Engine wie Java oder Ruby) (oder gab es, was ich mit modernen C ++ - Funktionen nicht mithalten konnte ...) einfach kein Konzept, Klassen- oder Methodennamen zur Laufzeit zu definieren. Daher ist die Codegenerierung der Build-Prozess das Werkzeug der Wahl für die meisten Dinge (andere C / C ++ - spezifische Beispiele wären beispielsweise flex, yacc usw.).


1
Ich denke, das ist besser als die Antworten, bei denen mehr Stimmen abgegeben wurden. Insbesondere das erwähnte Beispiel mit Java- und Datenbankprogrammierung bietet eine viel bessere Möglichkeit, den Grund für die Verwendung der Codegenerierung zu ermitteln und ist ein gültiges Tool.
Panzercrisis

Ist es heutzutage in Java möglich, dynamische Tabellen aus einer Datenbank zu erstellen? Oder nur mit einem ORM?
Noumenon

"(oder war, ich habe mit modernen C ++ - Funktionen nicht Schritt gehalten ...)" ist dies in C ++ dank Funktionszeigern seit über zwei Jahrzehnten möglich? Ich habe es nicht getestet, aber ich bin sicher, es sollte möglich sein, ein char-Array zuzuweisen, es mit Maschinencode zu füllen und dann einen Zeiger auf das erste Element auf einen Funktionszeiger zu setzen und es dann auszuführen. (Angenommen, die Zielplattform verfügt nicht über eine Sicherheitsmaßnahme, die Sie davon
abhält

1
msgstr "ein char - Array zuweisen, es mit Maschinencode füllen und dann einen Zeiger auf das erste Element auf einen Funktionszeiger setzen und dann ausführen?" Abgesehen davon, dass es sich um undefiniertes Verhalten handelt, ist es das C ++ - Äquivalent von "Generieren des Bytecodes im laufenden Betrieb". Es fällt in die gleiche Kategorie von "nicht von gewöhnlichen Programmierern berücksichtigt"
Caleth

1
@Pharap, "sicherlich ist dies in C ++ seit über zwei Jahrzehnten möglich" ... Ich musste ein wenig kichern; Es sind ungefähr zwei Jahrzehnte vergangen, seit ich C ++ zum letzten Mal codiert habe. :) Aber mein Satz über C ++ war trotzdem schlecht formuliert. Ich habe es ein bisschen geändert, es sollte klarer sein, was ich jetzt meinte.
AnoE

44

Warum Code generieren?

Denn das Programmieren mit Lochkarten (oder Alt-Codes im Notizblock ) ist mühsam.

Wenn es aus Performancegründen gemacht wird, dann klingt das nach einem Manko des Compilers.

Wahr. Die Leistung ist mir egal, es sei denn, ich bin dazu gezwungen.

Wenn es darum geht, zwei Sprachen zu verbinden, dann klingt das nach einem Mangel an Schnittstellenbibliothek.

Hmm, keine Ahnung wovon du sprichst.

Sieh mal so aus: Generierter und beibehaltener Quellcode ist immer und für immer ein Ärgernis. Es gibt nur einen Grund. Jemand möchte in einer Sprache arbeiten, während jemand anderes darauf besteht, in einer anderen Sprache zu arbeiten, und keiner kann sich die Mühe machen, herauszufinden, wie man zwischen ihnen interoperiert Sie wollen.

Was in Ordnung ist, bis ich es pflegen muss. An diesem Punkt können Sie alle sterben.

Ist es ein Anti-Muster? Seufz, nein. Viele Sprachen gäbe es nicht einmal, wenn wir nicht bereit wären, uns von den Mängeln früherer Sprachen zu verabschieden und den Code der älteren Sprachen zu generieren, wie viele neue Sprachen anfangen.

Es ist eine Codebasis, die in einem halb umgewandelten Frankenstein-Monster-Patchwork enthalten ist, das ich nicht ausstehen kann. Generierter Code ist unberührbarer Code. Ich hasse es, unantastbaren Code anzusehen. Trotzdem checken die Leute immer wieder ein. WARUM? Sie können auch die ausführbare Datei einchecken.

Nun, jetzt schimpfe ich. Mein Punkt ist, dass wir alle "Code generieren". Wenn du generierten Code wie Quellcode behandelst, machst du mich verrückt. Nur weil es so aussieht, als würde der Quellcode ihn nicht zum Quellcode machen.


41
Wenn Sie es generieren, handelt es sich nicht um SOURCE-Code. Es ist Zwischencode. Ich werde jetzt weinen gehen.
candied_orange

65
ARG !!! Es ist egal wie es aussieht !!! Text, Binär, DNA, wenn es nicht die QUELLE ist, sollten Sie sie nicht berühren, wenn Sie Änderungen vornehmen. Es geht niemanden etwas an, wenn mein Kompilierungsprozess 42 Zwischensprachen umfasst, die er durchläuft. Hör auf, sie zu berühren. Hören Sie auf, sie einzuchecken. Nehmen Sie Ihre Änderungen an der Quelle vor.
candied_orange

24
XML ist Text und nicht für den menschlichen Verzehr bestimmt. :-)
Nick Keighley

38
@utku: "Wenn etwas nicht dazu gedacht ist, von einem Menschen konsumiert zu werden, sollte es kein Text sein": Ich bin völlig anderer Meinung. Einige Gegenbeispiele aus der Luft: das HTTP-Protokoll, MIME-Codierungen, PEM-Dateien - so ziemlich alles, was base64 überall verwendet. Es gibt viele Gründe, Daten in einen sicheren 7-Bit-Stream zu verschlüsseln, selbst wenn es kein Mensch jemals sehen sollte. Ganz zu schweigen von dem viel größeren Raum von Dingen, mit denen ein Mensch normalerweise nie interagieren sollte, die er aber gelegentlich /etc/
tun

12
Ich denke nicht, dass "Programmieren mit Lochkarten" bedeutet, was Sie denken, dass es bedeutet. Ich war dort, ich habe das getan, und ja, es war ein Schmerz; aber es hat keine Verbindung zu "generiertem Code". Ein Lochkartenstapel ist nur eine andere Art von Datei - wie eine Datei auf der Festplatte oder eine Datei auf Band oder eine Datei auf einer SD-Karte. Früher haben wir Daten in Kartenspiele geschrieben und von diesen Daten gelesen. Wenn der Grund, warum wir Code generieren, darin besteht, dass das Programmieren mit Lochkarten mühsam ist, bedeutet dies, dass das Programmieren mit jeder Art von Datenspeicher mühsam ist.
Solomon Slow

41

warum generieren Sie Quellcode

Der häufigste Anwendungsfall für Codegeneratoren, mit dem ich in meiner Karriere arbeiten musste, waren Generatoren, die

  • hat eine Meta-Beschreibung auf hoher Ebene für eine Art Datenmodell oder Datenbankschema als Eingabe verwendet (möglicherweise ein relationales Schema oder eine Art XML-Schema)

  • und produzierte CRUD-Code für Datenzugriffsklassen als Ausgabe und möglicherweise zusätzliche Dinge wie entsprechende SQLs oder Dokumentation.

Der Vorteil hierbei ist, dass Sie aus einer Zeile einer kurzen Eingabespezifikation 5 bis 10 Zeilen fehlersicheren, typsicheren, fehlerfreien (vorausgesetzt, die Codegeneratorausgabe ist ausgereift) Code erhalten, den Sie ansonsten manuell implementieren und warten mussten. Sie können sich vorstellen, wie sehr dies den Wartungs- und Entwicklungsaufwand verringert.

Lassen Sie mich auch auf Ihre erste Frage antworten

Ist die Generierung von Quellcode ein Anti-Pattern?

Nein, nicht die eigentliche Generierung von Quellcode, aber es gibt tatsächlich einige Fallstricke. Wie in The Pragmatic Programmer dargelegt , sollte die Verwendung eines Codegenerators vermieden werden, wenn Code erzeugt wird, der schwer zu verstehen ist . Andernfalls kann der erhöhte Aufwand für die Verwendung oder das Debuggen dieses Codes den ersparten Aufwand leicht überwiegen, wenn der Code nicht manuell geschrieben wird.

Ich möchte auch hinzufügen, dass es in den meisten Fällen eine gute Idee ist, generierte Codeteile von manuell geschriebenem Code physisch so zu trennen, dass die Neuerstellung keine manuellen Änderungen überschreibt. Ich habe mich jedoch auch mehrmals mit der Situation befasst, in der die Aufgabe bestand, einen in alter Sprache X geschriebenen Code in eine andere, modernere Sprache Y zu migrieren, mit der Absicht, ihn anschließend in Sprache Y zu pflegen. Dies ist eine gültige Verwendung Fall für die einmalige Codegenerierung.


Ich stimme dieser Antwort zu. Mit Torque for Java kann ich Java-Quelldateien automatisch generieren, wobei die Felder mit der SQL-Datenbank übereinstimmen. Dies macht Rohöloperationen viel einfacher. Der Hauptvorteil ist die Typensicherheit, einschließlich der Möglichkeit, nur auf Felder zu verweisen, die in der Datenbank vorhanden sind (danke, dass Sie automatisch vervollständigen).
MTilsted

Ja, für statisch typisierte Sprachen ist dies der wichtige Teil: Sie können sicherstellen, dass Ihr handgeschriebener Code tatsächlich zu dem generierten Code passt.
Paŭlo Ebermann

"Einige in alter Sprache geschriebene Codes migrieren" - selbst dann kann die einmalige Codegenerierung ein großer Schmerz sein. Beispielsweise stellen Sie nach einigen manuellen Änderungen einen Fehler im Generator fest und müssen die Generierung nach dem Fix wiederholen. Glücklicherweise kann Schwachkopf oder Ähnliches normalerweise die Schmerzen lindern.
Maaartinus

13

warum Quellcode generieren?

Ich habe zwei Anwendungsfälle für generierten (zum Zeitpunkt der Erstellung und nie eingecheckten) Code festgestellt:

  1. Generieren Sie automatisch Boilerplate-Code wie Getters / Setters, ToString, Equals und HashCode aus einer Sprache, die zur Angabe solcher Dinge erstellt wurde (z. B. Project Lombok für Java).
  2. Generieren Sie automatisch DTO-Typklassen aus einigen Schnittstellenspezifikationen (REST, SOAP usw.), die dann im Hauptcode verwendet werden. Dies ähnelt dem Problem mit der Sprachbrücke, ist jedoch sauberer und einfacher und bietet eine bessere Typverarbeitung als der Versuch, dasselbe ohne generierte Klassen zu implementieren.

15
Sehr sich wiederholender Code in unaussprechlichen Sprachen. Zum Beispiel musste ich Code schreiben, der auf vielen ähnlichen, aber nicht identischen Datenstrukturen dasselbe tat. Wahrscheinlich hätte mit so etwas wie eine C ++ Template durchgeführt (hey nicht ist , dass Code - Generierung?). Aber ich habe C verwendet. Die Codegenerierung hat mir das Schreiben vieler nahezu identischer Codes erspart.
Nick Keighley

1
@NickKeighley Vielleicht erlaubte Ihnen Ihre Toolchain nicht, eine andere, besser geeignete Sprache zu verwenden?
Wilson

7
Normalerweise müssen Sie Ihre Implementierungssprache nicht auswählen. Das Projekt war in C, das war keine Option.
Nick Keighley

1
@Wilson, die ausdrucksstärkeren Sprachen verwenden häufig die Codegenerierung (z. B. Lisp-Makros, Ruby on Rails). Sie müssen in der Zwischenzeit nur nicht als Text gespeichert werden.
Pete Kirkham

4
Ja, Code-Generierung ist im Wesentlichen Meta-Programmierung. In Sprachen wie Ruby können Sie Metaprogramme in der Sprache selbst erstellen, in C müssen Sie stattdessen nicht die Codegenerierung verwenden.
Sean Burton

13

Sussmann hatte in seinem Klassiker "Struktur und Interpretation von Computerprogrammen" viel Interessantes darüber zu sagen, vor allem über die Code-Daten-Dualität.

Für mich besteht der Hauptzweck der Ad-hoc-Codegenerierung darin, mithilfe eines verfügbaren Compilers eine kleine domänenspezifische Sprache in etwas zu konvertieren, das ich in meine Programme einbinden kann. Denken Sie an BNF, denken Sie an ASN1 (eigentlich nicht, es ist hässlich), denken Sie an Tabellenkalkulationen für Datenwörterbücher.

Triviale domänenspezifische Sprachen können eine enorme Zeitersparnis bedeuten, und die Ausgabe von Daten, die mit Standard-Sprachwerkzeugen kompiliert werden können, ist der richtige Weg, wenn Sie solche Dinge erstellen, die Sie lieber bearbeiten, einen nicht trivialen handgehackten Parser in einer beliebigen Muttersprache schreiben, oder die BNF für eine automatisch generierte?

Durch die Ausgabe von Text, der dann einem System-Compiler zugeführt wird, erhalte ich all diese Compiler-Optimierung und systemspezifische Konfiguration, ohne darüber nachdenken zu müssen.

Ich verwende die Compiler-Eingabesprache effektiv nur als Zwischenrepräsentation. Was ist das Problem? Textdateien sind nicht von Natur aus Quellcode, sie können eine IR für einen Compiler sein , und wenn sie zufällig wie C oder C ++ oder Java oder was auch immer aussehen, wen interessiert das?

Nun , wenn Sie sind schwer zu denken , Sie könnten die Ausgabe der Spielzeug Sprache Parser bearbeiten, die eindeutig das nächste Mal , wenn jemand bearbeitet die Eingangssprachdateien enttäuschen wird und neu erstellt, die Antwort ist nicht die Auto Commit IR an die Repo erzeugt, hat es generiert durch Ihre Toolchain (und vermeiden Sie, dass solche Leute in Ihrer Entwicklergruppe sind, sie arbeiten normalerweise glücklicher im Marketing).

Dies ist weniger ein Mangel an Ausdruckskraft in unseren Sprachen als vielmehr ein Ausdruck der Tatsache, dass Sie manchmal Teile der Spezifikation in eine Form bringen (oder massieren) können, die automatisch in Code umgewandelt werden kann und in der Regel weitaus weniger ergibt Bugs und weitaus einfacher zu pflegen. Wenn ich unseren Test- und Konfigurationsmitarbeitern eine Tabelle geben kann, die sie optimieren können, und ein Tool, das sie dann ausführen, das diese Daten aufnimmt und eine vollständige Hex-Datei für den Flash auf meinem Steuergerät ausspuckt, ist das eine enorme Zeitersparnis gegenüber der manuellen Übersetzung das neueste Setup in einer Reihe von Konstanten in der Sprache des Tages (komplett mit Tippfehlern).

Das Gleiche gilt für das Erstellen von Modellen in Simulink und das anschließende Generieren von C mit RTW. Anschließend wird die Kompilierung mit einem beliebigen Werkzeug durchgeführt. Das Zwischen-C ist nicht lesbar. Das Matlab RTW-Zeug auf hoher Ebene muss nur eine Teilmenge von C kennen, und der C-Compiler kümmert sich um die Plattformdetails. Das einzige Mal, wenn ein Mensch durch das generierte C stöbern muss, ist, dass die RTW-Skripte einen Fehler aufweisen, und so etwas ist mit einer nominal vom Menschen lesbaren IR viel einfacher zu debuggen als mit nur einem binären Analysebaum.

Sie können solche Dinge natürlich schreiben, um Bytecode oder sogar ausführbaren Code auszugeben, aber warum sollten Sie das tun? Wir haben Werkzeuge, um eine IR in diese Dinge umzuwandeln.


Das ist gut, aber ich würde hinzufügen, dass es einen Kompromiss gibt, wenn man festlegt, welche IR verwendet werden soll: Wenn man C als IR verwendet, werden einige Dinge einfacher und andere schwieriger als beispielsweise x86-Assemblersprache. Die Auswahl ist noch wichtiger, wenn Sie beispielsweise zwischen Java-Sprachcode und Java-Bytecode wählen, da es viel mehr Operationen gibt, die nur in der einen oder anderen Sprache existieren.
Daniel Pryden

2
Aber die Assemblersprache X86 macht beim Targeting eines ARM- oder PPC-Kerns einen schlechten IR! Alle Dinge sind ein Kompromiss in der Technik, deshalb nennen sie es Technik. Man würde hoffen, dass die Möglichkeiten des Java-Bytecodes eine strenge Obergrenze der Möglichkeiten der Java-Sprache darstellen und dass dies im Allgemeinen der Fall ist, wenn Sie sich dem Metall nähern, unabhängig von der Toolchain und dem Ort, an dem Sie die IR einspeisen.
Dan Mills

Oh, da stimme ich vollkommen zu: Mein Kommentar war eine Antwort auf Ihren letzten Absatz, in dem Sie gefragt wurden, warum Sie jemals Bytecode oder etwas Niedrigeres ausgeben würden - manchmal benötigen Sie die niedrigere Ebene. (In Java gibt es eine Menge nützlicher Dinge, die Sie mit Bytecode tun können, die Sie in der Java-Sprache selbst nicht tun können.)
Daniel Pryden

2
Ich bin damit nicht einverstanden, aber die Verwendung einer IR, die näher am Metall liegt, ist nicht nur in der Regel mit Kosten verbunden, sondern auch in der Tatsache, dass Sie in der Regel mehr für die wirklich ärgerliche Optimierung auf niedriger Ebene verantwortlich sind. Die Tatsache, dass wir heutzutage im Allgemeinen eher an die Optimierung der Algorithmusauswahl als an die Implementierung denken, spiegelt wider, wie weit die Compiler gekommen sind. Manchmal muss man in diesen Dingen dem Metall wirklich nahe kommen, aber zweimal überlegen, bevor man die Compiler wegwirft Fähigkeit zur Optimierung durch Verwendung eines zu niedrigen IR-Pegels.
Dan Mills

1
"Sie arbeiten normalerweise glücklicher im Marketing" Catty, aber lustig.
dmckee

13

Pragmatische Antwort: Ist die Codegenerierung notwendig und nützlich? Stellt es etwas bereit, das wirklich sehr nützlich und für die proprietäre Codebasis erforderlich ist, oder scheint es nur eine andere Möglichkeit zu schaffen, Dinge auf eine Weise zu tun, die mehr intellektuellen Aufwand für suboptimale Ergebnisse mit sich bringt?

OK, ich weiß, dass Code auch Daten sind. Was ich nicht verstehe ist, warum Code generieren? Warum nicht daraus eine Funktion machen, die Parameter akzeptiert und auf diese einwirkt?

Wenn Sie diese Frage stellen müssen und es keine eindeutige Antwort gibt, ist die Codegenerierung wahrscheinlich überflüssig und trägt lediglich zur Exotik und zu einem hohen intellektuellen Aufwand für Ihre Codebasis bei.

In der Zwischenzeit können Sie OpenShadingLanguage verwenden: https://github.com/imageworks/OpenShadingLanguage

... dann müssen solche Fragen nicht gestellt werden, da sie durch die eindrucksvollen Ergebnisse sofort beantwortet werden.

OSL verwendet das LLVM-Compiler-Framework, um Shader-Netzwerke im laufenden Betrieb (Just-in-Time oder "JIT") in Maschinencode zu übersetzen, und optimiert dabei Shader und Netzwerke mit umfassender Kenntnis der Shader-Parameter und anderer Laufzeitwerte, die dies nicht konnten wurden bekannt, als die Shader aus dem Quellcode kompiliert wurden. Infolgedessen werden unsere OSL-Shading-Netzwerke 25% schneller ausgeführt als die in C handgefertigten entsprechenden Shader! (So ​​haben unsere alten Shader in unserem Renderer funktioniert.)

In einem solchen Fall müssen Sie die Existenz des Codegenerators nicht in Frage stellen. Wenn Sie in dieser Art von VFX-Domain arbeiten, lautet Ihre sofortige Antwort in der Regel eher "Halt die Klappe und nimm mein Geld!" oder "Wow, wir müssen auch so etwas machen."


Übersetzen Sie Shader-Netzwerke in Maschinencode . Das klingt eher nach einem Compiler als nach einem Codegenerator, oder?
Utku

2
Grundsätzlich benötigt der Benutzer ein Knotennetzwerk und generiert einen Zwischencode, der von LLVM JIT kompiliert wird. Die Unterscheidung zwischen Compiler und Codegenerator ist irgendwie unscharf. Haben Sie mehr über die Code-Generierungsfunktionen in Sprachen wie Vorlagen in C ++ oder dem C-Präprozessor nachgedacht?

Ich dachte an einen Generator, der Quellcode ausgeben würde.
Utku

Ich sehe, wo der Output noch für den menschlichen Verzehr ist, nehme ich an. OpenSL generiert auch Zwischen-Quellcode, aber es ist ein Low-Level-Code, der für den LLVM-Verbrauch fast fertiggestellt ist. Es ist normalerweise kein Code, der gepflegt werden soll (stattdessen pflegen die Programmierer die Knoten, die zum Generieren des Codes verwendet werden). Meistens glaube ich, dass diese Arten von Codegeneratoren eher missbraucht als nützlich genug sind, um ihren Wert zu rechtfertigen, insbesondere wenn Sie den Code im Rahmen Ihres Erstellungsprozesses ständig neu generieren müssen. Manchmal haben sie dennoch einen echten Platz, um Mängel zu

... der Sprache (n), die verfügbar sind, wenn sie für eine bestimmte Domain verwendet werden. QT hat mit seinem Meta-Object Compiler (MOC) einen dieser umstrittenen. Der MOC reduziert das Boilerplate, das Sie normalerweise benötigen, um Eigenschaften und Reflexionen sowie Signale und Slots usw. in C ++ bereitzustellen, jedoch nicht in einem solchen Ausmaß, um seine Existenz eindeutig zu rechtfertigen. Ich denke oft, dass QT ohne die umständliche Last der MOC-Codegenerierung besser gewesen wäre.

8

Nein, das Generieren von Zwischencode ist kein Anti-Pattern. Die Antwort auf den anderen Teil Ihrer Frage "Warum?" Ist eine sehr breite (und getrennte) Frage, obwohl ich trotzdem einige Gründe nennen werde.

Historische Konsequenzen, wenn man nie von Menschen lesbaren Code hat

Nehmen wir C und C ++ als Beispiele, da sie zu den bekanntesten Sprachen gehören.

Beachten Sie, dass bei der logischen Verarbeitung des Kompilierens von C-Code kein Maschinencode, sondern von Menschen lesbarer Assemblycode ausgegeben wird. Ebenso werden alte C ++ - Compiler verwendet, um C ++ - Code physisch in C-Code zu kompilieren. In dieser Ereigniskette können Sie vom lesbaren Code 1 zum lesbaren Code 2 zum lesbaren Code 3 zum Maschinencode kompilieren. "Warum?" Warum nicht?

Wenn Zwischen, für Menschen lesbaren Code nie erzeugt wurde, könnten wir nicht einmal haben C oder C ++ überhaupt. Das ist sicherlich eine Möglichkeit; Menschen beschreiten den Weg des geringsten Widerstands gegen ihre Ziele, und wenn eine andere Sprache aufgrund von C-Entwicklungsstagnation zuerst Dampf gewann, wäre C möglicherweise gestorben, als es noch jung war. Natürlich könnte man argumentieren "Aber dann würden wir vielleicht eine andere Sprache verwenden, und vielleicht wäre es besser." Vielleicht oder vielleicht wäre es schlimmer. Oder vielleicht würden wir alle noch in der Versammlung schreiben.

Warum von Menschen lesbaren Zwischencode verwenden?

  1. Manchmal ist Zwischencode erwünscht, damit Sie ihn vor dem nächsten Schritt in der Erstellung ändern können. Ich gebe zu, dass dieser Punkt der schwächste ist.
  2. Manchmal liegt es daran, dass die ursprüngliche Arbeit überhaupt nicht in einer für Menschen lesbaren Sprache ausgeführt wurde, sondern in einem GUI-Modellierungstool.
  3. Manchmal muss man etwas sehr Wiederholendes tun, und die Sprache sollte nicht auf das eingehen, was man tut, weil es so eine Nischensache oder so eine komplizierte Sache ist, dass es kein Geschäft hat, die Komplexität oder die Grammatik der Programmiersprache zu erhöhen, nur um sich anzupassen Sie.
  4. Manchmal muss man etwas sehr Wiederholendes tun, und es gibt keine Möglichkeit , das Gewünschte generisch in die Sprache zu bringen. Entweder kann es nicht durch die Grammatik der Sprache dargestellt werden oder steht in Konflikt damit.
  5. Eines der Ziele von Computern ist die Reduzierung des Personalaufwands. Manchmal wird in Code, der wahrscheinlich nie wieder berührt wird (geringe Wahrscheinlichkeit der Wartung), ein Meta-Code geschrieben, um den längeren Code in einem Zehntel der Zeit zu generieren. wenn ich kann es in 1 Tag tun , anstatt von 2 Wochen und es ist nicht wahrscheinlich immer beibehalten werden, dann habe ich es besser erzeugen - und für den unwahrscheinlichen Fall, dass jemand von 5 Jahren ab jetzt verärgert ist , weil sie eigentlich tun müssen , um es aufrechtzuerhalten, dann Sie können die 2 Wochen damit verbringen, es vollständig auszuschreiben, wenn sie möchten, oder sich über 1 Woche langes Warten des umständlichen Codes ärgern (aber wir haben zu diesem Zeitpunkt noch 1 Woche Zeit), und das ist, wenn diese Wartung überhaupt durchgeführt werden muss .
  6. Ich bin mir sicher, dass es weitere Gründe gibt, die ich übersehen habe.

Beispiel

Ich habe bereits an Projekten gearbeitet, bei denen Code auf der Grundlage von Daten oder Informationen in einem anderen Dokument generiert werden muss. In einem Projekt wurden beispielsweise alle Netzwerknachrichten und konstanten Daten in einer Tabelle definiert und ein Tool, das die Tabelle durchläuft und eine Menge C ++ - und Java-Code generiert, mit dem wir mit diesen Nachrichten arbeiten können.

Ich sage nicht, dass dies der beste Weg war, um dieses Projekt einzurichten (ich war nicht Teil seines Startups), aber das war es, was wir hatten, und es waren Hunderte (vielleicht sogar Tausende, nicht sicher) von Strukturen, Objekten und Konstanten das wurden erzeugt; Zu diesem Zeitpunkt ist es wahrscheinlich zu spät, um es in so etwas wie Rhapsody noch einmal zu versuchen. Aber selbst wenn es in so etwas wie Rhapsody überarbeitet wurde, haben wir trotzdem Code, der aus Rhapsody generiert wurde .

Außerdem war es in einer Hinsicht gut, all diese Daten in einer Tabelle zu haben: Es ermöglichte uns, die Daten auf eine Weise darzustellen, die wir nicht hätten, wenn sie alle nur in Quellcodedateien wären.

Beispiel 2

Als ich in der Compilerkonstruktion gearbeitet habe, habe ich das Tool Antlr zum Lexen und Parsen verwendet. Ich habe eine Sprachgrammatik angegeben, dann habe ich mit dem Tool eine Menge Code in C ++ oder Java ausgespuckt, dann habe ich diesen generierten Code neben meinem eigenen Code verwendet und ihn in den Build aufgenommen.

Wie hätte das anders gemacht werden sollen? Vielleicht könntest du dir einen anderen Weg einfallen lassen; es gibt wahrscheinlich andere möglichkeiten. Aber für diese Arbeit wären die anderen Möglichkeiten nicht besser gewesen als der generierte Lex / Parse-Code, den ich hatte.


Ich habe Zwischencode als eine Art Dateiformat und Debugging-Ablaufverfolgung verwendet, als die beiden Systeme inkompatibel waren, aber eine stabile API in einer sehr esoterischen Skriptsprache hatten. Wollte nicht manuell gelesen werden, hätte aber genauso wie XML sein können. Aber das ist häufiger, als man denkt, nachdem alle Webseiten auf diese Weise funktionieren, wie jemand darauf hingewiesen hat.
Joojaa

7

Was Sie vermissen, ist die Wiederverwendung .

Wir haben ein erstaunliches Werkzeug, um Quelltext in Binärcode umzuwandeln, einen so genannten Compiler. Die Eingaben sind gut definiert (normalerweise!), Und es wurde viel gearbeitet, um die Optimierung zu verfeinern. Wenn Sie wirklich wollen , verwenden Sie den Compiler einige Operationen durchzuführen, möchten Sie einen vorhandenen Compiler verwenden und nicht Ihr eigenes schreiben.

Viele Leute erfinden neue Programmiersprachen und schreiben ihre eigenen Compiler. Ziemlich ausnahmslos tun sie dies alle, weil sie die Herausforderung genießen und nicht, weil sie die Funktionen benötigen, die diese Sprache bietet. Alles, was sie tun, könnte in einer anderen Sprache erfolgen; Sie erstellen einfach eine neue Sprache, weil sie diese Funktionen mögen. Was ihnen das allerdings nicht bringt, ist ein gut abgestimmter, schneller, effizienter und optimierender Compiler. Es wird ihnen etwas bringen, das Text in Binärdateien verwandeln kann, aber es wird nicht so gut sein wie alle existierenden Compiler .

Text ist nicht nur etwas, was Menschen lesen und schreiben. Computer sind auch mit Text perfekt zu Hause. Tatsächlich sind Formate wie XML (und andere verwandte Formate) erfolgreich, weil sie einfachen Text verwenden. Binärdateiformate sind oft undeutlich und schlecht dokumentiert, und ein Leser kann nicht leicht herausfinden, wie sie funktionieren. XML ist relativ selbstdokumentierend und erleichtert es den Benutzern, Code mit XML-formatierten Dateien zu schreiben. Alle Programmiersprachen können Textdateien lesen und schreiben.

Angenommen, Sie möchten eine neue Einrichtung hinzufügen, um Ihr Leben zu erleichtern. Vielleicht ist es ein GUI-Layout-Tool. Vielleicht sind es die Schnittstellen für Signale und Slots, die Qt bereitstellt. Vielleicht können Sie mit TIs Code Composer Studio das Gerät, mit dem Sie arbeiten, auf diese Weise konfigurieren und die richtigen Bibliotheken in den Build ziehen. Möglicherweise werden ein Datenwörterbuch und automatisch generierte Typendefinitionen sowie Definitionen globaler Variablen benötigt (ja, dies ist in eingebetteter Software immer noch sehr wichtig). Was auch immer es ist, die effizienteste Möglichkeit, Ihren vorhandenen Compiler zu nutzen, besteht darin, ein Tool zu erstellen, das Ihre Konfiguration von what-it-is übernimmt und automatisch Code in der Sprache Ihrer Wahl erstellt.

Es ist einfach zu entwickeln und zu testen, da Sie wissen, was läuft, und Sie können den Quellcode lesen, den es ausspuckt. Sie müssen nicht viele Jahre damit verbringen, einen Compiler zu erstellen, der mit GCC mithalten kann. Sie müssen keine komplett neue Sprache lernen oder andere dazu auffordern. Alles, was Sie tun müssen, ist, diesen einen kleinen Bereich zu automatisieren, und alles andere bleibt gleich. Job erledigt.


Der Vorteil der textbasierten Funktion von XML besteht jedoch darin, dass sie bei Bedarf von Menschen gelesen und geschrieben werden kann (normalerweise stören sie sich nicht daran, wenn sie funktionieren, aber sicherlich während der Entwicklung). In Bezug auf Leistung und Platzersparnis sind Binärformate im Allgemeinen viel besser (was jedoch sehr oft keine Rolle spielt, da der Engpass woanders liegt).
links um ca. 30.11.17 Uhr

@leftaroundabout Wenn Sie diese Leistung und Raumeffizienz benötigen, stellen Sie sicher. Der Grund, warum sich heutzutage viele Anwendungen für XML-basierte Formate entschieden haben, ist, dass Leistung und Speichereffizienz nicht die wichtigsten Kriterien sind, die sie früher waren, und der Verlauf hat gezeigt, wie schlecht Binärdateiformate gepflegt werden. (Alte MS Word-Dokumente als klassisches Beispiel!) Der Punkt bleibt jedoch bestehen - Text ist für Computer genauso geeignet wie für Menschen.
Graham

Sicher, ein schlecht gestaltetes Binärformat kann tatsächlich schlechter abschneiden als ein durchdachtes Textformat, und selbst ein anständiges Binärformat ist häufig nicht viel kompakter als XML, das mit einem universellen Komprimierungsalgorithmus gepackt ist. IMO ist das Beste aus beiden Welten, eine für den Menschen lesbare Spezifikation über algebraische Datentypen zu verwenden und aus dem AST dieser Typen automatisch eine effiziente binäre Darstellung zu generieren. Siehe zB die flache Bibliothek .
links um ca. 30.11.17 Uhr

7

Eine etwas pragmatischere Antwort, die sich auf das Warum und nicht auf das konzentriert, was Quellcode ist und was nicht. Beachten Sie, dass das Generieren von Quellcode in all diesen Fällen Teil des Erstellungsprozesses ist. Daher sollten die generierten Dateien nicht in die Quellcodeverwaltung gelangen.

Interoperabilität / Einfachheit

Ein gutes Beispiel sind die Protokollpuffer von Google: Sie schreiben eine einzige Protokollbeschreibung auf hoher Ebene, mit der die Implementierung in mehreren Sprachen erstellt werden kann. Oftmals werden verschiedene Teile des Systems in verschiedenen Sprachen geschrieben.

Implementierung / technische Gründe

Verwenden Sie TypeScript - Browser können es nicht interpretieren, sodass der Erstellungsprozess einen Transpiler (Code-to-Code-Übersetzer) verwendet, um JavaScript zu generieren. Tatsächlich beginnen viele neue oder esoterisch kompilierte Sprachen mit dem Transpilieren nach C, bevor sie einen richtigen Compiler erhalten.

Benutzerfreundlichkeit

Für eingebettete Projekte (Think IoT), die in C geschrieben sind und nur eine einzige Binärdatei (RTOS oder kein Betriebssystem) verwenden, ist es ziemlich einfach, ein C-Array mit den Daten zu generieren, die wie normaler Quellcode kompiliert werden sollen, anstatt sie direkt zu verknüpfen als Ressourcen.

Bearbeiten

Erweiterung von protobuf: Mit der Codegenerierung können die generierten Objekte erstklassige Klassen in jeder Sprache sein. In einer kompilierten Sprache würde ein generischer Parser zwangsläufig eine Schlüsselwertstruktur zurückgeben - was bedeutet, dass Sie viel Code für das Boilerplate benötigen, einige Überprüfungen zur Kompilierungszeit (insbesondere für Schlüssel und Wertetypen) verpassen und eine schlechtere Leistung erzielen keine Code-Vervollständigung. Stellen Sie sich vor, dass alle void*in C oder so großen std::variantin C ++ (wenn Sie C ++ 17 haben), einige Sprachen möglicherweise überhaupt keine solche Funktion haben.


Aus dem ersten Grund denke ich, dass die Idee des OP darin besteht, eine generische Implementierung in jeder Sprache zu haben (die die Beschreibung des Protokollpuffers übernimmt und dann das On-the-Wire-Format analysiert / verbraucht). Warum wäre das schlimmer als Code zu generieren?
Paŭlo Ebermann

@ PaŭloEbermann Abgesehen von dem üblichen Leistungsargument würde eine solche generische Interpretation es unmöglich machen, diese Meldungen als erstklassige Objekte in kompilierten (und möglicherweise interpretierten) Sprachen zu verwenden - in C ++ beispielsweise würde ein solcher Interpreter zwangsläufig eine Schlüsselwertstruktur zurückgeben . Natürlich können Sie dann das kv in Ihre Klassen aufnehmen, aber es kann sich in eine Menge Code verwandeln. Und es gibt auch Code-Vervollständigung. Und Überprüfung der Kompilierungszeit - Ihr Compiler prüft nicht, ob Ihre Literale keine Tippfehler enthalten.
Jan Dorniak,

Ich stimme dir zu ... könntest du das in die Antwort einfügen?
Paŭlo Ebermann

@ PaŭloEbermann erledigt
Jan Dorniak

6

Ist die Generierung von Quellcode ein Anti-Pattern?

Es ist eine Umgehungslösung für eine nicht ausreichend ausdrucksstarke Programmiersprache. Es ist nicht erforderlich, Code in einer Sprache zu generieren, die eine angemessene integrierte Metaprogrammierung enthält.


3
Es ist auch eine Problemumgehung, um einen vollständigen, systemeigenen Objektcode-Compiler für eine ausdrucksstärkere Sprache schreiben zu müssen. Generieren Sie C und lassen Sie einen Compiler mit einem guten Optimierer den Rest erledigen.
Freitag,

Nicht immer. Manchmal haben Sie eine oder mehrere Datenbanken, die einige Definitionen für z. B. Signale auf einem Bus enthalten. Dann möchten Sie diese Informationen zusammenführen, möglicherweise einige Konsistenzprüfungen durchführen und dann einen Code schreiben, der die Schnittstellen zwischen den vom Bus kommenden Signalen und den Variablen herstellt, die Sie in Ihrem Code erwarten. Wenn Sie mir eine Sprache mit Metaprogrammierung zeigen können, die die Verwendung einiger vom Kunden bereitgestellter Excel-Tabellen, einer Datenbank und anderer Datenquellen erleichtert, und den von mir benötigten Code mit einigen erforderlichen Überprüfungen der Datengültigkeit und -konsistenz erstellt, dann durch Alle Mittel zeigen mir.
CodeMonkey

@CodeMonkey: Es fällt mir so etwas wie die ActiveRecord-Implementierung von Ruby on Rails ein. Es ist nicht erforderlich, das Datenbanktabellenschema im Code zu duplizieren. Ordnen Sie einfach eine Klasse einer Tabelle zu und schreiben Sie eine Geschäftslogik unter Verwendung der Spaltennamen als Eigenschaften. Ich kann mir kein Muster vorstellen, das mit einem Codegenerator erzeugt werden könnte, der auch nicht mit Ruby-Metaprogrammierung verwaltet werden könnte. Auch C ++ - Templates sind extrem leistungsfähig, wenn auch etwas arkane. Lisp-Makros sind ein weiteres leistungsstarkes Meta-Programmiersystem in der Sprache.
Kevin Cline

@kevincline, was ich meinte, war Code, der auf einigen Daten aus der Datenbank basierte (aus diesen Daten erstellt werden konnte), aber nicht auf der Datenbank selbst. Dh ich habe Informationen darüber, welche Signale ich in Excel-Tabelle A erhalte. Ich habe eine Datenbank B mit Informationen zu diesen Signalen usw. Jetzt möchte ich eine Klasse, die auf diese Signale zugreift. Es besteht keine Verbindung zur Datenbank oder zur Excel-Tabelle auf dem Computer, auf dem der Code ausgeführt wird. Verwenden von wirklich kompliziertem C ++ - Templating zum Generieren dieses Codes zur Kompilierungszeit anstelle eines einfachen Codegenerators. Ich werde Codegen holen.
CodeMonkey

6

Die Generierung von Quellcode ist nicht immer ein Anti-Pattern. Zum Beispiel schreibe ich gerade ein Framework, das gemäß vorgegebener Spezifikation Code in zwei verschiedenen Sprachen (Javascript und Java) generiert. Das Framework verwendet das generierte Javascript, um Browseraktionen des Benutzers aufzuzeichnen, und verwendet den Java-Code in Selenium, um die Aktion tatsächlich auszuführen, wenn sich das Framework im Wiedergabemodus befindet. Wenn ich keine Codegenerierung verwenden würde, müsste ich manuell sicherstellen, dass beide immer synchron sind, was umständlich ist und in gewisser Weise auch eine logische Verdoppelung darstellt.

Wenn man jedoch die Quellcodegenerierung verwendet, um Funktionen wie Generika zu ersetzen, ist dies ein Anti-Pattern.


Sie können Ihren Code natürlich auch einmal in ECMAScript schreiben und in Nashorn oder Rhino auf der JVM ausführen. Sie können auch eine JVM in ECMAScript schreiben (oder versuchen, Avian mit Emscripten in WebAssembly zu kompilieren) und Ihren Java-Code im Browser ausführen. Ich sage nicht, dass das großartige Ideen sind (na ja, es sind wahrscheinlich schreckliche Ideen :-D), aber zumindest sind sie möglich, wenn nicht machbar.
Jörg W Mittag

In der Theorie ist es möglich, aber es ist keine allgemeine Lösung. Was passiert, wenn ich eine der Sprachen nicht in einer anderen ausführen kann? Ein weiteres Beispiel: Ich habe gerade ein einfaches Netlogo-Modell mithilfe der Codegenerierung erstellt und eine interaktive Dokumentation des Systems erstellt, die immer mit dem Rekorder und dem Replayer synchronisiert ist. Im Allgemeinen werden beim Erstellen einer Anforderung und anschließenden Generieren von Code die semantisch zusammenlaufenden Elemente synchronisiert.
Hristo Vrigazov

6

Vermisse ich hier etwas?

Vielleicht ein gutes Beispiel, bei dem sich der Zwischencode als Grund für den Erfolg herausstellte? Ich kann Ihnen HTML anbieten.

Ich glaube, es war wichtig, dass HTML einfach und statisch war - es machte es einfach, Browser zu erstellen, es ermöglichte das frühe Starten von mobilen Browsern usw. Wie weitere Experimente (Java-Applets, Flash) zeigten, führten komplexere und leistungsfähigere Sprachen zu mehr Problemen . Es stellt sich heraus, dass Benutzer tatsächlich durch Java-Applets gefährdet sind und der Besuch solcher Websites genauso sicher war wie das Ausprobieren von Spiel-Cracks, die über DC ++ heruntergeladen wurden. Plain HTML hingegen ist harmlos genug, um Websites mit angemessenem Vertrauen in die Sicherheit unseres Geräts zu überprüfen.

HTML wäre jedoch bei weitem nicht dort, wo es jetzt ist, wenn es nicht computergeneriert wäre. Meine Antwort wurde erst auf dieser Seite angezeigt, wenn jemand sie manuell aus der Datenbank in eine HTML-Datei geschrieben hat. Zum Glück können Sie HTML in fast jeder Programmiersprache nutzbar machen :)

Das heißt, wenn es einen Codegenerator für etwas gibt, warum dann nicht eine ordnungsgemäße Funktion daraus machen, die die erforderlichen Parameter empfangen und die richtige Aktion ausführen kann, die der "erzeugte" Code ausgeführt hätte?

Können Sie sich eine bessere Möglichkeit vorstellen, die Frage sowie alle Antworten und Kommentare für den Benutzer anzuzeigen, als HTML als generierten Zwischencode zu verwenden?


Ja, ich kann mir einen besseren Weg vorstellen. HTML ist das Erbe einer Entscheidung von Tim Berners-Lee, die schnelle Erstellung eines reinen Text-Webbrowsers zu ermöglichen. Das war zu der Zeit vollkommen in Ordnung, aber wir würden das im Nachhinein nicht tun. CSS hat die verschiedenen Darstellungselementtypen (DIV, SPAN, TABLE, UL usw.) Überflüssig gemacht.
Kevin Cline

@ kevincline Ich sage nicht, dass HTML als solches fehlerfrei ist. Ich wies darauf hin, dass die Einführung einer Auszeichnungssprache (die von einem Programm generiert werden kann) in diesem Fall sehr gut funktioniert hat.
Džuris

HTML + CSS ist also besser als nur HTML. Ich habe sogar interne Dokumentationen für einige Projekte geschrieben, an denen ich direkt in HTML + CSS + MathJax gearbeitet habe. Aber die meisten Webseiten, die ich besuche, scheinen von Codegeneratoren erstellt worden zu sein.
David K

3

warum Quellcode generieren?

Weil es schneller und einfacher (und weniger fehleranfällig) ist, als den Code manuell zu schreiben, insbesondere bei langwierigen, sich wiederholenden Aufgaben. Sie können auch das übergeordnete Tool verwenden, um Ihr Design zu überprüfen und zu validieren, bevor Sie eine einzelne Codezeile schreiben.

Häufige Anwendungsfälle:

  • Modellierungswerkzeuge wie Rose oder Visual Paradigm;
  • Hoch er Sprachen wie Embedded SQL oder eine Schnittstellendefinitionssprache , die übersetzbar in etwas vorverarbeitet werden muss;
  • Lexer- und Parser-Generatoren wie Flex / Bison;

Beachten Sie, dass es sich bei keiner der oben genannten Umgebungen um eigenständige Ausführungsumgebungen handelt. Es gibt keine Möglichkeit, Ihren Code mit ihnen zu verknüpfen.


2

Manchmal verfügt Ihre Programmiersprache einfach nicht über die von Ihnen gewünschten Funktionen, sodass es tatsächlich unmöglich ist, Funktionen oder Makros zu schreiben, um das zu tun, was Sie möchten. Oder vielleicht könnten Sie tun, was Sie wollen, aber der Code zum Schreiben wäre hässlich. Ein einfaches Python-Skript (oder ähnliches) kann dann den erforderlichen Code als Teil Ihres Erstellungsprozesses generieren, den Sie dann #includein die eigentliche Quelldatei einfügen.

Woher weiß ich das? Da es sich um eine Lösung handelt, zu der ich bei der Arbeit mit verschiedenen Systemen, zuletzt SourcePawn, mehrmals gelangt bin. Ein einfaches Python-Skript, das eine einfache Zeile Quellcode analysiert und zwei oder drei Zeilen generierten Codes erzeugt, ist weitaus besser, als den generierten Code manuell zu erstellen, wenn Sie am Ende zwei Dutzend solcher Zeilen haben (alle meine cvars erstellen).

Demonstrativer / Beispiel-Quellcode verfügbar, wenn die Leute es wollen.


1

Textform ist für den einfachen Verzehr durch Menschen erforderlich. Computer verarbeiten auch Code in Textform ganz einfach. Aus diesem Grund sollte generierter Code in der Form generiert werden, die am einfachsten zu generieren und am einfachsten von Computern zu verarbeiten ist und die sehr oft lesbaren Text enthält.

Und wenn Sie Code generieren, muss der Code-Generierungsprozess selbst häufig von Menschen getestet werden. Es ist sehr, sehr nützlich, wenn der generierte Code von Menschen gelesen werden kann, damit Menschen Probleme bei der Codegenerierung erkennen können. Schließlich muss jemand den Code schreiben, um Code zu generieren. Es passiert nicht aus der Luft.


1

Code wird nur einmal generiert

Nicht die gesamte Generierung von Quellcode besteht darin, Code zu generieren und ihn dann nie zu berühren. Generieren Sie es dann aus der ursprünglichen Quelle, wenn es aktualisiert werden muss.

Manchmal generieren Sie Code nur einmal, verwerfen dann die ursprüngliche Quelle und behalten die neue Quelle bei, wenn Sie fortfahren.

Dies passiert manchmal, wenn Code von einer Sprache in eine andere portiert wird. Besonders wenn man nicht erwartet, dass man später über neue Änderungen im Original portieren möchte (z. B. wird der alte Sprachcode nicht beibehalten oder ist tatsächlich vollständig (z. B. im Fall einiger mathematischer Funktionen)).

Ein häufiger Fall ist, dass das Schreiben eines Codegenerators zu diesem Zweck möglicherweise nur 90% des Codes korrekt übersetzt. und dann müssen die letzten 10% von Hand repariert werden. Das ist viel schneller als 100% von Hand zu übersetzen.

Solche Codegeneratoren unterscheiden sich oft sehr von der Art der Codegeneratoren, die Vollsprachenübersetzer (wie Cython oder f2c) produzieren. Da ist das Ziel, den Code einmalig zu pflegen. Sie werden oft als eine 1 gemacht, um genau das zu tun, was sie müssen. In vielerlei Hinsicht ist es die nächste Version der Verwendung eines regulären Ausdrucks / Find-Replace-to-Port-Codes. "Tool assisted porting" könnte man sagen.

Einmaliges Generieren von Code, z. B. von einer Website aus.

Eng verwandt ist, wenn Sie den Code aus einer Quelle generieren, auf die Sie nicht erneut zugreifen möchten. ZB wenn die zum Generieren des Codes erforderlichen Aktionen nicht wiederholbar oder konsistent sind oder deren Ausführung teuer ist. Ich arbeite gerade an zwei Projekten: DataDeps.jl und DataDepsGenerators.jl .

DataDeps.jl hilft Benutzern beim Herunterladen von Daten (wie Standard-ML-Datasets). Dazu benötigt es einen sogenannten RegistrationBlock. Dies ist ein Code, der einige Metadaten angibt, z. B. den Ort, von dem die Dateien heruntergeladen werden sollen, sowie eine Prüfsumme und eine Meldung, in der dem Benutzer Begriffe / Codierungen / der Lizenzstatus der Daten erläutert werden.

Das Schreiben dieser Blöcke kann ärgerlich sein. Und diese Informationen sind häufig (strukturiert oder unstrukturiert) auf den Websites verfügbar, auf denen die Daten gehostet werden. Daher verwendet DataDepsGenerators.jl einen Webscraper, um den RegistrationBlockCode für einige Sites zu generieren, die viele Daten hosten.

Möglicherweise werden sie nicht korrekt generiert. Der Entwickler, der den generierten Code verwendet, kann und sollte ihn überprüfen und korrigieren. Vermutlich möchten sie sicherstellen, dass die Lizenzinformationen nicht übersehen werden.

Wichtig ist, dass Benutzer / Entwickler, die mit DataDeps.jl arbeiten, den Webscraper nicht installieren oder verwenden müssen, um den generierten RegistrationBlock-Code zu verwenden. (Und das Nicht-Herunterladen und Installieren eines Web-Scrapers spart einiges an Zeit. Insbesondere für die CI-Läufe.)

Einmal generierter Quellcode ist kein Gegenmuster. und es kann normalerweise nicht durch Metaprogrammierung ersetzt werden.


"report" ist ein englisches Wort, das etwas anderes als "port again" bedeutet. Versuchen Sie "re-port", um diesen Satz klarer zu machen. (Kommentar, weil zu klein für eine vorgeschlagene Bearbeitung.)
Peter Cordes

Guter Fang @PeterCordes, den ich umformuliert habe.
Lyndon White

Schneller, aber möglicherweise viel weniger wartbar, je nachdem, wie schrecklich der generierte Code ist. Fortran zu C war früher eine Sache (C-Compiler waren weiter verbreitet, daher würden die Leute f2c+ verwenden cc), aber der resultierende Code war nicht wirklich ein guter Ausgangspunkt für eine C-Version des Programms, AFAIK.
Peter Cordes

1
Möglicherweise möglicherweise nicht. Es liegt nicht am Konzept der Codegeneratoren, dass einige Codegeneratoren nicht wartbaren Code erstellen. Insbesondere ein handgefertigtes Werkzeug, das nicht jeden Fall fangen muss, kann oftmals perfekt schönen Code erstellen. Wenn zum Beispiel 90% des Codes nur eine Liste von Array-Konstanten sind, kann das Erzeugen dieser Array-Konstruktoren als Einzelanfertigung sehr einfach und mit geringem Aufwand durchgeführt werden. (Auf der anderen Seite kann der von Cython ausgegebene C-Code nicht von Menschen gepflegt werden. Weil dies nicht beabsichtigt ist. Genau wie Sie es früher sagten. f2c)
Lyndon White

1
Der große Tisch war nur das einfachste Argument. Ähnliches gilt beispielsweise für die Konvertierung von for-Schleifen oder Bedingungen. In der Tat sedgeht ein langer Weg, aber manchmal braucht man ein bisschen mehr Ausdruckskraft. Die Grenze zwischen Programmlogik und Daten ist oft fein. Manchmal ist die Unterscheidung nicht sinnvoll. JSON ist (/ war) nur JavaScript-Objektkonstruktorcode. In meinem Beispiel generiere ich auch Objektkonstruktorcode (sind es Daten? Vielleicht (vielleicht nicht, weil es manchmal Funktionsaufrufe hat). Wird es besser als Code behandelt? Ja.)
Lyndon White

1

Die Generierung von "Quellcode" ist ein Hinweis auf einen Mangel der generierten Sprache. Ist der Einsatz von Werkzeugen zur Überwindung dieses Problems ein Widerspruch? Absolut nicht - lass es mich erklären.

In der Regel wird die Codegenerierung verwendet, da eine Definition auf höherer Ebene vorhanden ist, die den resultierenden Code viel weniger ausführlich beschreiben kann als die Sprache auf niedrigerer Ebene. Die Codegenerierung erleichtert somit die Effizienz und Kürze.

Wenn ich c ++ schreibe, tue ich das, weil ich damit effizienter Code schreiben kann als mit Assembler- oder Maschinencode. Der Compiler generiert weiterhin Maschinencode. Am Anfang war c ++ einfach ein Präprozessor, der C-Code generierte. Allzwecksprachen eignen sich hervorragend zum Generieren von Allzweckverhalten.

Auf die gleiche Weise ist es mit einer DSL (domänenspezifischen Sprache) möglich, knappen, aber möglicherweise auf eine bestimmte Aufgabe beschränkten Code zu schreiben. Dadurch wird es weniger kompliziert, das richtige Verhalten des Codes zu generieren. Denken Sie daran, dass Code Mittel und Zweck ist . Was ein Entwickler sucht, ist eine effiziente Möglichkeit, Verhalten zu generieren.

Im Idealfall kann der Generator schnellen Code aus einer Eingabe erstellen, die einfacher zu bearbeiten und zu verstehen ist. Wenn dies erfüllt ist, ist die Nichtverwendung eines Generators ein Anti-Pattern . Dieses Anti-Pattern kommt in der Regel von der Vorstellung, dass "reiner" Code "sauberer" ist, ähnlich wie ein Holzarbeiter oder ein anderer Handwerker die Verwendung von Elektrowerkzeugen oder die Verwendung von CNC zum "Erzeugen" von Werkstücken sieht (denken Sie an Gold) Hammer ).

Wenn es andererseits schwieriger ist, den Quellcode zu verwalten oder zu generieren, der nicht effizient genug ist, gerät der Benutzer in die Falle, die falschen Tools zu verwenden (manchmal aufgrund desselben goldenen Hammers ).


0

Die Generierung von Quellcode bedeutet auf jeden Fall, dass der generierte Code Daten sind. Aber es sind erstklassige Daten, Daten, die der Rest des Programms manipulieren kann.

Die beiden gebräuchlichsten Datentypen, die mir bekannt sind und in den Quellcode integriert sind, sind grafische Informationen zu Fenstern (Anzahl und Platzierung verschiedener Steuerelemente) und ORMs. In beiden Fällen erleichtert die Integration über die Codegenerierung die Manipulation der Daten, da Sie keine zusätzlichen "speziellen" Schritte durchführen müssen, um sie zu verwenden.

Bei der Arbeit mit den Original-Macs (1984) wurden Dialog- und Fensterdefinitionen mit einem Ressourcen-Editor erstellt, der die Daten in einem Binärformat hielt. Die Verwendung dieser Ressourcen in Ihrer Anwendung war schwieriger als wenn das "Binärformat" Pascal gewesen wäre.

Nein, die Generierung von Quellcode ist kein Anti-Pattern, sondern ermöglicht es, die Daten in die Anwendung einzubeziehen, was die Verwendung vereinfacht.


0

Code-Generierung ist ein Anti-Pattern, wenn es mehr kostet als es leistet. Diese Situation tritt auf, wenn die Generierung von A nach B erfolgt, wobei A fast dieselbe Sprache wie B ist, jedoch mit einigen geringfügigen Erweiterungen, die durch einfaches Codieren in A mit weniger Aufwand als alle benutzerdefinierten Tools und Build-Staging-Vorgänge für A nach B ausgeführt werden können .

Der Kompromiss ist für die Codegenerierung in Sprachen, die keine Metaprogrammiermöglichkeiten (Strukturmakros) haben, untragbarer, da die Metaprogrammierung durch die Bereitstellung externer Textverarbeitung kompliziert und unzureichend ist.

Der schlechte Kompromiss könnte auch mit der Nutzungsmenge zu tun haben. Sprache A kann sich erheblich von B unterscheiden, aber das gesamte Projekt mit seinem benutzerdefinierten Codegenerator verwendet A nur an ein oder zwei kleinen Stellen, sodass die Gesamtkomplexität (kleine Bits von A plus A -> B-Codegenerator, plus das umgebende Build-Staging) übersteigt die Komplexität einer soeben in B durchgeführten Lösung.

Grundsätzlich sollten wir, wenn wir uns zur Codegenerierung verpflichten, wahrscheinlich "groß rausgehen oder nach Hause gehen": dafür sorgen, dass es eine substantielle Semantik hat und viel verwendet wird, oder uns nicht darum kümmern.


Warum haben Sie den Absatz "Als Bjarne Stroustrup C ++ zum ersten Mal implementierte ..." entfernt? Ich finde es interessant.
Utku

@Utku Andere Antworten behandeln dies unter dem Gesichtspunkt der Kompilierung einer vollständigen, hoch entwickelten Sprache, in der der Rest eines Projekts vollständig geschrieben ist. Ich denke nicht, dass es für die Mehrheit der sogenannten "Code-Generierung" repräsentativ ist.
Kaz

0

Ich habe diese Aussage nicht klar gesehen (ich habe gesehen, dass sie von ein oder zwei Antworten berührt wurde, aber sie schien nicht sehr klar zu sein)

Das Generieren von Code (wie Sie sagten, als ob es Daten wären) ist kein Problem - es ist eine Möglichkeit, einen Compiler für einen sekundären Zweck wiederzuverwenden.

Das Bearbeiten von generiertem Code ist eines der heimtückischsten, bösesten und schrecklichsten Anti-Patterns, denen Sie jemals begegnen werden. Mach das nicht.

Bestenfalls zieht die Bearbeitung des generierten Codes eine Menge schlechten Codes in Ihr Projekt (der GESAMTE Codesatz ist jetzt wirklich SOURCE CODE - keine Daten mehr). Im schlimmsten Fall ist der Code, der in Ihr Programm geladen wird, hochgradig redundant und hat einen schlechten Namen.

Ich nehme an, eine dritte Kategorie ist Code, den Sie einmal verwenden (GUI-Generator?) Und dann bearbeiten, um Ihnen den Einstieg / das Lernen zu erleichtern. Dies ist ein kleiner Teil von jedem - es kann ein guter Start sein, aber Ihr GUI-Generator wird darauf abzielen, "generierbaren" Code zu verwenden, der für Sie als Programmierer kein guter Start ist versucht, es erneut für eine zweite GUI zu verwenden, was bedeutet, redundanten SOURCE-Code in Ihr System zu ziehen.

Wenn Ihre Werkzeuge so intelligent sind, dass keine Änderungen am generierten Code zulässig sind, können Sie sie ausführen. Wenn nicht, würde ich es als eines der schlimmsten Anti-Patterns bezeichnen.


0

Code und Daten sind beide: Information.

Daten sind die Informationen genau in der Form, die Sie benötigen (und Wert). Code ist ebenfalls Information, jedoch in indirekter oder intermediärer Form. Code ist im Wesentlichen auch eine Form von Daten.

Insbesondere ist Code eine Information für Maschinen, mit der Menschen von der Verarbeitung von Informationen allein entlastet werden können.

Das wichtigste Motiv ist die Entlastung des Menschen von der Informationsverarbeitung. Zwischenschritte sind akzeptabel, solange sie das Leben erleichtern. Aus diesem Grund gibt es Tools für die Zuordnung von Zwischeninformationen. Wie Codegeneratoren, Compiler, Transpiler usw.

warum Quellcode generieren? Warum nicht daraus eine Funktion machen, die Parameter akzeptiert und auf diese einwirkt?

Nehmen wir an, jemand bietet Ihnen eine solche Zuordnungsfunktion an, deren Implementierung für Sie unklar ist. Wenn die Funktion wie versprochen funktioniert, ist es Ihnen dann wichtig, ob intern Quellcode generiert wird oder nicht?


0

Wenn etwas generiert werden kann, dann sind das Daten, kein Code.

Insofern Sie später festlegen, dass es sich bei diesem Code um Daten handelt, reduziert sich Ihr Vorschlag auf "Wenn etwas generiert werden kann, ist das Ding kein Code." Würden Sie dann sagen, dass der von einem C-Compiler generierte Assembly-Code kein Code ist? Was passiert, wenn es genau mit dem Assembler-Code übereinstimmt, den ich von Hand schreibe? Du kannst gerne dorthin gehen, wenn du möchtest, aber ich werde nicht mit dir kommen.

Beginnen wir stattdessen mit einer Definition von "Code". Ohne zu technisch zu werden, wäre eine ziemlich gute Definition für die Zwecke dieser Diskussion "maschinell ausführbare Anweisungen zum Durchführen einer Berechnung".

Ist diese Idee der Quellcode-Generierung nicht ein Missverständnis?

Nun ja, Ihr Ausgangspunkt ist, dass Code nicht generiert werden kann, aber ich lehne diesen Vorschlag ab. Wenn Sie meine Definition von "Code" akzeptieren, sollte es im Allgemeinen kein konzeptionelles Problem bei der Codegenerierung geben.

Das heißt, wenn es einen Codegenerator für etwas gibt, warum dann nicht eine ordnungsgemäße Funktion daraus machen, die die erforderlichen Parameter empfangen und die richtige Aktion ausführen kann, die der "erzeugte" Code ausgeführt hätte?

Nun, das ist eine ganz andere Frage, nach dem Grund für die Codegenerierung und nicht nach ihrer Natur. Sie schlagen die Alternative vor, dass anstelle von Schreiben oder Verwenden eines Codegenerators eine Funktion geschrieben wird, die das Ergebnis direkt berechnet. Aber in welcher Sprache? Vorbei sind die Zeiten, in denen irgendjemand direkt in Maschinencode geschrieben hat. Wenn Sie Ihren Code in einer anderen Sprache schreiben, sind Sie auf einen Codegenerator in Form eines Compilers und / oder Assemblers angewiesen, um ein Programm zu erstellen, das tatsächlich ausgeführt wird.

Warum schreibst du dann lieber in Java oder C oder Lisp oder was auch immer? Auch Assembler? Ich behaupte, dass dies zumindest teilweise darauf zurückzuführen ist, dass diese Sprachen Abstraktionen für Daten und Operationen bereitstellen, die es einfacher machen, die Details der durchzuführenden Berechnung auszudrücken.

Gleiches gilt auch für die meisten übergeordneten Codegeneratoren. Die prototypischen Fälle sind wahrscheinlich Scanner- und Parser-Generatoren wie lexund yacc. Ja, Sie können einen Scanner und einen Parser direkt in C oder in einer anderen Programmiersprache Ihrer Wahl schreiben (sogar in unformatiertem Maschinencode), und manchmal auch. Bei einem Problem mit erheblicher Komplexität erleichtert die Verwendung einer höheren Sprache für spezielle Zwecke wie Lex oder Yacc das Schreiben, Lesen und Verwalten des handgeschriebenen Codes. Meist auch viel kleiner.

Sie sollten sich auch überlegen, was genau Sie unter "Codegenerator" verstehen. Ich würde C-Vorverarbeitung und die Instanziierung von C ++ - Vorlagen als Übung bei der Codegenerierung betrachten. lehnen Sie diese ab? Wenn nicht, dann müssen Sie einige mentale Gymnastik machen, um zu rationalisieren, dass Sie diese akzeptieren, aber andere Arten der Code-Generierung ablehnen.

Wenn es aus Performancegründen gemacht wird, dann klingt das nach einem Manko des Compilers.

Warum? Sie sind im Grunde genommen der Meinung, dass man ein universelles Programm haben sollte, in das der Benutzer Daten einspeist, von denen einige als "Anweisungen" und andere als "Eingaben" klassifiziert sind und das die Berechnung durchführt und mehr Daten ausgibt, die wir "Ausgaben" nennen. (Unter einem bestimmten Gesichtspunkt könnte man ein solches universelles Programm als "Betriebssystem" bezeichnen.) Aber warum sollte ein Compiler bei der Optimierung eines solchen universellen Programms genauso effektiv sein wie bei der Optimierung eines spezialisierteren Programm? Die beiden Programme haben unterschiedliche Eigenschaften und Fähigkeiten.

Wenn es darum geht, zwei Sprachen zu verbinden, dann klingt das nach einem Mangel an Schnittstellenbibliothek.

Sie sagen, dass eine universelle Schnittstellenbibliothek zwangsläufig eine gute Sache wäre. Vielleicht wäre es so, aber in vielen Fällen wäre eine solche Bibliothek groß und schwierig zu schreiben und zu warten, und vielleicht sogar langsam. Und wenn ein solches Tier tatsächlich nicht existiert, um das jeweilige Problem zu lösen, wer sind Sie dann, um darauf zu bestehen, dass eines erstellt wird, wenn ein Code-Generierungsansatz das Problem viel schneller und einfacher lösen kann?

Vermisse ich hier etwas?

Mehrere Dinge, denke ich.

Ich weiß, dass Code auch Daten sind. Was ich nicht verstehe ist, warum Quellcode generieren? Warum nicht daraus eine Funktion machen, die Parameter akzeptiert und auf diese einwirkt?

Codegeneratoren wandeln in einer Sprache geschriebenen Code in Code in einer anderen, normalerweise niedrigeren Sprache um. Sie fragen sich also, warum die Leute Programme in mehreren Sprachen schreiben möchten und vor allem, warum sie möglicherweise Sprachen mit subjektiv unterschiedlichen Niveaus mischen möchten.

Aber das habe ich schon angesprochen. Man wählt eine Sprache für eine bestimmte Aufgabe zum Teil aufgrund ihrer Klarheit und Aussagekraft für diese Aufgabe. Da kleinerer Code im Durchschnitt weniger Fehler aufweist und einfacher zu warten ist, besteht zumindest bei umfangreichen Arbeiten eine Tendenz zu höheren Sprachen. Ein komplexes Programm beinhaltet jedoch viele Aufgaben, von denen einige in einer Sprache effektiver behandelt werden können, während andere in einer anderen Sprache effektiver oder prägnanter behandelt werden. Das richtige Werkzeug für den Job zu verwenden, bedeutet manchmal, Code zu generieren.


0

Beantwortung der Frage im Rahmen Ihres Kommentars:

Die Aufgabe des Compilers ist es, einen in lesbarer Form geschriebenen Code in maschinenlesbare Form umzuwandeln. Wenn der Compiler keinen effizienten Code erstellen kann, erledigt der Compiler seine Arbeit daher nicht ordnungsgemäß. Ist das falsch?

Ein Compiler wird niemals für Ihre Aufgabe optimiert. Der Grund dafür ist einfach: Es ist für viele Aufgaben optimiert . Es ist ein Allzweckwerkzeug, das von vielen Menschen für viele verschiedene Aufgaben verwendet wird. Sobald Sie wissen, was Ihre Aufgabe ist, können Sie sich domänenspezifisch dem Code nähern und Kompromisse eingehen, die die Compiler nicht eingehen konnten.

Als Beispiel habe ich an Software gearbeitet, bei der ein Analyst möglicherweise Code schreiben muss. Sie könnten ihren Algorithmus in C ++ schreiben und alle Schrankenprüfungen und Memo-Tricks hinzufügen, von denen sie abhängen, aber das erfordert viel Wissen über die innere Funktionsweise des Codes. Sie schreiben lieber etwas Einfaches und lassen mich einen Algorithmus darauf anwenden, um den endgültigen C ++ - Code zu generieren. Dann kann ich exotische Tricks ausführen, um die Leistung zu maximieren, wie z. B. statische Analysen, von denen ich niemals erwarten würde, dass sie meine Analysten aushalten. Mithilfe der Codegenerierung können sie domänenspezifisch schreiben, wodurch sie das Produkt einfacher als jedes andere Allzweckwerkzeug auf den Markt bringen können.

Ich habe auch genau das Gegenteil getan. Ich habe eine weitere Arbeit, die das Mandat "Keine Code-Generierung" hatte. Wir wollten denen, die die Software verwenden, immer noch das Leben erleichtern, deshalb haben wir eine Menge Template-Metaprogramme verwendet, um den Compiler den Code im laufenden Betrieb generieren zu lassen. Daher brauchte ich nur die Allzwecksprache C ++, um meine Arbeit zu erledigen.

Es gibt jedoch einen Haken. Es war äußerst schwierig zu garantieren, dass die Fehler lesbar waren. Wenn Sie jemals metaprogrammierten Code für Vorlagen verwendet haben, wissen Sie, dass ein einzelner unschuldiger Fehler einen Fehler erzeugen kann, der 100 Zeilen mit unverständlichen Klassennamen und Vorlagenargumenten benötigt, um zu verstehen, was schief gelaufen ist. Dieser Effekt war so ausgeprägt, dass der empfohlene Debug-Vorgang für Syntaxfehler lautete: "Scrollen Sie durch das Fehlerprotokoll, bis Sie das erste Mal sehen, dass eine Ihrer eigenen Dateien einen Fehler aufweist. Wechseln Sie zu dieser Zeile und blinzeln Sie, bis Sie erkennen, was Sie tun falsch gemacht."

Wenn wir die Codegenerierung verwendet hätten, hätten wir viel leistungsfähigere Fehlerbehandlungsfunktionen mit vom Menschen lesbaren Fehlern. So ist das Leben.


0

Es gibt verschiedene Möglichkeiten, Code zu generieren. Sie könnten in drei Hauptgruppen eingeteilt werden:

  • Generieren von Code in einer anderen Sprache als Ausgabe eines Schritts im Kompilierungsprozess. Für den typischen Compiler wäre dies eine niedrigere Sprache, aber es könnte sich auch um eine andere höhere Sprache handeln, wie im Fall der Sprachen, die mit JavaScript kompiliert werden.
  • Generieren oder Transformieren von Code in der Quellcodesprache als Schritt im Kompilierungsprozess. Dies ist, was Makros tun.
  • Code mit einem Tool unabhängig vom regulären Kompilierungsprozess generieren . Die Ausgabe davon ist Code, der als Dateien zusammen mit dem regulären Quellcode existiert und zusammen mit diesem kompiliert wird. Beispielsweise können Entitätsklassen für einen ORM automatisch aus einem Datenbankschema generiert werden, oder Datenübertragungsobjekte und Dienstschnittstellen können aus einer Schnittstellenspezifikation wie einer WSDL-Datei für SOAP generiert werden.

Ich denke, Sie sprechen von der dritten Art des generierten Codes, da dies die umstrittenste Form ist. In den ersten beiden Formen ist der generierte Code ein Zwischenschritt, der sehr sauber vom Quellcode getrennt ist. In der dritten Form gibt es jedoch keine formale Trennung zwischen Quellcode und generiertem Code, außer dass der generierte Code wahrscheinlich einen Kommentar enthält, der besagt, dass "Diesen Code nicht bearbeiten". Es besteht immer noch das Risiko, dass Entwickler den generierten Code bearbeiten, was wirklich hässlich wäre. Aus Sicht des Compilers ist der generierte Code Quellcode.

Trotzdem können solche Formen des generierten Codes in einer statisch typisierten Sprache wirklich nützlich sein. Bei der Integration mit ORM-Entitäten ist es beispielsweise sehr nützlich, stark typisierte Wrapper für die Datenbanktabellen zu haben. Sicher, Sie könnten die Integration zur Laufzeit dynamisch handhaben, verlieren jedoch die Typensicherheit und die Toolunterstützung (Code-Vervollständigung). Ein Hauptvorteil der statischen Typensprache ist die Unterstützung des Typensystems bei der Art des Schreibens und nicht nur zur Laufzeit. (Umgekehrt ist diese Art der Codegenerierung in dynamisch typisierten Sprachen nicht sehr verbreitet, da sie in einer solchen Sprache im Vergleich zu Laufzeitkonvertierungen keinen Vorteil bietet.)

Das heißt, wenn es einen Codegenerator für etwas gibt, warum dann nicht eine ordnungsgemäße Funktion daraus machen, die die erforderlichen Parameter empfangen und die richtige Aktion ausführen kann, die der "erzeugte" Code ausgeführt hätte?

Da Typensicherheit und Code-Vervollständigung Funktionen sind, die Sie zur Kompilierungszeit (und beim Schreiben von Code in einer IDE) benötigen, werden reguläre Funktionen jedoch nur zur Laufzeit ausgeführt.

Es kann jedoch einen Mittelweg geben: F # unterstützt das Konzept von Typanbietern, bei dem es sich im Grunde um stark typisierte Schnittstellen handelt, die zur Kompilierungszeit programmgesteuert generiert werden. Dieses Konzept könnte wahrscheinlich viele Verwendungen der Codegenerierung ersetzen und eine sauberere Trennung von Bedenken ermöglichen.


0

Prozessorbefehlssätze sind grundsätzlich unerlässlich , aber Programmiersprachen können deklarativ sein . Das Ausführen eines Programms, das in einer deklarativen Sprache geschrieben ist, erfordert zwangsläufig eine Art Codegenerierung. Wie bereits in dieser und anderen Antworten erwähnt , besteht ein Hauptgrund für die Generierung von Quellcode in einer für Menschen lesbaren Sprache darin, die von Compilern durchgeführten raffinierten Optimierungen zu nutzen.


-3

Wenn etwas generiert werden kann, dann sind das Daten, kein Code.

Sie haben es falsch herum verstanden. Es sollte lesen

Wenn etwas in einen Generator für interpretierbare Daten eingespeist werden kann , dann ist das Code, nicht Daten.

Es ist das Quellformat für diese Kompilierungsphase, und das Senkenformat ist immer noch Code.


1
Falsche Definition des Quellcodes . Der Quellcode ist hauptsächlich für Menschen gedacht, die daran arbeiten (und diese bloße Tatsache definiert ihn, siehe auch, was freie Software von der FSF ist). Assembler-Code, der mit erzeugt wird, gcc -fverbose-asm -O -Sist kein Quellcode (und ist nicht nur oder größtenteils Daten), auch wenn es sich um eine Textform handelt, die immer an GNU weitergeleitet asund manchmal von Menschen gelesen wird.
Basile Starynkevitch

Außerdem werden viele Sprachimplementierungen zu C-Code kompiliert , aber dieses generierte C ist kein echter Quellcode (z. B. kann nicht einfach von Menschen bearbeitet werden).
Basile Starynkevitch

Schließlich interpretiert Ihre Hardware (z. B. Ihr AMD- oder Intel-Chip oder Ihr Computer-Motherboard) den Maschinencode (der offensichtlich kein Quellcode ist). Übrigens: Der IBM1620 verfügte über BCD-Maschinencode ( Keyboard Typable ), der jedoch nicht zum "Quellcode" wurde. Der gesamte Code ist keine Quelle.
Basile Starynkevitch

@BasileStarynkevitch Ah, du hast mich da hingebracht. Ich sollte nicht versuchen, meine witzigen Aussagen zu stark zu komprimieren, sonst ändern sie ihre Bedeutung. Richtig, Quelle Code sollte die Original - Code, der in die erste Compilation Stufe zugeführt wird.
Bergi

Kein Quellcode ist Code für Menschen. Es ist ebenso schwierig und subjektiv zu definieren wie Musik (vs. Klang). Es geht nicht darum, die Software zu finden, die sie verbraucht.
Basile Starynkevitch
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.