Template Metaprogrammierung



  • drakon schrieb:

    Denkanstösse sind schwierig. Man muss einfach mal eine Idee haben was man so machen könnte (und das ist sehr vieles; siehe dazu mal "Modern C++ Design" von Andrej Alexandrescu).
    Man kann eigentlich noch recht viele Probleme (oder zumindest Teile davon) auf etwas mappen, dass zur Kompilierungszeit gemacht werden kann und dann kann man da üblicherweise auch etwas mit Metaprogrammierung erreichen.

    Mal eine kleine Idee, die mir während einer Zugfahrt gekommen ist:
    http://www.drakon.ch/index.php?id=&offset=2&mobile=0&show_entry=94

    musste da die römischen Zeichen und Regeln mit dem Telefon nachschlagen.. 😛

    Ist natürlich nur eine Spielerei, aber immerhin nicht das übliche Rekursive Gedöns und nicht so abgedreht wie Typlisten. 😉

    Der Trick ist doch das #define am Anfang. Zählen defines auch zu Metaprogrammierung?



  • hhhhmmmmm schrieb:

    Der Trick ist doch das #define am Anfang. Zählen defines auch zu Metaprogrammierung?

    Das #define erspart nur lästiges Tippen. Man könnte es genauso gut ausschreiben oder in eine Metafunktion packen. Die eigentliche Metaprogrammierung liegt ja woanders.
    Und prinzipiel: #define und Metaprogrammierung können sich mitunter ganz gut ergänzen.



  • ipsec schrieb:

    [...]Das #define erspart nur lästiges Tippen.[...]

    Nichts anderes tut TMP letzten Endes auch. Und ja, Defines zählen zur Metaprogrammierung.


  • Mod

    Tachyon schrieb:

    ipsec schrieb:

    [...]Das #define erspart nur lästiges Tippen.[...]

    Nichts anderes tut TMP letzten Endes auch.

    Nein. Das Templatemetaprogramm wird tatsächlich ausgeführt und etwas berechnet. Das kann man nicht durch Tipparbeit ersetzen, man braucht mindestens noch einen Taschenrechner und ein Gerät welches Entscheidungen treffen kann. Sprich: einen Computer.

    Und ja, Defines zählen zur Metaprogrammierung.

    Ja, ich würde sie trotzdem auch dazu zählen, weil sie in kleinem Umfang etwas erlauben, was der Metaprogrammierung sehr ähnlich ist (so lange man nicht hinter die Kulisse guckt).



  • SeppJ schrieb:

    Nein. Das Templatemetaprogramm wird tatsächlich ausgeführt und etwas berechnet.[...]

    Ein TMP erzeugt Programmcode aus Templatecode zur Compilezeit, welcher dann ausgeführt werden kann. Das tun Makros auch.



  • Anderswo schon mal verlinkt: http://vimeo.com/7211030 . Wenn ihr Macros aus Scheme oder Lisp sehen und verstehen wuerdet ... Naja, was soll's.


  • Mod

    Tachyon schrieb:

    SeppJ schrieb:

    Nein. Das Templatemetaprogramm wird tatsächlich ausgeführt und etwas berechnet.[...]

    Ein TMP erzeugt Programmcode aus Templatecode zur Compilezeit, welcher dann ausgeführt werden kann. Das tun Makros auch.

    Nein! Das ist ein ganz großer Unterschied zwischen einer simplen Textersetzung und einer Turing-vollständigen Programmiersprache.

    Erklär mir mal, wie du ein static assert durch mehr Tippen ausgleichen willst. Oder wie du das Berechnen von Zahlenfolgen zur Compilezeit durch Tippen ausgleichen willst (außer mit einem anderen Programm diese Zahlen zu Berechnen und dann abzutippen).



  • SeppJ schrieb:

    Tachyon schrieb:

    SeppJ schrieb:

    Nein. Das Templatemetaprogramm wird tatsächlich ausgeführt und etwas berechnet.[...]

    Ein TMP erzeugt Programmcode aus Templatecode zur Compilezeit, welcher dann ausgeführt werden kann. Das tun Makros auch.

    Nein! Das ist ein ganz großer Unterschied zwischen einer simplen Textersetzung und einer Turing-vollständigen Programmiersprache.

    Erklär mir mal, wie du ein static assert durch mehr Tippen ausgleichen willst. Oder wie du das Berechnen von Zahlenfolgen zur Compilezeit durch Tippen ausgleichen willst (außer mit einem anderen Programm diese Zahlen zu Berechnen und dann abzutippen).

    Den Static-Assert kann ich z.B. durch tippen bekommen, indem ich an jeder Stelle an der geprüft werden soll ein Array hinbastel, dass im Fehlerfall die Größe -1 bekommt.
    Berechnungen z.B. N! bekommt ich, indem ich den Code bis N von Hand eintippe. (N=5 -> 2 * 3 * 4 * 5 -> mehr Tipparbeit). Der TMP-Code für ! wird ja nicht zur Laufzeit ausgeführt, sondern es wird zur Compilezeit eine Konstante erzeugt.



  • Tachyon schrieb:

    SeppJ schrieb:

    Tachyon schrieb:

    SeppJ schrieb:

    Nein. Das Templatemetaprogramm wird tatsächlich ausgeführt und etwas berechnet.[...]

    Ein TMP erzeugt Programmcode aus Templatecode zur Compilezeit, welcher dann ausgeführt werden kann. Das tun Makros auch.

    Nein! Das ist ein ganz großer Unterschied zwischen einer simplen Textersetzung und einer Turing-vollständigen Programmiersprache.

    Erklär mir mal, wie du ein static assert durch mehr Tippen ausgleichen willst. Oder wie du das Berechnen von Zahlenfolgen zur Compilezeit durch Tippen ausgleichen willst (außer mit einem anderen Programm diese Zahlen zu Berechnen und dann abzutippen).

    Den Static-Assert kann ich z.B. durch tippen bekommen, indem ich an jeder Stelle an der geprüft werden soll ein Array hinbastel, dass im Fehlerfall die Größe -1 bekommt.
    Berechnungen z.B. N! bekommt ich, indem ich den Code bis N von Hand eintippe. (N=5 -> 2 * 3 * 4 * 5 -> mehr Tipparbeit). Der TMP-Code für ! wird ja nicht zur Laufzeit ausgeführt, sondern es wird zur Compilezeit eine Konstante erzeugt.

    Das war gar nicht die Frage. Da C++ auch ohne TMP Turing-vollständig ist, kannst du alle Berechnungen von TMP-Programmen natürlich auch ohne TMP berechnen.



  • Michael E. schrieb:

    Das war gar nicht die Frage. Da C++ auch ohne TMP Turing-vollständig ist, kannst du alle Berechnungen von TMP-Programmen natürlich auch ohne TMP berechnen.

    Aber die Antwort.
    Ein TMP tut nichts anderes, als aus einer Template-Parametrisierung zur Compilezeit! Code/Konstanten/Typen zu generieren. Anstatt ein Template zu nehmen, kann ich auch den Code für alle im Programm benutzen Template-Parameter (die ja zur Compilezeit bereits existieren müssen), von Hand eintippen.



  • Dass man alles, was TMP berechnet, von Hand berechnen kann, ist klar, da wir Menschen auch "Turing-vollständig" sind.

    Wie aber lässt du deinen Compiler ohne TMP die Fakultät ausrechnen? (Explizit, nicht darauf vertrauend, dass er 1*2*3*4*5 auswerten wird.)



  • Diese Argumentation ist Schwachsinn. Du kannst auch alle Anweisungen des Programms von Hand ausfuehren z.B. mit Stift und Zettel, dauert nur etwas ...

    TMP ist mehr als simple Textersetzung. Du gibst ein Code vor (Templates), der ausgefuehrt wird. Ob das zur Compilezeit geschieht, ist dabei voellig irrelevant.



  • Michael E. schrieb:

    Dass man alles, was TMP berechnet, von Hand berechnen kann, ist klar, da wir Menschen auch "Turing-vollständig" sind.

    Wie aber lässt du deinen Compiler ohne TMP die Fakultät ausrechnen? (Explizit, nicht darauf vertrauend, dass er 1*2*3*4*5 auswerten wird.)

    Das TMP tut doch genau das. 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, sondern es gint eine zur Compilezeit beststehende Menge an Folgen und deren Produkten. Entweder habe ich ein TMP, welches für jedes zur Compilezeit gegebene N die Konstante berechnet, oder ich schreibe für jedes benötigte N die die Folge samte Multiplikation von Hand hin. Auch muss man in beiden Fällen muss die Rechenvorschrift kennen.



  • knivil schrieb:

    Diese Argumentation ist Schwachsinn. Du kannst auch alle Anweisungen des Programms von Hand ausfuehren z.B. mit Stift und Zettel, dauert nur etwas ...

    TMP ist mehr als simple Textersetzung. Du gibst ein Code vor (Templates), der ausgefuehrt wird. Ob das zur Compilezeit geschieht, ist dabei voellig irrelevant.

    Guck Dir die Definition für "Metaprogrammierung" an. Zum Beispiel im Tanenbaum, oder meinetwegen auch bei Wikipedia. 🙄


  • Mod

    Tachyon schrieb:

    Wie aber lässt du deinen Compiler ohne TMP die Fakultät ausrechnen? (Explizit, nicht darauf vertrauend, dass er 1*2*3*4*5 auswerten wird.)

    Das TMP tut doch genau das. 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, sondern es gint eine zur Compilezeit beststehende Menge an Folgen und deren Produkten. Entweder habe ich ein TMP, welches für jedes zur Compilezeit gegebene N die Konstante berechnet, oder ich schreibe für jedes benötigte N die die Folge samte Multiplikation von Hand hin. Auch muss man in beiden Fällen muss die Rechenvorschrift kennen.

    Nein, das tut die TMP eben nicht. Durch TMP wird der Compiler explizit gezwungen, diese Konstanten auszurechnen.

    Zeig mir mal wie du mit Arrays und ohne Templates ein static_assert schreibst, welche prüft, ob ein Typ T ein Pointer ist.

    Tachyon schrieb:

    Ein TMP tut nichts anderes, als aus einer Template-Parametrisierung zur Compilezeit! Code/Konstanten/Typen zu generieren. Anstatt ein Template zu nehmen, kann ich auch den Code für alle im Programm benutzen Template-Parameter (die ja zur Compilezeit bereits existieren müssen), von Hand eintippen.

    Du verwechselst TMP mit normalen Templates. Normale Templates sind letztendlich so etwas wie eine typsichere Textersetzung. Der Trick bei der TMP ist aber, dass man den Compiler mittels der Regeln für partielle Spezialisierung dazu bringt, so viel mehr zu tun als nur Text zu ersetzen.



  • TMP ist mehr als Textersetzung, nämlich wenn es um Rekursion geht. Beispiel:

    template<int n>
    struct sum
    {
        int value = n + summe<n-1>::value;
    };
    
    template<>
    struct summe<0>
    {
        int value = 0;
    }
    

    Die Spezialisierung kann man schecht ausschreiben. Das müsste dann etwa so aussehen:

    // summe<2>::value ausgeschrieben:
    // Spezialisierung
    2 == 0 ? 0 : 
    // n +
    2 +
    // summe<3-1>::value
        // Spezialisierung
        2-1 == 0 ? 0 :
        // n +
        (2-1) +
        // summe<(2-1)-1>::value
            // Spezialisierung
            (2-1)-1 == 0 :
            // n +  // !!! Dieser Teil wird nie erreicht, muss aber beim Ausschreiben hingeschrieben werden !!!
            ((2-1)-1) +
            // summe<((2-1)-1)-1>::value
                // usw.
    

    Lange Rede kurzer Sinn: Ausschreiben ermöglicht keinen Rekursionsabbruch. Zur Laufzeit würde es funktionieren, aber man wäre mit dem Schreiben unendlich lange beschäftigt.
    Aus diesem Grund geht auch keine Rekursion mit Makros.
    Reines Ausschreiben (was man ja mit Makros machen kann) ist somit nicht turingvollständig.

    static_assert hat damit aber nichts zu tun, das basiert nicht auf der Turingvollständigkeit. Sämtliche Berechnungen mit Typen kann man nicht ausschreiben, weil Typen keine Ausdrücke sein können (außer in der TMP).



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


Anmelden zum Antworten