Template Metaprogrammierung



  • Ich habe doch gar nicht gesagt, das TMP nur Textersetzung macht. Ich habe gesagt, dass es Tiparbeit erspart.

    Zum Beispiel Pointerprüfung:
    Du schreibst einen Code, der im Fall T == Pointer ein Stück Code compiliert, und im Fall T != Pointer einen anderen. Das kann ich auch mit Überladung erreichen. Man muss bloß viel mehr Aufwand betreiben.

    Was Du natürlich nicht machen kannst, ist, dass ein Template im Fall T == Pointer nicht kompiliert. Das ist aber ein Problem, dass überhaupt erst durch Templateprogrammierung entsteht.



  • ipsec schrieb:

    Lange Rede kurzer Sinn: Ausschreiben ermöglicht keinen Rekursionsabbruch.

    Die Notwendigkeit für einen automatisierten Rekursionsabbruch ergibt sich überhaupt erst aus der TMP.



  • Also Freunde.
    Rekursion und ein bisschen Rechnen geht mit dem PP auch. Wenn auch leider nur sehr umständlich.



  • ... und mit beschränkter Rekursionstiefe. Also wie als würde ich in meinem Summebeispiel willkürlich irgendwann aufhören.

    Naja die Diskussion ist auch etwas akademisch. Auf jeden Fall ist TMP etwas mehr als Codegenerierung auf Basis von stupider Textersetzung, man kann die Templates in der Regel zwar ausschreiben (summe<3>::value == 3+2+1+0), muss dazu aber selber etwas rechnen (z.B. "berechnen", wann der Rekursionsabbruch erreicht ist).



  • hustbaer schrieb:

    ein bisschen Rechnen geht mit dem PP auch.

    Wie?



  • Sowas wie boost::proto als Mittel der TMP ist doch schon sehr praktisch:
    http://cpp-next.com/archive/2011/01/expressive-c-expression-optimization/


  • Mod

    Michael E. schrieb:

    hustbaer schrieb:

    ein bisschen Rechnen geht mit dem PP auch.

    Wie?

    Das ist das was ich oben meinte mit "wenn man nicht hinter die Kulissen guckt".

    Man kann z.B. so etwas machen:

    #define result_3_4 7
    
    #define add(a,b) result_ ## a ## _ ## b
    
    add(3,4)   // ist 7 zur Compilezeit (sogar schon davor!)
    

    Wenn das jetzt ein fleißiger Schreiberling für alle Zahlen bis 100 und für das kleine Einmaleins macht und das alles in einer Headerdatei versteckt, könnte man den Eindruck bekommen, dass man mit dem Präprozessor richtig rechnen kann.



  • Ich sehs kommen, der nächste Trend beginnt: Präprozessor Meta Programming... Für die Geeks unter den Geeks denen TMP zu trivial ist. 😃



  • Für die Fakultät ist das sogar noch viel besser. Das toppt dann auch die Template-Lösung! Und es gibt keine Einschränkung in der Rekursionstiefe :p


  • Mod

    HighLigerBiMBam schrieb:

    Ich sehs kommen, der nächste Trend beginnt: Präprozessor Meta Programming... Für die Geeks unter den Geeks denen TMP zu trivial ist. 😃

    Alles schon gemacht. Sogar in einem produktivem Programm. Denn mit TMP kann man manche Dinge nicht (oder nurschlecht) machen die mit Präprozessor trivial sind, beispielsweise Zeichenketten zur Compilezeit erzeugen ist mit TMP die Hölle bis unmöglich.

    Das Programm war dann am Ende natürlich so unwartbar und unleserlich, dass ich dann später doch alles auf eine Laufzeitlösung umgeschrieben habe (trotz boost::preprocessor welches schon viel zur Lesbarkeit beiträgt). Weil das resultierende Programm weitaus leserlicher war, war es dann auch ein leichtes zu optimieren und dadurch war das Endergebnis wesentlich (ca. 30%) schneller als das Metaprogrammierungsmonster.

    In dieser Anekdote steckt bestimmt irgendeine Moral, aber ich finde sie nicht :p .



  • 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.



  • 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?



  • Generierter Code war eben suboptimal. Metaprogrammierung bedeutet nicht, das alles zur Compilezeit ausgerechnet wird.



  • Der Vergleich von Makros und TMP ist wie der Vergleich von Äpfeln und Birnen. Oder von Perl und C++.
    Klar, beide ersparen Tipparbeit. Aber Tipparbeit spare ich auch, indem ich eine höhere Programmiersprache benutze und nicht alles in Assembler schreibe. Darüber hinaus sind beide aber so unterschiedlich wie Tag und Nacht. Es gibt Dinge, die man bei makros machen kann und in TMP nicht, es gibt Dinge die man in beidem machen kann, wobei je nach Problemstellung das eine oder das andere geeigneter ist. Beides in einen Topf zu werfen, nur weil beide Tipparbeit sparen ist aber ausgemachter Blödsinn.



  • 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 ...


  • Mod

    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.


  • Mod

    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?


Anmelden zum Antworten