Template Metaprogrammierung
-
knivil schrieb:
Nein, Makros im Sinne von C sind da wesentlich eingeschraenkter. C++ Templates und Makros auf eine Stufe zu stellen ist falsch.
Ich würde mal sagen das kommt drauf an wie viele Stufen man hat

-
Oder Treppen ...
-
Fast2_unreg schrieb:
Zu SeppJ:
Wieso war denn die Laufzeitlösung schneller? Was könnte denn – wenn man die Laufzeit betrachtet – effizienter sein, als alles schon zuvor berechnen zu lassen?Vorher: Statische Polymorphie, zig automatisch generierte Funktionen.
Nachher: Schicke Laufzeitpolymorphie, wenige kleine, übersichtliche Funktionen.Also anstatt generiertem Code der so aussah:
void do_something_foo(foo1); void do_something_foo(foo2); void do_something_foo(foo3); void do_something_foo(foo4); // ... Foo1 foo1[count_of_foo1]; Foo1 foo2[count_of_foo2]; Foo1 foo3[count_of_foo3]; Foo1 foo4[count_of_foo4]; // ... for (int i=0; i<count_of_foo1; ++i) do_something(foo1[i]); for (int i=0; i<count_of_foo2; ++i) do_something(foo2[i]); for (int i=0; i<count_of_foo3; ++i) do_something(foo3[i]); for (int i=0; i<count_of_foo4; ++i) do_something(foo4[i]);hatte ich hinterher etwas wie
void do_something(foo*); // Eigentlich mit Referenzen, aber ich will die Laufzeitpolymorphie hervorheben // ... Foo *foo[count_of_foos]; // ... for (int i=0; i<count_of_foos; ++i) do_something(foo[i]);Das ist schon einmal wesentlich weniger Overhead durch die Schleifen, dadurch natürlich mehr Overhead durch Polymorphie. Bei mir war das aber wesentlich günstiger zugunsten der Polymorphie.
Außerdem konnte der Compiler den handgeschriebenen Code wesentlich besser inlinen. Vermutlich, weil eben nur eine Funktion an genau einer Stelle eingefügt werden musste, anstatt vier leicht verschiedene Funktionen an vier Stellen die das Ergebnis sehr aufgebläht hätten. Weil mein Code aus vielen kleinen spezialisierten Funktionen bestand (wie man es ja bei gutem Stil macht
) haute auch das ganz gewaltig rein.Ja, ich war ehrlich gesagt auch etwas überrascht. Die Regel, dass sauberer Code schnellerer Code ist, stimmt wirklich.
-
Ach so, ich habe mich auf
SeppJ schrieb:
beispielsweise Zeichenketten zur Compilezeit erzeugen
beim Lesen des Rests gestützt, und gedacht, du hättest zuvor die fertigen Zeichenketten mit dem PP erzeugt.
-
Fast2_unreg schrieb:
Ach so, ich habe mich auf
SeppJ schrieb:
beispielsweise Zeichenketten zur Compilezeit erzeugen
beim Lesen des Rests gestützt, und gedacht, du hättest zuvor die fertigen Zeichenketten mit dem PP erzeugt.
Nein, das war nur der Grund, den Präproszessor zu benutzen, weil das mit TMP so gut wie unmöglich ist (ja, natürlich ist es irgendwie möglich, aber viel Spaß dabei). Und da ich dann schon ein Präprozessorframework hatte, lag die Idee nahe, das auch für den Rest zu benutzen, anstatt auch noch TMP reinzubringen. Zumal TMP oftmals noch schlechter lesbar ist als Präprozessorcodegenerierung und vor allem schwerer zu debugggen. Das Programm sollten ja auch andere Leute noch lesen können.
Die Quintessenz dürfte aber trotzdem die gleiche sein, denn der Code der durch Templates erzeugt worden wäre, wäre vom Verhalten her nicht anders gewesen als der durch den Präprozessor erzeugte. Also mehrere spezialisierte Funktionen und mehrere Schleifen für verschiedene abgeleitete Klassen.
-
SeppJ schrieb:
und vor allem schwerer zu debugggen
Schwerer zu debuggen als "garnicht debuggbar"? Oder andersrum gefragt: was kann man bei makros überhaupt debuggen?
-
pumuckl schrieb:
SeppJ schrieb:
und vor allem schwerer zu debugggen
Schwerer zu debuggen als "garnicht debuggbar"? Oder andersrum gefragt: was kann man bei makros überhaupt debuggen?
Präprozessorausgabe angucken. Dann sieht man genau an welcher Stelle es hakt und welche Makros vorher wie extrahiert wurden. Und wenn man die Stelle hat, ist oftmals auch das warum klar. Bei TMP habe ich das Problem, dass die Fehlermeldungen 1. extrem kryptisch sind und 2. Hat die Stelle des Fehlers meistens nichts mit der Ursache zu tun.
Also ich empfinde das Debuggingverhalten von Makros jedenfalls nicht als einen Nachteil.
-
Ich habe mir den ganzen Flamewar nicht durchgelesen, daher bitte ignorieren, falls der Link schon gepostet wurde:
Artikel bei heise developer über eine "bodenständige" Anwendung von TMP: boost::variant mit QVariant koppeln: http://www.heise.de/developer/artikel/Ueber-den-praxisrelevanten-Einsatz-der-Template-Metaprogrammierung-992944.html
-
SeppJ schrieb:
Bei TMP habe ich das Problem, dass die Fehlermeldungen 1. extrem kryptisch sind und 2. Hat die Stelle des Fehlers meistens nichts mit der Ursache zu tun.
Die Ursache ist meistens die selbe: fehlendes frühzeitiges Abfangen von Fehlern, sprich: static_assert. Zugegeben, die Möglichkeiten dazu sind in C++03 noch nicht so umwerfend, aber dennoch vorhanden.
Es gibt allerdings auch Tools, die solche Compilermeldungen lesbarer gestalten und dann hat man im grunde nichts anderes als beim PP-Output auch: man sieht welches Template wie instanziiert wurde und kann sich entsprechend einfach die Fehler suchen.
Ich persönlich lese allerdings lieber die kryptischen Compilermeldungen (durchaus auch im Klartext) als in einem auf eine 12kb-Zeile expandierten Makro nach dem Fehler zu suchen - denn wenns ans Eingemachte geht wird das bei den Makros schnell zu so einem Monstrum, und leider meckert der Compiler dann nur "irgendwo in dieser Megazeile steckt der Wurm"...
-
pumuckl schrieb:
SeppJ schrieb:
Bei TMP habe ich das Problem, dass die Fehlermeldungen 1. extrem kryptisch sind und 2. Hat die Stelle des Fehlers meistens nichts mit der Ursache zu tun.
Die Ursache ist meistens die selbe: fehlendes frühzeitiges Abfangen von Fehlern, sprich: static_assert. Zugegeben, die Möglichkeiten dazu sind in C++03 noch nicht so umwerfend, aber dennoch vorhanden.
Es gibt allerdings auch Tools, die solche Compilermeldungen lesbarer gestalten und dann hat man im grunde nichts anderes als beim PP-Output auch: man sieht welches Template wie instanziiert wurde und kann sich entsprechend einfach die Fehler suchen.
Ich persönlich lese allerdings lieber die kryptischen Compilermeldungen (durchaus auch im Klartext) als in einem auf eine 12kb-Zeile expandierten Makro nach dem Fehler zu suchen - denn wenns ans Eingemachte geht wird das bei den Makros schnell zu so einem Monstrum, und leider meckert der Compiler dann nur "irgendwo in dieser Megazeile steckt der Wurm"...Ahh, ich vergaß: Vorher wird durch den Prettyprinter gepiped. Ist natürlich für herkömmliche IDEs sicherlich etwas umständlich, aber bei meiner Vorliebe klassische Texteditoren, Kommandozeilen und Makefiles ist mir das kaum aufgefallen.
-
knivil schrieb:
für jedes N wird eine Folge von Konstanten 2..N und das Produkt der Elemente gebildet. N ist kein Parameter der zur Laufzeit änderbar ist
Du sagst doch selbst: gebildet. Also ausgerechnet. Und natuerlich zur Compilezeit, da das Programm zur Compilezeit ausgewertet wird und nicht zur Laufzeit.
Ein TMP erzeugt Programmcode aus Templatecode zur Compilezeit, welcher dann ausgeführt werden kann. Das tun Makros auch.
Nein, Makros im Sinne von C sind da wesentlich eingeschraenkter. C++ Templates und Makros auf eine Stufe zu stellen ist falsch.
Guck Dir die Definition für "Metaprogrammierung" an. Zum Beispiel im Tanenbaum, oder meinetwegen auch bei Wikipedia.
Dito. Dir wird von vielen gesagt, dass du falsch liegst. Vielleicht haben sie ja recht. Auch haette ich gern noch gewusst, in welchem Buch von Tanenbaum etwas ueber Metaprogrammierung zu finden ist.
Ich habe doch gar nicht gesagt, das TMP nur Textersetzung macht. Ich habe gesagt, dass es Tiparbeit erspart.
Doch du hast TMP auf die Stufe von C-Makros gesetzt. Dein Hauptargument ist dabei die Tipparbeit, alles andere scheint nur eine untergeordnete Rolle zu spielen. Dabei gibt es soviel mehr Moeglichkeiten, dass Tipparbeit eher eine untergeordnete Rolle spielt.
Ich finde es bemerkenswert, dass Du denkst, es würde für das ausgeführte Programm einen Unterschied machen, ob ein konstanter Wert durch
Fak<N>oder durchFak_Nentsteht. Der Unterschied entsteht für den Programmierer, denn er muss für jedes im ausgeführten Programm benötigte N eine Konstante bereitsstellen, was im Falle vonFak<N>der Compiler erledigt.
Notwendigkeiten für Dinge wiestatic_assert, Typprüfungen oder Rekursionsabbrüche entstehen überhaupt erst durch die TMP und der Tatsache, dass Laufzeitcode parametrisch erzeugt werden kann.
Ein gutes Beispiel:
Es gibt einen teils auf TMP basierten FFT (Radix2) Algorithmus, welcher für eine FFT-Länge N benötigten Pfade des Danielson-Lanczos-Anteils der FFT durch TMP-Rekursion erzeugt, also quasi ein Loop-Unrolling betreibt. Genauso könnte man für jedes N Code bereitsstellen, wo das Unrolling von Hand gemacht wurde (in C wird das auf einigen Plattformen sogar so gemacht). Anstatt FFT<N> hätte man dann also FFT_N. Aus Laufzeitsicht ist das schlussendlich kein Unterschied, nur der Schreibaufwand (und auch die Möglichkeit Fehler zu machen) steigt für viele N dramatisch an.
-
Ich finde es bemerkenswert, dass Du denkst, es würde für das ausgeführte Programm einen Unterschied machen, ob ein konstanter Wert durch Fak<N> oder durch Fak_N entsteht
Nein das denke ich nicht. Ehrlich ... langsam denke ich du hast ein Brett vor dem Kopf. Dem ausgefuehrten ist so ziemlich alles egal, ob die Eingabe als Parameter erfolgt, als Konstante im Quelltext steht oder durch TMP zur Compilezeit berechnet wird. TMP dient dem Programmierer, Arbeitsaufwand und Fehleranfaeligkeit zu reduzieren. Genau wie ein Word-Programm einer Sekretaerin hilft, Briefe auf Rechtschreibung zu ueberpruefen. Fuer den Brief macht es keinen Unterschied, wohl aber fuer die Person, die ihn schreibt oder liest.
Notwendigkeiten für Dinge wie static_assert, Typprüfungen oder Rekursionsabbrüche entstehen überhaupt erst durch die TMP und der Tatsache, dass Laufzeitcode parametrisch erzeugt werden kann.
Die Notwendigkeit, Fehler zu reduzieren und Arbeit bei der Programmierung und Wartung einzusparen, entsteht nicht durch TMP. TMP ist nur ein Weg, diesen Problemen zu begegnen.
Aus Laufzeitsicht ist das schlussendlich kein Unterschied, nur der Schreibaufwand (und auch die Möglichkeit Fehler zu machen) steigt für viele N dramatisch an.
Tja, wie du selbst sagst, macht es aus Laufzeitsicht keinen Unterschied. Warum? Weil Templates zur Compilerzeit ausgefuehrt werden ... Es ist foellig witzlos bei Templates aus Sicht der Laufzeit zu argumentieren, da sie zu diesem Zeitpunkt ihre Arbeit laengst abgeschlossen haben und im Programm nicht mehr existent sind.
-
knivil schrieb:
[...]
Ich sehe, wir sind uns einig
. Dann habe ich wohl doch kein Brett vor dem Kopf.