17 Stimmen

Wann/Warum (wenn überhaupt) sollte ich über generische Programmierung/Meta-Programmierung nachdenken?

IMHO sind für mich OOPS, Design Patterns sinnvoll und ich habe sie praktisch anwenden können.

Aber wenn es darum geht "Generische Programmierung/Metaprogrammierung" der Art Modern C++, bin ich verwirrt.

-- Ist es ein neues Programmier-/Entwurfsparadigma?

-- Ist sie nur auf die "Bibliotheksentwicklung" beschränkt? Wenn nicht, welche Design-/Codierungssituationen erfordern die Verwendung von Metaprogrammierung/generischer Programmierung.

-- Bedeutet die Verwendung von Vorlagen, dass ich generisch programmiere?

Ich habe viel zu diesem Thema gegoogelt, aber ich verstehe das GROSSE BILD nicht ganz. Siehe auch dies Beitrag .


Nach der Lektüre der Diskussionen hier unter, bis jetzt, bin ich sicher (kann immer noch nicht richtig sein):

a) Generische Programmierung und Metaprogrammierung sind zwei unterschiedliche Konzepte.

30voto

jalf Punkte 235501

Metaprogrammierung ist ein ziemlich exotisches Thema. Es ist interessant, darüber zu lernen, es ist ein mächtiges Werkzeug, und gelegentlich können Sie es nützlich finden. Aber es wird nie das am häufigsten verwendete Werkzeug in Ihrem Werkzeugkasten sein. Manchmal möchten Sie vielleicht, dass Ihr Code auf eine Reihe von nicht verwandten Typen mit unterschiedlichen Eigenschaften wirkt, und hier kommt die Metaprogrammierung ins Spiel. Mit ein paar Tricks können Sie eine Überladung einer Funktion schreiben, die nur verfügbar ist, wenn das Argument vom Typ Integral ist, oder wenn es ein Zeiger ist, oder wenn es entweder vom Typ X, Y oder Z ist (vielleicht ignorieren Sie die Konstante von Z).

Es ist im Wesentlichen eine Programmierung mit Typen. Normalerweise kann Ihr Programm z. B. zwei Zahlen nehmen und eine dritte Zahl erzeugen oder Ihnen sagen, ob eine Zahl eine bestimmte Anforderung erfüllt. Die Metaprogrammierung kann zwei Typen nehmen und einen dritten Typ erzeugen oder Ihnen sagen, ob ein Typ eine bestimmte Anforderung erfüllt. Und ja, sie ist wahrscheinlich vor allem bei der Entwicklung von Bibliotheken nützlich. Aber andererseits, der meiste Code könnte als Bibliothekscode betrachtet werden. Man könnte sagen, dass alles außerhalb Ihrer main()-Funktion Bibliothekscode ist.

Wenn Sie ein Problem durch Metaprogrammierung lösen wollen, werden Sie wahrscheinlich die entsprechenden Boost-Bibliotheken verwenden wollen, um die schwere Arbeit zu erledigen. Boost.TypeTraits und natürlich Boost.Mpl können die Dinge für Sie wirklich vereinfachen. Aber es ist nicht etwas, das Sie brauchen und Sie werden es wahrscheinlich nicht oft brauchen.

Generische Programmierung ist verwandt (und kann in einigen Fällen Metaprogrammierung unter der Haube verwenden, um wirklich generisch zu werden, zum Beispiel verwendet die Standardbibliothek einen Hauch von Metaprogrammierung, um rohe Zeiger in gültige Iteratoren zu verwandeln, was für das "Iterator"-Konzept erforderlich ist, um generisch zu sein), aber nicht ganz dasselbe. Und es ist viel weiter verbreitet.

Jedes Mal, wenn Sie eine Instantiierung einer std::vector verwenden Sie eine generische Programmierung. Jedes Mal, wenn Sie ein Paar von Iteratoren verwenden, um eine Folge von Werten zu verarbeiten, verwenden Sie generische Programmierung. Bei der generischen Programmierung geht es darum, dass Ihr Code so generisch wie möglich sein sollte und unabhängig von den verwendeten Typen funktioniert. Ein std::vector erfordert nicht, dass der enthaltene Typ eine "ICanBeContained"-Schnittstelle implementiert (erinnern Sie sich daran, dass in Java alles von Object abgeleitet sein muss, damit es in einer Containerklasse gespeichert werden kann? Das bedeutet, dass primitive Typen in Boxen untergebracht werden und wir die Typsicherheit verlieren. Das ist nicht generisch, und es ist eine sinnlose Einschränkung).

Der Code zur Iteration über eine Sequenz mit Iteratoren ist generisch und funktioniert mit cualquier Typ von Iteratoren oder sogar mit einfachen Zeigern.

Die generische Programmierung ist sehr nützlich und kann OOP oft weitgehend ersetzen. (siehe das obige Beispiel. Warum sollte ich einen Container schreiben, bei dem die enthaltenen Typen eine Schnittstelle implementieren müssen, wenn ich diese Einschränkung vermeiden kann?)

Wenn Sie in der OOP Schnittstellen verwenden, geht es oft nicht darum, dass sich der Typ während der Laufzeit ändern kann (obwohl das natürlich auch ab und zu vorkommt), sondern darum, dass Sie zur Kompilierzeit einen anderen Typ einfügen können (vielleicht durch Injektion eines Mock-Objekts während der Tests, anstatt die vollwertige Implementierung zu verwenden), oder einfach um zwei Klassen zu entkoppeln. Die generische Programmierung kann dies leisten, ohne dass Sie die mühsame Arbeit der Definition und Pflege der Schnittstelle übernehmen müssen. In diesen Fällen bedeutet die generische Programmierung, dass Sie weniger Code schreiben und pflegen müssen, und Sie erhalten eine bessere Leistung und eine höhere Typsicherheit. Also ja, Sie sollten sich mit generischer Programmierung auf jeden Fall wohlfühlen. C++ ist keine sehr gute OOP-Sprache. Wenn Sie sich strikt an OOP halten wollen, sollten Sie zu Java oder einer anderen, mehr auf OOP fixierten Sprache wechseln. C++ ermöglicht Sie können OO-Code schreiben, aber das ist oft nicht die beste Lösung. Es gibt einen Grund, warum fast die gesamte Standardbibliothek auf generischer Programmierung und nicht auf OOP beruht. Es gibt nur sehr wenig Vererbung oder Polymorphismus in der Standardbibliothek. Man brauchte sie nicht, und der Code wurde ohne sie einfacher zu benutzen und leistungsfähiger.

Und um Ihre anderen Fragen zu beantworten: Ja, die generische Programmierung ist ein ziemlich eigenständiges Paradigma. Die Schablonen-Metaprogrammierung ist es nicht. Es handelt sich um eine ziemlich spezifische Technik zur Manipulation des Typsystems, mit der sich eine kleine Anzahl von Problemen sehr gut lösen lässt. Um als Paradigma zu gelten, müsste es meiner Meinung nach viel allgemeiner nützlich sein, und ein Ansatz, den man grundsätzlich für todo wie funktionale, OO- oder generische Programmierung.

Ich denke, xtofl hat es wirklich auf den Punkt gebracht: Bei der generischen Programmierung geht es darum, den Code typ-unbewusst zu machen. (Ein std::vector hat keine Pflege ou Wissensbedarf welcher Typ darin gespeichert ist. Es funktioniert einfach).

Bei der Metaprogrammierung hingegen geht es um Typberechnungen. Bei den Typen T0 und T1 können wir einen Typ T2 definieren, so wie wir bei den ganzen Zahlen N0 und N1 einen N2 definieren können, der die Summe von N0 und N1 ist.

Die Boost.Mpl-Bibliothek bietet ein offensichtliches Beispiel dafür. Wenn Sie in Ihrem normalen Code die ganzen Zahlen N0, N1 und N2 haben, können Sie einen std::vector erstellen, der diese drei Werte enthält. Ich kann dann einen anderen Algorithmus verwenden, um einen Index zu berechnen, und dann den Wert extrahieren, der an dieser Stelle im Vektor gespeichert ist.

Bei den Typen T0, T1 und T2 können wir einen mpl::Vektor erstellen, der diese drei Typen enthält Typen . Ich kann nun einen anderen Algorithmus verwenden, um einen Index zur Kompilierungszeit zu berechnen und die Typ an dieser Stelle des Vektors gespeichert.

2 Stimmen

Danke für die objektive Stellungnahme, die den Unterschied zwischen Meta- und Gattungsbezeichnungen aufzeigt!

0 Stimmen

Nun, das ist wie I die beiden Begriffe zu verstehen. Andere mögen anderer Meinung sein. (Aber wenn dem so ist, werden wir das sicher in den Kommentaren erfahren ;))

0 Stimmen

Der Beitrag wirft mehrere Punkte auf, die ich googeln/erforschen sollte. Danke für die Antwort.

12voto

xtofl Punkte 39285

Man muss wirklich unterscheiden zwischen generischer Programmierung (die in gewisser Weise typenunbewusst ist) und Metaprogrammierung, die eine völlig legale Möglichkeit ist, Berechnungen innerhalb des Typsystems durchzuführen.

Generische Programmierung ist sehr nützlich, wenn Sie ein verallgemeinerbares Muster in einer Menge von Code finden. Wenn der Unterschied in dem Muster nicht nur in Variablenwerten, sondern auch in verschiedenen Typen liegt, ist die generische Programmierung nützlich, um den Code zu refaktorisieren.

Metaprogrammierung ist in einem völlig anderen Bereich anwendbar. Dieser Bereich ist in der Tat recht neu und muss von Ihnen selbst erforscht werden. Es es auch Spaß!

Ein sehr nützliches und weit verbreitetes Metaprogrammierungsmuster ist das Traits/Policy-Konzept.

0 Stimmen

Oo, gut formuliert. Viel prägnanter als mein Versuch. +1 ;)

10voto

Roddy Punkte 64661

Die C++ Template-Metaprogrammierung ist eine leistungsstarke Technik zur Codeverschleierung, die für eine Reihe von Anwendungen geeignet ist:

  • Wenn Sie einen Code schreiben wollen, den kein anderer in Ihrem Team verstehen kann
  • Wenn Sie einen Code wollen, den Sie 7 Tage, nachdem Sie ihn geschrieben haben, nicht mehr verstehen können
  • Wenn Ihnen die Codeleistung wichtiger ist als die Wartungsfreundlichkeit
  • Wenn Sie "Template Metaprogramming" als Fähigkeit in Ihrem Lebenslauf angeben möchten.
  • Wenn Sie Code schreiben müssen, der wahrscheinlich nicht auf vielen realen Compilern funktionieren wird
  • Wenn Sie lieber Rasierklingen essen als Präprozessormakros verwenden

Ein weiterer Fall:

  • Wenn Sie wissen wollen, wie die Boost-Bibliotheken unter der Haube funktionieren, oder wenn Sie zu ihnen beitragen wollen.

Die Unterscheidung zwischen "generischer Programmierung" (man denke an STL-Container, auto_ptrs usw.), wofür C++-Templates entwickelt wurden, und "Template-Metaprogrammierung" (Verwendung des Template-Systems, um den Compiler dazu zu bringen, Algorithmen für Sie auszuführen) ist wichtig.

Ich bin für die erste Variante, kann aber nur schwerlich einen realen Nutzen aus der zweiten Variante ziehen.

0 Stimmen

Das klingt so perfekt. Ich muss also nicht unbedingt Modern C++ lesen/verstehen. Ich hoffe, jemand kann mich vom Gegenteil überzeugen

0 Stimmen

Wollen Sie damit sagen, dass Makros keine gute Technik zur Codeverschleierung sind? Im Ernst, ich finde Vorlagen großartig, @hotadvice, du solltest sie unbedingt kennenlernen.

2 Stimmen

Dasselbe könnte man auch über OOP sagen. Wenn man es mit Klassenhierarchien übertreibt und alles hinter 8 Schnittstellen versteckt und jede Funktion virtuell ist, erreicht man so ziemlich alles von dem, was oben steht. Wie auch immer, schauen Sie sich das Beispiel von tydok an und sagen Sie mir, dass es lesbarer und wartbarer werden würde, wenn der Argumenttyp ISwappable wäre, anstatt ein Template-Typ T. Natürlich nicht, und genau deshalb ist generische Programmierung so nützlich.

5voto

Alexandru Nedelcu Punkte 7914

Für fortgeschrittene Vorlagen und Techniken empfehle ich: Modernes C++-Design , von Andrei Alexandrescu

C++ ist eine starre Sprache, die praktisch keine Introspektionsmöglichkeiten zur Laufzeit bietet. Viele der Probleme, auf die Sie stoßen werden, können mit Templates gelöst werden. Außerdem ist die Templatesprache turing-komplett, was es unter anderem ermöglicht, komplexe Datenstrukturen zu erzeugen und Werte zur Kompilierzeit vorzuberechnen. Für viele Szenarien kann dies jedoch mehr Aufwand bedeuten, als es wert ist.

4voto

Jacques Carette Punkte 3002

Eine Antwort, die bisher noch nicht gegeben wurde: Obwohl die generische Programmierung und die Meta-Programmierung in der Tat recht unterschiedliche Techniken sind, wurden beide entwickelt, um (im Wesentlichen) das gleiche Problem zu lösen: wie man Boilerplate loswerden kann. Diese sind auch in nicht nur einem, sondern zwei Akronymen von Software-Engineering-Prinzipien verkörpert: DRY (wiederhole dich nicht) und DIE (Duplikation ist böse) . Dies ist eine moderne Umformulierung von Ockhams Rasiermesser d. h., dass "Einheiten nicht über das notwendige Maß hinaus vervielfältigt werden dürfen" ( entia non sunt multiplicanda praeter necessitatem ). (Wer hätte gedacht, dass Logiker des 14. Jahrhunderts etwas Nützliches für die Softwareentwicklung im 21.]

Wie auch immer, der Sinn beider Techniken ist es, Wege zu finden, um Codes, die "fast gleich" sind, in einem einzigen Stück parametrisierten Code zu vereinheitlichen. Im Fall der Meta-Programmierung verwenden Sie die Code-Generierung, so dass die Technik Folgendes beinhaltet Spezialisierung ein allgemeines Stück Code (auch als Vorlage bezeichnet, oder allgemeiner eine codeerzeugende Funktion) für einen bestimmten Fall. Bei der generischen Programmierung versucht man, das Typsystem zu nutzen, um zu erkennen, dass verschiedene Codestücke "gleich" sind, und verwendet dann typbasierte Abfertigung für die Spezialisierung.

Es stellt sich heraus, dass diese beiden Techniken sogar zusammen verwendet werden können, um ziemlich ausgefallene Dinge zu tun. Siehe Mehrstufige Programmierung mit Funktoren und Monaden: Beseitigung des Abstraktions-Overheads von generischem Code für ein entsprechendes Beispiel. Aber wenn Sie denken, dass die STL beängstigend ist, schauen Sie besser nicht darauf...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X