Was kosten Templates?



  • Ich denke, Berechnungen über Template-Metaprogrammierung werden – falls sie überhaupt zum Einsatz kommen – eher ausgeführt, um einen Wert schon zur Compilezeit zu haben. Gewisse C++-Sprachmittel benötigen konstante Ausdrücke. Indirekt kann vielleicht dadurch eine Optimierung zustandekommen (z.B. indem man statische Arrays statt dynamischen vewendet, oder abhängig von einem Resultat einen anderen Ablaufpfad wählt).

    Den anderen Fall, wo Compilezeit-Berechnungen direkt benötigt werden, habe ich bisher kaum angetroffen. Man ist auch etwas eingeschränkt, weil z.B. keine Fliesskommazahlen verwendet werden können.

    Viel häufiger sieht man die Template-Metaprogrammierung aber im Zusammenhang mit statischem Dispatchen von Typen (Traits, Static Assertions, Policies, ...).



  • -.-



  • Templates sind gerade für 1,50 Euro im Angebot...

    und 4 für 5 Euro...



  • hier ist mein beispiel
    ist aus der 6. auflage von "C++ - einführung und professionelle programmierung" von ulrich breymann (S. 260)

    #include <iostream>
    
    template<int p, int i>
    struct istPrimzahl {
        enum {prim=(p%i) && istPrimzahl<(i>2?p:0), i-1>::prim};
    };
    
    template<int i>
    struct druckePrimzahlenBis {
        druckePrimzahlenBis<i-1> a;
        enum { prime = istPrimzahl<i, i-1>::prim };
    
        druckePrimzahlenBis() {
            if ( prime )
                std::cout << i << std::endl;
        }
    };
    
    struct istPrimzahl<0, 0> { enum {prim=1};};
    struct istPrimzahl<0, 1> { enum {prim=1};};
    struct druckePrimzahlenBis<2> { enum{prim=1}; };
    
    int main() {
        druckePrimzahlenBis< __17__ > a;
        return 0;
    }
    

    Dieses Programm wurde 1994 nach einer Idee von Erwin Unruh geschrieben, der ein programmm konstruierte, das beid er Übersetzung Primzahlen in den Fehlermeldungen des Compilers erzeugte.



  • Abgesehen, das der Code fehler hat, kommt man auch nicht weit als über IsPrim(500) zu berechnen 😞

    #include <iostream> 
    
    template<int p, int i> 
    struct istPrimzahl { 
        enum {prim=(p%i) && istPrimzahl<(i>2?p:0), i-1>::prim}; 
    }; 
    
    template<int i> 
    struct druckePrimzahlenBis { 
        druckePrimzahlenBis<i-1> a; 
        enum { prime = istPrimzahl<i, i-1>::prim }; 
    
        druckePrimzahlenBis() { 
            if ( prime ) 
                std::cout << i << std::endl; 
        } 
    }; 
    
    template<> 
    struct istPrimzahl<0, 0> { enum {prim=1};};
    
    template<> 
    struct istPrimzahl<0, 1> { enum {prim=1};}; 
    
    template<> 
    struct druckePrimzahlenBis<2> { enum{prim=1}; }; 
    
    int main() { 
        druckePrimzahlenBis< 501 > a; 
        return 0; 
    }
    

    Error 1 error C1202: recursive type or function dependency context too complex e:\workspace\cxx01\cxx01\main.cpp 5 1 cxx01


  • Mod

    Danke. Sowas meinte ich. Eine nichttriviale Aufgabe die während der Compilezeit gelöst werden kann. 👍



  • ich weis dass der code nicht klappt
    hat er bei mir auch nicht

    aber ich hab ihn wortwörtlich abgeschrieben aus dem buch vorhin, genauso wie die 3 zeilen danach

    aber das prinzip sollte klar sein dabei denke ich
    und die cracks hier können den auch so ohne probleme von syntaktischen rerrors befreien



  • SeppJ schrieb:

    Danke. Sowas meinte ich. Eine nichttriviale Aufgabe die während der Compilezeit gelöst werden kann. 👍

    TMP ist turingvollständig, also kann man alle Algorithmen damit zur Compilezeit lösen. Aber sämtliche Beispiele dafür sind prinzipbedingt akademischer Humbug, denn ein zur Compilezeit feststehendes Ergebnis kann ich genauso gut hardcodieren.



  • ipsec schrieb:

    Aber sämtliche Beispiele dafür sind prinzipbedingt akademischer Humbug, denn ein zur Compilezeit feststehendes Ergebnis kann ich genauso gut hardcodieren.

    Es sei denn das Ergebnis ist nicht eine Zahl sondern ein Typ.
    Dann wirds naemlich interessant.



  • Shade Of Mine schrieb:

    ipsec schrieb:

    Aber sämtliche Beispiele dafür sind prinzipbedingt akademischer Humbug, denn ein zur Compilezeit feststehendes Ergebnis kann ich genauso gut hardcodieren.

    Es sei denn das Ergebnis ist nicht eine Zahl sondern ein Typ.
    Dann wirds naemlich interessant.

    Ich denke, dann kommt der Horror 😛

    #define TYPELIST_1(T1) typelist<T1, null_typelist>
    #define TYPELIST_2(T1, T2) typelist<T1, TYPELIST_1(T2) >
    #define TYPELIST_3(T1, T2, T3) typelist<T1, TYPELIST_2(T2, T3) >
    #define TYPELIST_4(T1, T2, T3, T4) typelist<T1, TYPELIST_3(T2, T3, T4) >
    


  • Shade Of Mine schrieb:

    Es sei denn das Ergebnis ist nicht eine Zahl sondern ein Typ.
    Dann wirds naemlich interessant.

    ahja, das lässt sich nicht hartcodieren, oder was



  • x schrieb:

    ahja, das lässt sich nicht hartcodieren, oder was

    Tu bitte mal

    template <typename ForwardIterator>
    void Algorithm(ForwardIterator itr)
    {
        typedef typename std::iterator_traits<ForwardIterator>::value_type ValueType;
        ... // Mache was mit ValueType
    }
    

    hier ValueType ohne Metaprogrammierung hardcodieren.



  • Shade Of Mine schrieb:

    ipsec schrieb:

    Aber sämtliche Beispiele dafür sind prinzipbedingt akademischer Humbug, denn ein zur Compilezeit feststehendes Ergebnis kann ich genauso gut hardcodieren.

    Es sei denn das Ergebnis ist nicht eine Zahl sondern ein Typ.
    Dann wirds naemlich interessant.

    Ja natürlich, da habe ich mich falsch ausgedrückt. Ich meinte Beispiele wie:

    fak<10>::value;
    primzahlen_bis<100>::value;
    nullstelle<add<mul<2, mul<x, x> >, -1> >::next::value;    // 2. Nullstelle von f(x)=2x^2-1
    kuerzester_weg<'A', 'C', graph<knoten<'A'>, knoten<'B'>, knoten<'C'>, kante<'A', 'B', 1>, kante<'B', 'C', 2>, kante<'A', 'C', 100> > >::begin::next;   // 2. Knoten des kürzesten Weges
                                                                                                                                                           // von A nach C in dem Graphen
    

    Diese Ergebnisse kann man wesentlich lesbarer auch hardcodieren.
    Das in Verbindung mit Typen TMP gewaltig Entwicklungszeit oder Laufzeit spart, ist natürlich klar. (Man denke an eine Implementierung von vector ohne Templates (und am besten noch ohne Makros, denn die sind böse)).



  • ipsec schrieb:

    und am besten noch ohne Makros, denn die sind böse

    Interessant ist doch gerade, dass Makros in Kombination mit Template-Metaprogrammierung enormes Potential haben.

    Ob nun

    TYPELIST_4(int, double, bool, signed char)
    

    oder

    STATIC_ASSERT(Condition)
    

    oder

    MAP_TYPES(KeyA, ValueA)
    MAP_TYPES(KeyB, ValueB)
    MAP_TYPES(KeyC, ValueC)
    

    Man hat mit Makros eine effiziente Waffe gegen den unschönen und sperrigen Template-Code. Man kann Metafunktionen wie Funktionen aussehen lassen. Makros ermöglichen sogar mächtige Codegeneratoren wie Boost.Preprocessor.


Anmelden zum Antworten