Automatische Programmierung: Code schreiben, der Code schreibt [geschlossen]


105

Nachdem ich das Buch The Pragmatic Programmer gelesen hatte , war eines der interessantesten Argumente "Code schreiben, der Code schreibt".

Ich habe versucht, im Internet nach weiteren Erklärungen oder Artikeln zu suchen, und obwohl ich einige gute Artikel zu diesem Thema gefunden habe, habe ich immer noch keine spezifische Code-Implementierung oder gute Beispiele gefunden.

Ich denke, es ist immer noch kein so verbreitetes Argument, etwas, das nicht dokumentiert ist oder von so vielen Leuten nicht angenommen wird, und ich würde gerne mehr darüber erfahren.

Was denkst du über das Thema? Ist es etwas, das Ihre Produktivität wirklich steigern wird? Was sind einige gute Ressourcen zu diesem Thema, darunter Bücher, Blogs, Diashows usw.?


Einige Codebeispiele würden mich sehr freuen, wenn ich deren Implementierung besser verstehen könnte.


Hier ist die Wiki-Seite zum Thema mit verschiedenen relevanten Programmiertechniken, wie Metaprogrammierung, generative Programmierung und Codegenerierung.


32
Ich habe einmal Code geschrieben, der Code schrieb, der Code schrieb ... :)
Benjol

9
@Benjol: Hast du in Lisp geschrieben?
Compman

11
Darüber hinaus wird dies bei serverseitigen Sprachen ständig durch Generieren von HTML, CSS und JavaScript erreicht. Sie könnten ein serverseitiges Skript haben, das ein serverseitiges Skript erstellt, das HTML mit Javascript erstellt, das mehr HTML erstellt, und niemand wird ein Auge darauf werfen, weil es so verbreitet ist.
zzzzBov

8
Wenn Sie dies noch nicht getan haben, lesen Sie diese IBM developerWorks-Artikelserie: " Die Kunst der Metaprogrammierung ", Teil 1 , Teil 2 und Teil 3 .
John Tobler

3
AtomWeaver ( atomweaver.com ) ist ein gutes Beispiel für die automatische Programmierung: Zunächst erstellen Sie wiederverwendbare Miniprogramme in Lua. Anschließend modellieren Sie Ihr System, indem Sie diese Assets wiederverwenden. AtomWeaver webt dann ein Lua-Programm, das Ihre "Mini-Generatoren" enthält, um den endgültigen Quellcode des Systems zu generieren. Sie können dann Ihr Modell optimieren und neu generieren.
Rui Curado

Antworten:


49

In der Lisp-Welt ist es durchaus üblich, den Code zu sehen, der Code schreibt, der Code schreibt (und so weiter). Jedes anständige Lisp- oder Scheme-Projekt ist also ein gutes Codebeispiel. Ich würde empfehlen, sich den Racket- Compiler und die Laufzeitquellen sowie Bigloo anzuschauen , deren Bibliotheken einfach brillant sind.

Produktivität: Ich verwende in fast allen meiner Entwicklungsarbeiten die Metaprogrammierung als vorherrschende Technik, und sie hilft eindeutig sehr, da sie sowohl die Codegröße verringert als auch die Lesbarkeit erhöht. Der Schlüssel liegt in der Verwendung domänenspezifischer Sprachen , und die Metaprogrammierung ist eine der effizientesten Möglichkeiten, diese zu implementieren.


67

Ich gehe lieber ein bisschen weiter und anstatt Code zu schreiben, der Code schreibt , schreibe ich Code, der Objekte, Methoden und Funktionen generiert. Dies kann beispielsweise mit Lisp-Makros oder dynamischen Ruby-Programmmodifikationsfunktionen erreicht werden.

Der kleine Unterschied ist, dass Sie nicht mit automatisch generierten Quelldateien enden. Normalerweise sind diese Dateien nicht lesbar und können nicht geändert werden. Warum sollten Sie sich also damit beschäftigen? Ich mag es nicht, meine Codebasis mit etwas zu vergrößern, das ich nicht kontrollieren kann.

Ein Buch, das ich gerne zu diesem Thema las, war Metaprogramming Ruby (wenn Sie die Ruby-Sprache beherrschen ).


Bearbeiten Sie nach der folgenden Frage im Kommentar:

Warum sollte es nützlich sein, dass ich den generierenden Code noch codieren muss? Sollte ich einen Code schreiben, der je nach Benutzereingabe unterschiedliche Dinge erzeugen kann, damit ich ihn immer wieder verwenden kann?

Erstens ist die Metaprogrammierung kein Ziel, sondern ein Werkzeug. Verwenden Sie keine Metaprogrammierung, weil "es ist cool" oder "X sagte, jeder Entwickler sollte es verwenden".

Ich denke, ein guter Grund für die Verwendung der Metaprogrammierung besteht darin, ein allgemeines Muster (Muster als Wiederholung) zu verallgemeinern, das Sie in Ihrem Code gefunden haben und das mit keiner anderen üblichen Programmiertechnik (Vererbung, Entwurfsmuster usw.) erreicht werden kann.

Wie Jordan sagte , ist ein typischer Anwendungsfall das Datenbankhandling und ORM (Object Relation Mapping). In Ruby sollten Sie sich noch einmal ActiveRecord ansehen , das ein hervorragendes Beispiel für die auf ORM angewendete Metaprogrammierung ist.

Als letzte Anmerkung:

Denken Sie nicht "Ich möchte die Metaprogrammierung anwenden, wo könnte ich sie in meinem Code anwenden?".

Denken Sie "Ich sehe dieses Muster, das sich im gesamten Code wiederholt. Ich kann keinen Weg finden, den Code in etwas Kleineres und Wiederverwendbareres umzugestalten. Vielleicht kann mir die Metaprogrammierung helfen?"


3
@Jose: Am häufigsten generieren Sie Code über Vorlagen. Es gibt zum Beispiel Apache (N-) Velocity oder die Visual Studio T4 Templates. Dann haben Sie nur noch ein Programm, das die Metadaten in Ihre Vorlagen einspeist und daraus neue Dateien erstellt. Es ist ziemlich einfach und ich mache es die ganze Zeit, um UI-Skelette, Entitäten usw. zu generieren
Falcon

2
@ Jose Faeti, werfen Sie einen genaueren Blick auf Lisp-Makros (oder Clojure oder Nemerle, abhängig von Ihren Plattformeinstellungen).
SK-logic

1
Ich würde hinzufügen, dass Metaprogrammierung einige Muster wie Richtlinien oder Status ersetzen kann, jedoch ohne Laufzeitkosten. Dies gilt nicht nur für Probleme, die mit gängigem Refactoring nicht gelöst werden können, sondern manchmal auch für eine bessere Alternative.
Deadalnix

1
@ Jose Faeti: Ich sehe, dass Sie etwas Python kennen. Es hat auch Metaprogrammiermöglichkeiten, obwohl ich diese nicht wirklich benutzt habe. Werfen Sie einen Blick auf Dangerously Advanced Python PDF
Kit

3
@ Falcon: IMO, das ist der schlechteste Weg, Code zu generieren. Für Sprachen ohne eingebaute Metaprogrammierfunktion ist die Umgehungsmöglichkeit sehr schlecht. Anstatt Java oder C # zu generieren, ist es besser, diesen Code in einer höheren JVM- oder .NET-Sprache zu schreiben.
Kevin Cline

19

Verwenden Sie noch besser den Code, den jemand anderes geschrieben hat und der Ihren Code für Sie schreibt.

Die Code-Automatisierung eignet sich im Allgemeinen für ORMs und anderen Datenbank-Interaktionscode und natürlich für die wiederholte, aber ähnliche Code-Erstellung.

Natürlich, wenn Sie viele ähnlich aussehende Klassen aufbauen, hätten Sie vielleicht das Gleiche in einer dynamischen Sprache viel früher erreichen können, aber ich schweife ab.

Dies wird von vielen Menschen begrüßt, obwohl die Software häufig als Codegeneratoren bezeichnet wird.

Sehen Sie sich Unternehmen und Produkte wie CodeSmith und MyGeneration an oder stöbern Sie in diesem Wikipedia-Artikel: http://en.wikipedia.org/wiki/Comparison_of_code_generation_tools


6
Besser geht es nicht. Ihr kostbarer kleiner, sehr eigener Code kann nicht richtig von einem Code-Generierungstool eines anderen Typen verwaltet werden, da dieser andere Typ nichts über Ihre Besonderheiten weiß. Die produktivste Verwendung der Metaprogrammierung ist die Implementierung domänenspezifischer Sprachen - und wie der Name schon sagt, sind diese spezifisch für Ihre eigentliche Problemdomäne und können nur von Ihnen selbst implementiert werden.
SK-logic

@ SK-logic: was ist mit ORM generiertem Code? Es wird von einem anderen Tool / einer anderen Bibliothek generiert und erfüllt immer noch viele Projektanforderungen.
David

@ David, um ehrlich zu sein, ich bin nicht ganz überzeugt von den generischen ORMs. Ich hatte in der Vergangenheit so viele Probleme mit ihnen, dass ich stattdessen oft auf die Implementierung meiner kleinen, eigenen ORMs zurückgegriffen habe.
SK-logic

1
@Jordan, all diese Tools sind zu spezifisch (und schlechter - textbasiert , dh von vornherein minderwertig). Ich spreche stattdessen von der richtigen Metaprogrammierung.
SK-logic

1
@AtillaOzgur, sie können "sehr gut" sein, stimmt. Aber sie sind nicht besser als eDSLs. Die Erzeugung von Standalone-Code ist offensichtlich wesentlich eingeschränkter und weniger flexibel als die Makro-Metaprogrammierung.
SK-logic

16

Eines der klassischen Beispiele ist Lex und Yacc. Ihr Hauptzweck ist es, die Plackerei beim Schreiben jeglicher Art von Parser zu vermeiden. Auf dem Weg dorthin können komplexe Parser mit vielen Regeln und Zuständen viel schneller erstellt werden, und es werden auch alle Überraschungsfehler vermieden, die von Leuten begangen werden, die ihre eigenen machen.

Dies ist auch die Idee hinter c, einem Tool zum Schreiben von Assemblern. Das Gleiche gilt für jede höhere Sprache, die Sie benennen möchten. Für Tools, die Code für Sie schreiben, gibt es einige einfache Paradigmen.

Eine ordnungsgemäße IDE hilft, indem sie Dokumentation, intelligente automatische Vervollständigung und Codefragmente zur Hand gibt. Die IDEs enthalten auch verschiedene Vorlagen, sodass Sie ein Programm nicht von Grund auf neu starten müssen. Es gibt Programme, um ein UML-Diagramm zu erstellen und Klassen in einer höheren Sprache aufzubereiten.

Schließlich können Sie Ihre eigenen Tools für die Codegenerierung innerhalb Ihres Problemsets schreiben. So haben Lex und Yacc angefangen. Aus genau diesem Grund gibt es jede Art von domänenspezifischer Sprache. Sie erstellen einige Bausteine, die Ihre Lösung in einfacher verständlichem Code beschreiben, und fassen allgemeine Aktivitäten oder komplizierte Abschnitte mit einfachen Befehlen zusammen. Sie suchen nicht für jedes Problem eine Lösung, sondern eine einfachere Definition des konkreten Problems, mit dem Sie sich befassen.

In gewisser Weise ist alles, was Sie über der Binärschicht tun, Code-Automatisierung.


Das ist eine wirklich schöne Aussicht. Alles in allem ist dies nur eine der vielen Methoden, mit denen Programmierer versuchen, ihre Operationen zu vereinfachen und sich auf eine höhere Codierungsebene zu konzentrieren, anstatt Details zum Syntaxcode.
Jose Faeti

1
@Jose Faeti Der Wikipedia-Artikel en.wikipedia.org/wiki/Automatic_programming enthält Links zu verschiedenen Tools, falls Sie an weiteren Details interessiert sind. Ich würde auch vorschlagen, in Lex und Yacc nachzulesen, da es für diese ziemlich viel mehr Dokumentation und Beschreibung gibt.
Spencer Rathbun

In ausreichend leistungsfähigen Sprachen (z. B. C ++ im Gegensatz zu C) sind externe Tools wie Lex und Yacc nicht erforderlich.
kevin Cline

YACC schreibt keinen "Parser". Es schreibt eine bestimmte Art von Parser (LALR), die ohne automatisierte Hilfe nur sehr schwer zu finden ist. Es gibt eine andere Art von Parser (rekursiver Abstieg), die viel einfacher zu schreiben und zu korrigieren ist und dementsprechend leichter zu lesen und zu verstehen ist, was vor sich geht.
Mason Wheeler

@MasonWheeler Die Art von Parser bezog sich auf die Grammatiken, die zur Lösung von Problemen im weiteren, nicht genauen Sinne erstellt werden können. Als ich es ein Jahr später las, war es nicht so klar, wie ich es mir gewünscht hätte. Ich bin mir jedoch nicht sicher, ob LL (*) -Parser einfacher zu schreiben und zu verwenden sind.
Spencer Rathbun

13

Metaprogrammierung

Metaprogrammierung ist in vielen Geschäften eine umstrittene Technik. Der Grund dafür ist, wie bei jedem leistungsfähigen Werkzeug, die Größe der Hilfe oder des Schmerzes groß.

Vorteile

  • Ausdrucksvoller, weniger Code zum Schreiben und Verwalten (oft um eine Größenordnung oder mehr)
  • Konsistenz, konsistenteres Verhalten in Bezug auf die Klasse der Probleme, die Sie mit dem Code lösen
  • Produktivität, weniger Code für eine Lösung für einen größeren Problembereich

Nachteile

  • Komplexität, es kann sehr kompliziert sein, obwohl es weniger Code gibt
  • Sicherheit, gelegentliche Typensicherheit und statische Analyse im Allgemeinen werden geopfert
  • Fehler betreffen mehr, kleine Fehler haben größere Auswirkungen

Ich bin ein großer Fan von Metaprogrammierung, aber ich mache das schon lange. Für mich macht der Kompromiss zwischen reduzierter Codegröße und konsistentem Verhalten die Risiken mehr als wett. Weniger Code bedeutet weniger Fehler, weniger Code, der gewartet werden muss, und ich kann normalerweise sehr schnell umfangreiche Funktionen hinzufügen.

Dies bedeutet jedoch nicht, dass ich denke, dass sich alle Programmierer damit befassen sollten. Ich habe große Probleme gesehen und musste sie beheben, die durch Metaprogrammierung entstanden sind. In der Regel von Personen, die das Konzept nicht verstehen und versucht haben, die Funktionalität zu erweitern oder nur einen Fehler zu beheben. Es braucht eine bestimmte Denkweise, die zumindest detailorientiert ist. Die Frage zur Verwendung von Metaprogrammiertechniken sollte eine Teamentscheidung sein . Wenn Sie Teammitglieder haben, die nicht verstehen, nicht das Temperament dafür haben oder einfach dagegen sind, sollte kein Teammitglied Metaprogramme verwenden.


Vielen Dank für die nützlichen Überlegungen! Könnten Sie mir eine wirklich einfache und grundlegende Aufgabe vorschlagen, die ich mithilfe der Metaprogrammierung umsetzen kann, was mir gegenüber der normalen Codierung Zeit spart, ein kleines Codebeispiel?
Jose Faeti

haha, ich erinnere mich an einen Fehler, den ich vor einigen Jahren bei GCC hatte. 162 Zeilen, um die Fehlermeldung auf meinem Bildschirm zu platzieren. Rekursive Metaprogrammierung FTW!
Deadalnix

6
Die Komplexität der Metaprogrammierung wird stark überbewertet. Es ist absolut nichts kompliziertes dabei, solange Sie die richtigen Werkzeuge verwenden. Und DSLs sind viel einfacher zu debuggen und zu warten als der typische Kesselcode. Ich kann auch nicht verstehen, warum man auf Typensicherheit verzichten sollte - genau das Gegenteil ist der Fall, DSLs haben möglicherweise auch domänenspezifische, hocheffiziente Typensysteme.
SK-logic

2
@ SK-logic: Nicht alle Sprachen unterstützen Metaprogrammierung. So werden manchmal Dinge wie Typensicherheit geopfert (zB C) . Auch bei der Metaprogrammierung handelt es sich nicht nur um DSLs. Es umfasst Dinge wie Versandprogrammierung, Generika, Currying, Objektprüfung, dynamische Anwendung usw. Was die Komplexität betrifft, fällt es uns (Leuten mit Erfahrung in der Metaprogrammierung) leicht, zu sagen, dass dies nicht kompliziert ist. Ich habe andere Schwierigkeiten mit dem Verständnis aller Fälle gesehen, unter denen der Code ausgeführt wird. Es hängt hauptsächlich von ihrer Erfahrung und der Technik ab.
Dietbuddha

@dietbuddha, könntest du bitte näher darauf eingehen, warum soll man die Sicherheit ihres eigenen DSL opfern, egal wie es implementiert ist? Sie können einen Ad-hoc-Interpreter in einem reinen C mit einem starken Typsystem schreiben (siehe z. B. Umarmungen). Sie können einen Codegenerator schreiben, der auf C abzielt und die gesamte Typprüfung selbst durchführt, ohne sich auf das System des Zielsprachentyps zu verlassen. Für die Komplexität: Die meisten Leute machen es auf unnötig komplexe Weise, während für die Codegenerierung dieselben Entwurfsmethoden angewendet werden können wie bei der "normalen" Programmierung. Es sind fast keine neuen Kenntnisse erforderlich.
SK-logic

9

Der meiste Code schreibt Code. Zum Beispiel hilft PHP-Code beim Schreiben von HTML. Die PHP-PDO-Bibliothek hilft beim Schreiben von SQL-Aufrufen. Die Datei-E / A-Funktionen schreiben Code für die Kommunikation mit dem Betriebssystem. Sogar ein regulärer Funktionsaufruf ist ein Verweis auf einen anderen Codeblock, der ausgeführt wird. Ihre Funktionsaufrufe schreiben also Code.

Allgemein können wir uns das Schreiben von Codes vorstellen, die rekursiv Codes schreiben und einen Stapel bilden, der endet, wenn er gegen die physikalische Realität von in Hardware verkabelten Codes stößt.


3
Ich würde HTML nicht als Programmiersprache bezeichnen. Es ist eine Syntax für Dokumente
Simon Bergot

3
@ Simon es ist ein interessanter Punkt. Es gibt eine Vielzahl von Ausdrucksmöglichkeiten für die verschiedenen Codes, die wir verwenden. Code kann in eine schwächere Sprache, eine stärkere Sprache oder eine eigene Sprache schreiben.
Ben Haley

5

Wie Sie dies tun, hängt von Ihren Anforderungen ab. Angenommen, Sie verwenden die statische Codegenerierung, können Sie die gesamte Infrastruktur selbst schreiben oder einen vorhandenen Generator wie CodeSmith oder MyGeneration verwenden. Mit diesen müssen Sie nur die erforderlichen Vorlagen schreiben.

Mein letztes Projekt, das dies betraf, waren einige grundlegende ASP.NET CRUD-Bildschirme (die Codegenerierung ist hierfür gut geeignet). Der Prozess ging Entitäten als Metadaten in XML-Dateien definieren. Schreiben Sie Vorlagen, um die verschiedenen erforderlichen Artefakte abzudecken (Entitätsklassen, Repositorys, Serviceklassen, asp.net-Steuerelemente, asp.net-Seiten usw.). Führen Sie den Generierungsprozess aus und gestalten Sie die Ausgabe.

Das Schreiben der Vorlagen ist mit einem gewissen Aufwand verbunden, kann jedoch für nachfolgende ähnliche Projekte wiederverwendet werden. In ähnlicher Weise werden Änderungen an den zugrunde liegenden Daten behandelt, indem die Metadaten geändert und die Generierung erneut ausgeführt wird, wodurch Änderungen einfacher und schneller implementiert werden können.

Wie zum Testen. Da es sich um ein Templat-System handelt, müssen Sie zunächst einige Zeit damit verbringen, die Ausgabe des Prozesses zu validieren. Wenn Ihre Vorlage falsch ist, ist die gesamte Ausgabe dieser Vorlage in ähnlicher Weise falsch. Wenn Sie damit zufrieden sind, können Sie mit den Codegeneratoren auch grundlegende Tests aus den XML-Metadaten erstellen, die Sie dann erweitern können, um Sonderfälle abzudecken. Denken Sie jedoch daran, dass Sie möglicherweise immer noch Code-Tests durchführen müssen, um bestimmte Dinge zu berücksichtigen. Die Code-Generierung reduziert Ihre Arbeit und beseitigt sie nicht vollständig.


5

In unserem Unternehmen verwenden wir einige Tools, die C ++ - oder C # -Klassen mit aus dem Internet heruntergeladenen Daten generieren. Diese Klassen sind Datencontainer und enthalten eine große Anzahl von Objekten in Listen.


So etwas wie die Code-Schnipsel, die in einer IDE wie Visual Studio zum Beispiel zu finden sind?
Jose Faeti

@Jose Unser Tool ist nur eine Anwendung zum Konvertieren von HTML-Ausgaben in eine Klasse. Anstatt die Daten bei jedem Start der Anwendung herunterzuladen, laden wir sie einmal herunter und bilden daraus eine Klasse.
Holli

5

Die Metaprogrammierung ist seit langem Teil der Programmierung. Betrachten Sie nicht nur Tools wie SWIG oder WYSIWYG-Designer, die Code erstellen, sondern auch Tools in Sprache wie Cs Präprozessor oder sogar C ++ - Vorlagen und C # / Java-Generika - ganz zu schweigen von Reflection.

Tatsächlich könnte man argumentieren, dass jeder Compiler nur ein anderes Metaprogramm ist - er nimmt Programmtext und Ausgabemaschinen- oder VM-Code auf. Und Leben ohne Compiler? Owch.


Das stimmt, aber wie können Sie es tatsächlich in Ihre eigene Programmiersprache implementieren, um Ihre Produktivität tatsächlich zu steigern? Das fehlt mir.
Jose Faeti

5

Hier ist ein konkretes Beispiel aus meiner Vergangenheit.

Ich arbeitete an einer Site mit etwa 50 MB Delphi-Quellcode, der die BDE für den Datenzugriff verwendete. Sie wollten zu Direct Oracle Access wechseln, um ein Oracle-Upgrade über die höchste von der BDE unterstützte Version hinaus zu ermöglichen (8i, wenn ich mich richtig erinnere).

Anstatt ein Team von Programmierern zu veranlassen, alle Formulare und Datenmodule manuell zu ändern, habe ich ein PERL-Skript geschrieben, das:

  1. Analysierte das DFM (Formulardatei) und identifizierte alle TQuery-, TTable-, TStoredProcedure- und TDatabase-Objekte, wobei die Elemente in einer Liste gespeichert wurden.

  2. Analysierte den PAS (Code) und identifizierte die Verwendung der Objekte - führten die TQueries Aktualisierungen oder Auswahlen durch? Außerdem wurden alle Objekte identifiziert, die im Code erstellt wurden, anstatt in der IDE auf einem Formular abgelegt zu werden.

  3. DFM & PAS wurden umgeschrieben, und die Objekttypen wurden entsprechend geändert (z. B. TTable -> TOracleDataSet, wobei die SQL-Eigenschaft auf "select * from" usw. gesetzt war) und die Methodenaufrufe wurden durchgeführt. Gegebenenfalls wurden zusätzliche Methodenaufrufe hinzugefügt, um Parameter zu schließen, zu öffnen und festzulegen.

Kurz gesagt, 3 Wochen Arbeit, um das Skript an verschiedenen Anwendungen zu optimieren, die von verschiedenen Teams mit unterschiedlichen Codierungsstilen geschrieben wurden, anstatt der ursprünglichen Schätzung, dass mehr als 5 Entwickler 6 Monate lang arbeiten.

Und der Grund, warum ich überhaupt daran gedacht habe, diesen Ansatz zu verwenden, war das Lesen von The Pragmatic Programmer


Das ist großartig, ich bin jetzt seit ein paar Tagen bei Perl und habe bereits einige Produktivitätswerkzeuge erstellt, um grundlegende Arbeitsbereiche für die Webentwicklung mit allen Verzeichnissen, Dateien usw. zu generieren, indem ich einfach "create workspace" eingebe! :)
Jose Faeti

1
@Jose Das ist die Idee. Verwenden Sie Skriptsprachen, um sich wiederholende Aufgaben zu automatisieren. Dies kann einmalig sein, wenn Sie eine 8-fache Produktivitätssteigerung erzielen, oder, wie in Ihrem Fall, zeitaufwändig, wenn Sie dies immer wieder tun.
Mcottle

4

Sie fragen nach Beispielen ....

Wenn Sie mit SQL arbeiten, sollten Sie die Datenbank nicht direkt ändern, sondern Skripts ausführen, die die gewünschten Änderungen vornehmen, einschließlich struktureller Änderungen an der Datenbank (Hinzufügen von Tabellen, Spalten, Primärschlüsseln, Einschränkungen usw.). . Häufig müssen Sie dieselbe Aktion für viele Tabellen oder Spalten gleichzeitig ausführen, und es wäre mühsam, sie einzeln auszuführen. Ein kurzes Skript, das ein größeres Skript ausgibt, das das tut, was Sie wollen, kann real sein Zeitersparnis.

Zum Beispiel, bevor der Datentyp DATE in MS SQL Server eingeführt wurde, war die einzige Wahl für eine Datumsspalte DATETIME, die einen Zeitteil hat - einen Zeitteil, der den Umgang mit den Daten etwas erschwert. Nach dem Upgrade auf eine Version mit dem Datentyp Datum möchten Sie möglicherweise die Spalten aktualisieren, bei denen die Uhrzeit immer 00:00 Uhr ist. In einer Datenbank mit Dutzenden oder sogar Hunderten von DateTime-Spalten wäre dies recht zeitaufwändig. Es ist jedoch einfach, ein Skript zu schreiben, das alle Tabellen abfragt, wobei jede Spalte mit dem Datentyp DATETIME überprüft wird, um festzustellen, ob es jemals eine andere Zeit als 00:00 Uhr gibt, und wenn dies nicht der Fall ist, eine ALTER-Anweisung für die zu ändernde Tabelle / Spalte zu erstellen der Datentyp auf DATE. Presto, Code, der Code schreibt.


3

Schauen Sie sich die CL-Makros (Common Lips) an. Meiner Meinung nach ist das genau das, was Sie wollen. Lippen sind perfekt für die Metaprogrammierung.

Außerdem empfehle ich Nemerle, wenn Sie .NET-Fähigkeiten mit perfekter Metaprogrammierungsunterstützung (einschließlich Makros) haben möchten.

Aber wenn Sie eine echte Code-Generierungs-Engine wollen, schauen Sie sich Apache Thrift an


3

Ich arbeite gerade an einem solchen Tool. In unserem speziellen Fall generieren wir den VB.NET-Code basierend auf den Signaturen der Funktionen in der Datenbank für die Datenschicht.

Es ist zunächst schwierig, mit der Codegenerierung zu beginnen, da Sie keine Ahnung haben, wie der Code generiert werden soll. Wenn Sie jedoch über einen festgelegten Satz von Regeln verfügen und der zu generierende Code immer auf der Grundlage dieser Regeln generiert werden kann Das Arbeiten mit diesem Code ist nicht so schwierig. Abhängig von der Komplexität der Codegenerierung und der Anzahl der Regeln kann die Aufgabe natürlich schwieriger werden. Im Wesentlichen wird die automatische Codegenerierung jedoch für sich wiederholende Codierungsaufgaben verwendet und nicht für erweiterten Code, der sehr unterschiedlich ist.

Es gibt zwei Möglichkeiten, die Ausgabe zu testen. Zuerst müssen Sie sicherstellen, dass der Code kompiliert wird, und das ist einfach. Dann müssen Sie sicherstellen, dass die Ausgabe auf der Grundlage der Parameter, mit denen sie generiert wurde, genau das tut, was Sie beabsichtigt haben. Die Schwierigkeit hängt von der Komplexität des von Ihnen generierten Codes ab.

Meine aufrichtige Empfehlung lautet, dass Sie sich die Zeit leisten können , wenn Sie das Gefühl haben, Code wiederholt zu schreiben . Versuchen Sie zu überlegen, ob das, was Sie tun, nicht durch generierten Code erreicht werden kann. Und wenn ja (wenn es sich um sich wiederholenden Code handelt, der fast immer der Fall ist), überlegen Sie, wie oft Sie diesen Code erweitern, leicht ändern und wie oft Sie genau diesen Code schreiben müssen. Wenn die Antwort auf eine dieser Fragen "viele" ist , sollten Sie ernsthaft in Betracht ziehen, einen Generator für diesen Code zu erstellen .

Hoffe das hilft,
IPP


Danke für die Antwort! Wie sind die Regeln in Ihrem Beispiel tatsächlich implementiert?
Jose Faeti

1
Ich kann Ihnen nicht alle Regeln nennen, aber ich kann Ihnen einige Beispiele geben. Wir analysieren die von einer Oracle-Datenbank bereitgestellte Schnittstelle und berücksichtigen die Signaturen der Funktionen in der Oracle-Schnittstelle. Basierend auf der Signatur generieren wir den Namen der Datenschichtfunktion. Wir wissen, dass wir aus der Datenbank immer eine Oracle-Datentabelle als Ergebnis erhalten, die wir analysieren und in einem Array von speziellen Objekttypen speichern, die wir zum Speichern unserer Daten verwenden. Basierend auf den Eingabe- / Ausgabeparametern der DB-Funktionssignatur fügen wir den von uns generierten Funktionen entsprechende Eingabe- und Ausgabeparameter hinzu und so weiter.
Ioan Paul Pirau

3

Ich habe ein PHP-Modul, das eine Webseite ausgibt, die JavaScript-Code enthält, der HTML generiert. Das sind genau dort drei Schichten. Junge war so schwer zu lesen!

In einer Programmierklasse mussten wir ein Programm schreiben, das dem Benutzer eine Formelzeichenfolge entnimmt, diese analysiert und den Wert anzeigt. Der eindrucksvollste Löser hat einfach die Benutzereingaben übernommen, in main () {printf ("% d", ...);} eingeschlossen und ein Skript zum Kompilieren, Verknüpfen und Ausführen ausgeführt. Er hat keinen Parser geschrieben! Heute könnten Sie das in einer SQL SELECT-Anweisung tun.

Es ist ein Werkzeug, mit dem Sie spielen und es dann für einen zukünftigen Tag aufbewahren sollten, wenn es nützlich sein wird.


Das ist genau das, was ich versucht habe umzusetzen! :) Aber dann habe ich beschlossen, es mit Perl offline zu programmieren und es funktioniert großartig. Ich habe eine Menge Funktionen, die ich hinzufügen möchte!
Jose Faeti

Ich schreibe Code mit bis zu 20 Sprachebenen, die sich problemlos in Sprache verwandeln lassen. Es ist nicht komplizierter als eine Call-Stack-Tiefe von 20 Schichten. Aus diesem Grund bin ich nicht der Meinung, dass es sich um ein Tool handelt, mit dem Sie es " für einen zukünftigen Tag aufbewahren können, an dem es praktisch ist " - die Codegenerierung ist immer praktisch.
SK-logic

3

Ich habe mit Prolog nette Meta-Programmierlösungen entwickelt . Wobei die Hauptanwendung (in C ++ etwa) eine abstrakte Definition eines Problems zur Laufzeit in eine Prolog-Anwendung übersetzt, die dann an delegiert wird. Häufig dauerte es ewig, äquivalente Funktionen in C ++ zu schreiben.

Ich denke, dieses Szenario ist ein ausgezeichneter Fall für das Argument Code-Schreiben-Code .


3

Was denkst du über das Thema?

Die Metaprogrammierung wird am häufigsten mit nicht dynamischen Sprachen in Verbindung gebracht, da es schwieriger ist, bestimmte Verhaltensweisen (z. B. die Implementierung eines ORM) ohne viele unproduktive und nicht intelligente Codezeilen zu erreichen.

Aber auch in dynamischeren Sprachen wie PHP kann die Codegenerierung wirklich lebensrettend sein und die Produktivität enorm steigern. In modernen Frameworks ist es sehr verbreitet, ein Gerüst zu haben, das die meisten gängigen Modelle, Formen, Tests und Aktionen für ein bestimmtes Geschäftsobjekt generiert, das Sie deklarieren. Dies ist einer der Gründe, warum Frameworks wie Symfony oder RoR so erfolgreich sind, dass diese Tools zur Codegenerierung sehr schnell konsistenten Code erstellen und die Produktivität des Programmierers steigern.

Auf Websites dreht sich der Großteil der Interaktion um vier Hauptaktionen:

  • Erstellen Sie ein Element
  • Abrufen einer Reihe von Elementen (mit möglicher Filterung)
  • Aktualisieren Sie ein Element mit neuen Attributen
  • Löschen Sie eine Gruppe von Elementen

Zumindest alles, was sich um diese 4 Hauptaktionen dreht, könnte und sollte IMHO mit Code-Generierungs-Tools erreicht werden, um maximale Produktivität zu erzielen.

In meinem Unternehmen verwenden wir symfony, und der Admin-Generator ist ein außergewöhnliches Tool, das sogar Code zur Laufzeit generiert (und zwischenspeichert), was bedeutet, dass wir dazu nicht einmal irgendeine Aufgabe oder ein externes Tool verwenden müssen Um neuen Code zu generieren, müssen wir nur unseren Cache bereinigen. Ich rate dringend, diese Art von Tool für CRUD-Operationen zu verwenden.

Aber es ist keine leichte Aufgabe, das zu tun, was symfony großartige Mitwirkende getan haben. Ich habe einige Code-Generierungsaufgaben selbst implementiert und es ist nicht einfach, etwas zu tun, das wirklich konsistent ist und mit einer breiten Implementierung die meisten Eckfälle abdeckt.

Ist es etwas, das Ihre Produktivität wirklich steigern wird?

Ich glaube, dass Metaprogrammierung in niedrigeren Arbeitsebenen (Frameworks, Caching, Compiler usw.) sehr wichtig ist, aber etwas, das wir mit äußerster Vorsicht angehen müssen, wenn wir Dinge auf der Business-Ebene tun.

Die Verwendung von Code-Generierung ist ohne Frage ein großer Produktivitätsschub. Implementieren Sie Ihre eigenen Tools zur Codegenerierung, nicht so sehr, es sei denn, Sie erstellen selbst ein Framework.

Was sind einige gute Ressourcen zu diesem Thema, darunter Bücher, Blogs, Diashows usw.?

Die beste Quelle, um die Programmierung zu verstehen, ist immer guter und gut kommentierter Quellcode. Ich würde sagen, dass ein Blick in RubyOnRails und Symfony- Administratoren eine gute Idee ist.


3

Während sich viele Antworten hier auf das beziehen, was allgemein als Metaprogrammierung bekannt ist, gab es tatsächlich ein mit AI verbundenes Feld, das als automatische Programmierung bekannt ist und sich mit dem Verstehen oder Synthetisieren von Programmen befasst [1].

Jeder Compiler (oder Metaprogramm, Codegenerator, Übersetzer, Makrosystem, ...) arbeitet mit Transformationen und generiert aus einer Eingabe eine Ausgabe, indem er seinen festen Transformationsalgorithmus ausführt. Ein herkömmlicher Compiler oder ein herkömmliches Metaprogramm erstellt jedoch angesichts einer Definition, Beschreibung oder eines Beispiels für das Sortieren einer Liste (z. B. [5, 3, 9] => [3,5,9]) keinen Sortieralgorithmus. Solche Probleme waren von Interesse für dieses "automatische Programmieren".

[1] - Fortschrittsbericht zu Systemen für das Programmverständnis ftp://db.stanford.edu/pub/cstr/reports/cs/.../CS-TR-74-444.pdfShare


2

Meta-Programmierung kann sehr schwierig zu pflegen sein. Am Anfang sieht es elegant aus, aber wenn Sie anfangen, auf Eckfälle zu stoßen, werden die Fehler zu spät abgefangen (auf dem Code, der generiert wurde), und das Ganze wird zu einem Albtraum, den Sie benutzen / debuggen müssen.

Ich habe hauptsächlich Python-Code geschrieben und meiner Erfahrung nach ist Meta-Programmierung in dieser Sprache immer eine schlechte Wahl. Sie können Dinge, die Sie mit langweiligen normalen Sprachfunktionen erledigen möchten, jederzeit umgestalten. Das Ergebnis ist weniger funky, aber einfacher zu leben.


Jede Art von Code kann sehr schwierig zu pflegen sein. Und kann sehr einfach sein, wenn es richtig gemacht wird. Durch Metaprogrammierung kann die Wartbarkeit in der Tat um Größenordnungen erhöht werden. Ihre Python-Erfahrung ist wahrscheinlich für die eigentliche Metaprogrammierung irrelevant, da Python mit seinem ungeschickten, zu tiefen AST für diese Denkweise nicht sehr geeignet ist. Aber selbst mit Python habe ich die Tempita-Bibliothek mit hoher Effizienz verwendet und hatte nie Probleme mit der Wartbarkeit, auch nicht mit einem Team, das fast keine Python-Vorkenntnisse hatte.
SK-logic

Ich interessiere mich für Ihren Punkt über Python AST. Haben Sie Tempita für Meta-Programmierzwecke verwendet?
Simon Bergot

Dies ( docs.python.org/library/ast.html ) ist ein Ad-hoc-AST, und der Parser liefert einen nicht optimierten, überlasteten Baum, wodurch eine Analyse problematisch wird (insbesondere, wenn in Python keine ordnungsgemäße Musterübereinstimmung vorliegt). Das Erzeugen eines solchen AST ist ebenfalls nicht sehr bequem. Ich habe Tempita sowohl für die Erstellung von Python- als auch von C-Code verwendet (dh reine textbasierte Metaprogrammierung), es hat für diese spezielle Aufgabe gut funktioniert (Boilerplate-Code-Generierung). Ich habe auch oft Python verwendet, um C-Code aus einigen XML-High-Level-Beschreibungen zu generieren.
SK-logic

2

OP fragt nach Ressourcen.

Sie könnten unser DMS Software Reengineering Toolkit interessant finden. Es handelt sich um ein reines Metaprogrammierungswerkzeug, mit dem benutzerdefinierte Programmanalyse- und Transformationswerkzeuge erstellt werden können.

[Um einem Kommentar zur Frage von OP zu folgen, ist DMS bei der Erstellung eines bestimmten Transformationstools eine Produktlinie, die Code schreibt, der Code schreibt:]

DMS erreicht dies, indem es agnostisch (aber nicht unabhängig) von Zielprogrammiersprachen ist. DMS bietet die Standarddienste, die für eine Vielzahl von Metaprogrammierungsaufgaben erforderlich sind, ebenso wie ein Betriebssystem eine Vielzahl von Diensten für Standardprogrammierungsaufgaben bereitstellt. Diese Dienste umfassen starkes Parsen, automatisches Erstellen von Abstact-Syntaxbäumen, Musterabgleich und Umschreiben von Bäumen, Symboltabellenbibliotheken, mit denen Sprachen mit unangenehmen Bereichsregeln wie Mehrfachvererbung, Kontrollfluss, Datenfluss, Point-to- und Aufruf einfach verwaltet werden können Graph-Analyse. Nichts davon ist bedeutungslos, da keine spezifischen zu verarbeitenden Sprachen vorhanden sind. Daher akzeptiert DMS Sprachdefinitionen, die an diese allgemeinen Maschinen gebunden sind. Dies führt zu sprachspezifischem Parsing, AST-Konstruktion und zielsprachspezifischem Pattern Matching / Rewriting unter Verwendung der Zielsprache. Sprachsyntax,

Und wie ein Betriebssystem ist DMS so konzipiert, dass nur sehr wenige Meinungen oder Einschränkungen zu den (Meta-) Programmen bestehen, die Sie schreiben möchten. Dies bedeutet, dass es für eine Vielzahl von Zwecken verwendet werden kann: Extrahieren von Metriken, Auffinden von totem Code, Implementieren von Aspektwebern, Übersetzen Sprachen, Generieren von Codes aus DSLs, Neuarchitektur großer Anwendungen. (DMS wurde bereits für alle diese Aufgaben verwendet).

Man braucht robuste Sprachdefinitionen, wenn man nicht alles im Sprachreferenzhandbuch codieren möchte (überlegen Sie, was dies für Java und C ++ bedeutet). DMS löst dieses Problem, indem eine Bibliothek mit vollständigen Sprachdefinitionen zur Verfügung steht. Das Analoge hier ist wie eine Datenbank, die für Ihr Betriebssystem zur Verfügung steht. Sie müssen keinen von ihnen implementieren, um mit dem Schreiben Ihrer datenbankzentrierten Anwendung fortzufahren.


2

Siehe Philip Greenspuns Problemsatz 4 aus MIT-Kurs 6.916: Software-Engineering innovativer Webdienste ( http://philip.greenspun.com/teaching/psets/ps4/ps4.adp ).

Das Ziel lautet: "Bringen Sie den Schülern die Vorzüge von Metadaten bei. Insbesondere lernen sie, wie sie die Anforderungen eines Webdienstes formal darstellen und anschließend ein Computerprogramm erstellen, um die Computerprogramme zu generieren, die diesen Dienst implementieren."

Dies ist eines der Probleme, die potenzielle Rekruten von ArsDigita ( http://en.wikipedia.org/wiki/ArsDigita ) während der ersten Blase lösen mussten.

Das Buch "SQL für Web-Nerds", auf das Philip im pset verweist, wurde verschoben ( http://philip.greenspun.com/sql/ ).


2

Um das Jahr 2001 herum begann ich mit der Arbeit an einem Projekt, bei dem Geschäftsobjekte und Datenobjekte in großem Umfang genutzt wurden. Ich sollte die Front-End-Website erstellen, hatte aber das Problem, dass die Business-Schicht und die Datenzugriffsschicht nicht vollständig entwickelt waren. Nach ein paar Wochen begann ich mir genau anzuschauen, was diese Schichten taten. Grundsätzlich machten sie Daten, die von gespeicherten Prozeduren zurückgegeben wurden, als Auflistungen von Objekten mit Eigenschaften verfügbar, die den Feldern in den Daten entsprechen, oder nahmen Eingabeparameter und sendeten sie an gespeicherte Prozeduren, um sie in Datenbanktabellen zu speichern. Zwischen den beiden Ebenen fand eine Menge Serialisierung / Deserialisierung statt, es war Microsoft Transaction Server beteiligt, eine IDL / ODL-Typbibliothek ... aber alles passte zu einem Muster.

2 Wochen später ließ ich einen Codegenerator ausarbeiten, der IDL / ODL und auch die Geschäfts- und Datenobjekte löschte. Der Typ, der die Business- und Data-Layer-Objekte erstellt hatte, hatte 2 Jahre gebraucht, um diese Objekte zu debuggen und zu testen. In 2 Wochen hatten wir mit der Codegenerierung die gleiche Ausgabe, aber da alles generiert wurde, war es ziemlich fehlerfrei.

Dieser Codegenerator (untergeordnetes CASE-Tool) hat mich 8 bis 10 Jahre lang durch viele verschiedene Iterationen begleitet, weil das Prinzip so einfach war: Sie tun etwas, das getan werden muss, wenn Sie mit Datenbanken sprechen. Es ist hübsch viel repetitive Codierung, und sobald Sie es richtig gemacht haben, müssen Sie sich nicht mehr darum kümmern.

Also, ja: Verwenden Sie einen Codegenerator, insbesondere wenn sich die Codierung wiederholt und einem genau definierten Muster entspricht.

Ich kenne Leute, die RegX-Makros verwenden, um ähnliche Dinge zu tun, oder Excel-Formeln, um ähnliche Dinge zu tun (das mache ich auch).


2

Ein Beispiel für die Metaprogrammierung

Ich habe eine Ruby-Autorisierungsbibliothek namens Authority . Hier können Entwickler mit Methoden wie current_user.can_read?(@post)und Fragen in ihrer App stellen @post.readable_by?(current_user). Diese Fragen werden von zentralisierten Autorisierungsklassen beantwortet.

Dies ist der entscheidende Teil: Die Behörde weiß nicht, welche Methoden zu definieren sind, bis sie die Konfiguration des Benutzers sieht . Die Benutzerkonfiguration kann enthalten:

config.abilities =  {
  ...
  :read      => 'readable',
  :microwave => 'microwavable',  # user-defined
  ...
}

In diesem Fall muss es eine Methode wie diese geben current_user.can_microwave?(@post).

Metaprogrammierung macht dies möglich: Nach dem Lesen der Konfiguration weiß ich, welche Methoden zu definieren sind :

Authority.verbs.each do |verb|
  class_eval <<-RUBY, __FILE__, __LINE__ + 1 # allows for a nice bracktrace
    def can_#{verb}?(resource)
      resource.#{Authority.abilities[verb]}_by?(self)
    end
  RUBY
end
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.