Ich weiß, dass sie in C / C ++ extrem unsicher implementiert sind. Können sie nicht sicherer umgesetzt werden? Sind die Nachteile von Makros wirklich schlimm genug, um die enorme Leistung, die sie bieten, aufzuwiegen?
programming-languages
macros
Casebash
quelle
quelle
WithEvents
Modifikator von Visual Basic entspricht ? Sie brauchen so etwas wie semantische Makros.Antworten:
Ich denke, der Hauptgrund ist, dass Makros lexikalisch sind . Dies hat mehrere Konsequenzen:
Der Compiler kann nicht überprüfen, ob ein Makro semantisch geschlossen ist, dh, dass es wie eine Funktion eine „Bedeutungseinheit“ darstellt. (Überlegen Sie
#define TWO 1+1
- was machtTWO*TWO
gleich? 3.)Makros werden nicht wie Funktionen getippt. Der Compiler kann nicht überprüfen, ob die Parameter und der Rückgabetyp sinnvoll sind. Es kann nur den erweiterten Ausdruck überprüfen, der das Makro verwendet.
Wenn der Code nicht kompiliert wird, kann der Compiler nicht wissen, ob der Fehler im Makro selbst oder an der Stelle vorliegt, an der das Makro verwendet wird. Der Compiler meldet entweder die Hälfte der Zeit die falsche Stelle oder er muss beide melden, obwohl eine davon wahrscheinlich in Ordnung ist. (Überlegen Sie
#define min(x,y) (((x)<(y))?(x):(y))
: Was sollte der Compiler tun, wenn die Typen vonx
undy
nicht übereinstimmen oder nicht implementiert werdenoperator<
?)Automatisierte Tools können nicht mit ihnen auf semantisch nützliche Weise arbeiten. Insbesondere können Sie keine Funktionen wie IntelliSense für Makros verwenden, die wie Funktionen funktionieren, jedoch zu einem Ausdruck erweitert werden. (Wieder das
min
Beispiel.)Die Nebenwirkungen eines Makros sind nicht so deutlich wie bei Funktionen, was für den Programmierer zu Verwirrung führen kann. (Betrachten Sie noch einmal das
min
Beispiel: In einem Funktionsaufruf wissen Sie, dass der Ausdruck fürx
nur einmal ausgewertet wird, aber hier können Sie es nicht wissen, ohne das Makro zu betrachten.)Wie gesagt, das sind alles Konsequenzen der Tatsache, dass Makros lexikalisch sind. Wenn Sie versuchen, sie in etwas Richtigeres zu verwandeln, erhalten Sie Funktionen und Konstanten.
quelle
Aber ja, Makros können besser entworfen und implementiert werden als in C / C ++.
Das Problem bei Makros besteht darin, dass es sich tatsächlich um einen Mechanismus zur Erweiterung der Sprachsyntax handelt , der Ihren Code in etwas anderes umschreibt.
Im C / C ++ - Fall gibt es keine grundlegende Überprüfung der Integrität. Wenn Sie vorsichtig sind, sind die Dinge in Ordnung. Wenn Sie einen Fehler machen oder Makros überbeanspruchen, können Sie große Probleme bekommen.
Hinzu kommt, dass viele einfache Dinge, die Sie mit Makros im C / C ++ - Stil tun können, auf andere Weise in anderen Sprachen ausgeführt werden können.
In anderen Sprachen, wie z. B. verschiedenen Lisp-Dialekten, sind Makros besser in die Hauptsprachensyntax integriert, es können jedoch weiterhin Probleme mit Deklarationen in einem Makro auftreten, die "undicht" sind. Dies wird durch hygienische Makros behoben .
Kurzer historischer Kontext
Makros (Abkürzung für Macro-Instructions) tauchten erstmals im Kontext der Assemblersprache auf. Laut Wikipedia waren in den 1950er Jahren Makros in einigen IBM-Assemblern verfügbar.
Die ursprüngliche LISP hatte keine Makros, aber sie wurden erstmals Mitte der 1960er Jahre in MacLisp eingeführt: https://stackoverflow.com/questions/3065606/when-did-the-idea-of-macros-user-defined-code -Transformation-erscheinen . http://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf . Zuvor bot "fexprs" eine makrofähige Funktionalität.
Die frühesten Versionen von C hatten keine Makros ( http://cm.bell-labs.com/cm/cs/who/dmr/chist.html ). Diese wurden um 1972-73 über einen Präprozessor hinzugefügt. Vorher unterstützte C nur
#include
und#define
.Der M4-Makro-Präprozessor stammt aus dem Jahr 1977.
Neuere Sprachen implementieren anscheinend Makros, bei denen das Operationsmodell syntaktisch und nicht textuell ist.
Wenn also jemand über den Vorrang einer bestimmten Definition des Begriffs "Makro" spricht, ist zu beachten, dass sich die Bedeutung im Laufe der Zeit weiterentwickelt hat.
quelle
Makros können, wie Scott bemerkt , das Ausblenden von Logik ermöglichen. Dies gilt natürlich auch für Funktionen, Klassen, Bibliotheken und viele andere gängige Geräte.
Ein leistungsfähiges Makrosystem kann jedoch noch weiter gehen und es Ihnen ermöglichen, Syntax und Strukturen zu entwerfen und zu verwenden, die in der Sprache normalerweise nicht zu finden sind. Dies kann in der Tat ein wunderbares Werkzeug sein: domänenspezifische Sprachen, Codegeneratoren und vieles mehr - alles in einer einzigen Sprachumgebung ...
Es kann jedoch missbraucht werden. Es kann das Lesen, Verstehen und Debuggen von Code erschweren, die Zeit verlängern, die neue Programmierer benötigen, um sich mit einer Codebasis vertraut zu machen, und zu kostspieligen Fehlern und Verzögerungen führen.
Für Sprachen, die die Programmierung vereinfachen sollen (wie Java oder Python), ist ein solches System ein Anathema.
quelle
Makros können unter bestimmten Umständen sehr sicher implementiert werden - in Lisp beispielsweise sind Makros nur Funktionen, die transformierten Code als Datenstruktur (s-Ausdruck) zurückgeben. Natürlich profitiert Lisp erheblich von der Tatsache, dass es homoikonisch ist und dass "Code Daten sind".
Ein Beispiel dafür, wie einfach Makros sein können, ist dieses Clojure-Beispiel, das einen Standardwert angibt, der im Falle einer Ausnahme verwendet werden soll:
Aber auch in Lisps lautet der allgemeine Rat: "Verwenden Sie keine Makros, es sei denn, Sie müssen".
Wenn Sie keine homoikonische Sprache verwenden, werden Makros viel kniffliger, und die verschiedenen anderen Optionen weisen einige Probleme auf:
Darüber hinaus kann alles, was ein Makro kann, letztendlich auf eine andere Art und Weise in einer vollständigen Sprache erreicht werden (auch wenn dies das Schreiben einer großen Menge von Boilerplate bedeutet). Angesichts all dieser Schwierigkeiten ist es nicht verwunderlich, dass viele Sprachen der Meinung sind, dass die Implementierung von Makros nicht wirklich die Mühe wert ist.
quelle
Überlegen Sie sich zur Beantwortung Ihrer Fragen, wofür Makros überwiegend verwendet werden (Warnung: Gehirn-kompilierter Code).
#define X 100
Dies kann leicht ersetzt werden durch:
const int X = 100;
#define max(X,Y) (X>Y?X:Y)
In jeder Sprache, die das Überladen von Funktionen unterstützt, kann dies durch Überladen von Funktionen des richtigen Typs oder in einer Sprache, die Generika unterstützt, durch eine generische Funktion wesentlich typsicherer emuliert werden. Das Makro wird gerne versuchen, alles zu vergleichen , einschließlich Zeiger oder Zeichenfolgen, die möglicherweise kompiliert werden, aber mit ziemlicher Sicherheit nicht das sind, was Sie wollten. Auf der anderen Seite bieten Makros, die Sie typsicher gemacht haben, keine Vorteile oder Bequemlichkeiten gegenüber überladenen Funktionen.
#define p printf
Dies kann leicht durch eine Funktion ersetzt werden
p()
, die dasselbe tut. Dies ist mit C verbunden (Sie müssen dieva_arg()
Funktionsfamilie verwenden), in vielen anderen Sprachen, die eine variable Anzahl von Funktionsargumenten unterstützen, ist dies jedoch viel einfacher.Die Unterstützung dieser Funktionen in einer Sprache anstelle einer speziellen Makrosprache ist einfacher, weniger fehleranfällig und für andere, die den Code lesen, weitaus weniger verwirrend. Tatsächlich fällt mir kein einziger Anwendungsfall für Makros ein, der nicht einfach auf andere Weise dupliziert werden kann. Der einzige Ort, an dem Makros wirklich nützlich sind, ist, wenn sie an bedingte Kompilierungskonstrukte wie
#if
(usw.) gebunden sind .In diesem Punkt werde ich nicht mit Ihnen streiten, da ich glaube, dass Nicht-Präprozessor-Lösungen für die bedingte Kompilierung in gängigen Sprachen äußerst umständlich sind (wie die Bytecode-Injektion in Java). Sprachen wie D haben jedoch Lösungen entwickelt, die keinen Präprozessor erfordern und nicht umständlicher sind als die Verwendung von Präprozessor-Bedingungen, während sie weitaus weniger fehleranfällig sind.
quelle
In fact, I can't think of a single use-case for macros that can't easily be duplicated in another way
: Zumindest in C können Sie mithilfe der Token-Verkettung keine Bezeichner (Variablennamen) bilden, ohne Makros zu verwenden.enum
, die Bedingungen mithilfe von und ein konstantes Zeichenfolgenarray zum Definieren der Nachrichten zu definieren, zu Problemen führen, wenn enum und das Array nicht mehr synchron. Wenn Sie ein Makro verwenden, um alle Paare (Aufzählung, Zeichenfolge) zu definieren, und dieses Makro dann zweimal mit anderen geeigneten Definitionen im Gültigkeitsbereich einschließen, wird jeder Aufzählungswert neben die Zeichenfolge gesetzt.Das größte Problem, das ich bei Makros gesehen habe, ist, dass sie bei starker Verwendung das Lesen und Verwalten von Code sehr erschweren können, da Sie mit ihnen Logik im Makro verbergen können, die möglicherweise leicht zu finden ist (und möglicherweise trivial ist oder auch nicht ).
quelle
Beginnen Sie mit der Feststellung, dass MAKROs in C / C ++ sehr begrenzt, fehleranfällig und nicht wirklich nützlich sind.
MAKROs, wie sie beispielsweise in LISP oder z / OS-Assemblersprache implementiert sind, können zuverlässig und unglaublich nützlich sein.
Aber wegen des Missbrauchs der eingeschränkten Funktionalität in C haben sie sich einen schlechten Ruf erworben. Daher implementiert niemand mehr Makros. Stattdessen erhalten Sie Templates, die einige der einfachen Stuff-Makros ausführen, und Java-Annotationen, die einige der komplexeren Stuff-Makros ausführen.
quelle