Templatemetaprogramming: Zählen zur Compilezeit



  • Das würde ich mal

    // Größe von Counter<i> == i 
    template <int i> 
    struct Counter 
    { 
        char c[i]; 
    };
    

    durch

    // Größe von Counter<i> == i 
    template <int i> 
    struct Counter 
    { 
        static const size=i; 
    };
    

    ersetzen.

    // Am Anfang ist dies die einzig vorhandene Funktion getCounter
    // Dies entspricht einem currentVal von 1
    static Counter<1> getCounter(Gen<0>);
    

    Diese Zeil versteh ich nicht du erzeugst eine File statiche instance von Counter<1> names getCounter. And den Konstruktor übergibst du Gen<0>. Allerdings gibt es solch ein Konstruktor gar net also eigentlich Fehler. 😕 Was hab ich da falsch verstanden?



  • Hallo,
    ok. Scheinbar bin ich nicht dazu in der Lage euch klar zu machen, worum es geht.

    @volkard
    Kennst du das __COUNTER__-Makro des VC .NET Compilers?

    Das geht so:

    // einDatei.cpp
    
    char c[__COUNTER__];   // c[1]
    char d[__COUNTER__];   // d[2]
    char e[__COUNTER__];   // e[3]
    

    __COUNTER__ wird durch eine Compile-Zeit-Konstante ersetzt. Danach wird intern ein Counter erhöht. Jede Anwendung des Makros erhöht also einen Counter.
    Schreibe ich das Makro viermal, dann ist der Wert 4. Schreibe ich es weitere viermal, dann ist der Wert 8. Usw.

    Genau sowas versuche ich nachzubauen. Meine Lösung funktioniert, gefällt mir aber nicht so sehr.

    Inwiefern aber hilft mir dein enum-Ansatz?

    Irgendwer schrieb:

    Das würde ich mal

    // Größe von Counter<i> == i 
    template <int i> 
    struct Counter 
    { 
        char c[i]; 
    };
    

    durch

    // Größe von Counter<i> == i 
    template <int i> 
    struct Counter 
    { 
        static const size=i; 
    };
    

    ersetzen.

    Sicher nicht. Denn damit würde ich den kompletten Trick kaputt machen.

    Irgendwer schrieb:

    // Am Anfang ist dies die einzig vorhandene Funktion getCounter
    // Dies entspricht einem currentVal von 1
    static Counter<1> getCounter(Gen<0>);
    

    Diese Zeil versteh ich nicht du erzeugst eine File statiche instance von Counter<1> names getCounter. And den Konstruktor übergibst du Gen<0>. Allerdings gibt es solch ein Konstruktor gar net also eigentlich Fehler. 😕 Was hab ich da falsch verstanden?

    Nein. Ich *deklariere* eine Funktion namens GetCounter die ein Counter<1>-Objekt liefert und ein Gen<0>-Objekt als Parameter erwartet.

    Noch mal langsam:
    Als erstes habe ich *eine* Funktion getCounter. Diese erwartet ein Gen<0>-Objekt.

    Der Code zur Bestimmung des aktuellen Wertes des Counters basiert auf simpler sizeof-Magic:

    sizeof(getCounter(Gen<Type, Type::maxCtrVal>()))
    

    Hier wird die *Größe* des Rückgabewertes eines *hypothetischen* Funktionsaufrufs bestimmt. sizeof führt dabei Überladungsauflösung durch. Da es aber zur Compilezeit ausgewertet wird, wird natürlich nicht wirklich eine Funktion aufgerufen. Deshalb reichen auch überall Funktionsdeklarationen.

    Um den Counter-Wert zu erhöhen, erzeuge ich eine *neue* Funktionsdeklaration.
    Diese neue Funktion liefert ein Counter<Wert+1>-Objekt. Dieses Objekt ist um eins größer als ein Counter<Wert>-Objekt (das char-Array hat ein Element mehr).

    Damit auch die neue Funktion bei der Überladungsauflösung ausgewählt wird, muss sie natürlich besser passen, als die vorherige. Dies erreiche ich dadurch, dass ich ein Gen<Wert>-Parameter verwende.

    Alle Gen<i>-Objekte erben von einander, d.h. ein Gen<MaxWert>-Objekt kann an alle Gen<n>, mit n <= MaxWert, Parameter gebunden werden.

    Je Größer ist aber n dabei wähle, desto weniger Derived-to-Base-Konvertierungen sind nötig und desto besser passt die Funktion zu einem Gen<MaxWert>-Objekt.



  • HumeSikkins schrieb:

    Hallo,
    @volkard
    Kennst du das __COUNTER__-Makro des VC .NET Compilers?

    nein.

    HumeSikkins schrieb:

    Das geht so:
    ...

    jetzt kene ich es.

    Genau sowas versuche ich nachzubauen. Meine Lösung funktioniert, gefällt mir aber nicht so sehr.

    mir auch nicht. sicherlich aus den gleichen gründen.

    Inwiefern aber hilft mir dein enum-Ansatz?

    gar nicht. ich mag nur sizeof nicht so. und ich habe nicht gecheckt, ob enum hier überhaupt geht. der code kam sozusaen aus dem bauch. da er dich nicht freudig angesprungen hat, geht es wohl nicht.

    ich mache mich mal dran, zu versuchen, __COUNTER__ zu basteln. ohne deine weitere fürderung, daß es parallele counters geben soll. mal schaun, ob das auch ohne obergrenze geht.



  • volkard schrieb:

    ich mache mich mal dran, zu versuchen, __COUNTER__ zu basteln. ohne deine weitere fürderung, daß es parallele counters geben soll.

    Ok. Ich brauche in meinem konkreten Fall zwar mehrere Counter, aber das ist leztlich wurscht, da es mir mehr um den allgemeinen Fall geht 🙂

    mal schaun, ob das auch ohne obergrenze geht.

    Da bin ich ganz zuversichtlich. Nach langem Suchen habe ich folgendes gefunden:

    Compile time...variables?
    Funktioniert natürlich aber nicht mit dem VC.
    Denke aber, dass man da zur Not einen Workaround basteln könnte.



  • dein link war nett.
    statt dessen code zu verstehen, hab ich nur __LINE__ übernommen.

    #include <iostream>
    using namespace std;
    
    template<int LINE>
    struct C
    {
    	enum{VALUE=C<LINE-1>::VALUE};
    };
    template<>
    struct C<0>
    {
    	enum{VALUE=1};
    };
    
    #define COUNTER C<__LINE__>::VALUE
    
    #define INC_COUNTER \
    template<> \
    struct C<__LINE__> \
    { \
    	enum{VALUE=C<LINE-1>::VALUE+1}; \
    }
    
    ///////////////////////////////////////////////////
    
    char a[COUNTER];
    INC_COUNTER;
    char b[COUNTER];
    
    int main()
    {
    	cout<<sizeof(a)<<endl;
    	cout<<sizeof(b)<<endl;
    	return 0;
    }
    

    hoffe, sowas meintest du.



  • Es kann nur anders gehen, wenn man etwas findet, das zur Compilezeit Werte zurückgibt und da fällt mir momentan nur sizeof ein.

    mfg



  • Glamdrink schrieb:

    Es kann nur anders gehen, wenn man etwas findet, das zur Compilezeit Werte zurückgibt und da fällt mir momentan nur sizeof ein.

    Ich hatte spontan an Typelisten und deren Längen gedacht. Bin aber zu keiner Lösung gekommen. Deshalb die Lösung mit der Überladung.

    @volkard
    Fett. Da wäre ich im Leben nicht drauf gekommen. Dabei ist deine Lösung von der Komplexität her viel einfacher. Aber so ist das ja immer: Die Guten finden die einfachen Lösungen. Die Blöden die komplizierten 🙂
    Ich denke es sollte auch nicht so schwer sein, deine Lösung auf mehrere Counter auszuweiten. Auch wenn die naheliegenste Variante vom VC nicht gefressen wird.

    Btw. Den Code aus dem Artikel habe ich problemlos auf den VC 6.0 übertragen können.



  • HumeSikkins schrieb:

    @volkard
    Fett. Da wäre ich im Leben nicht drauf gekommen.

    hihi. und ich hab nichtmal backspace drücken müssen. das floss einfach so rein.

    Dabei ist deine Lösung von der Komplexität her viel einfacher. Aber so ist das ja immer: Die Guten finden die einfachen Lösungen. Die Blöden die komplizierten 🙂

    naja, "gut" ist so ein ding. ich kann halt nur einfache dinge machen. die komplizierten raffe ich nicht. und wenn's nur kompilziert geht, hab ich ein dickes problem, das andere nicht haben.

    Ich denke es sollte auch nicht so schwer sein, deine Lösung auf mehrere Counter auszuweiten. Auch wenn die naheliegenste Variante vom VC nicht gefressen wird.

    jo, konnte kompliziert werden. kann ich nicht.



  • Jetzt hab ich noch ne dumme Frage hintendran, was sind die Voteile etwas zur Laufzeit zu machen? Im Endeffekt könnte man das Ganze unglaublich einfach zur Laufzeit machen. Ein Vorteil fällt mir grad noch ein, der Name sagt es ja schon, Compile-Zeit, d.h. das was sonst die Laufzeit langsamer machen würde kann jetzt zur Compile-Zeit gemacht werden, oder nicht??



  • Anderer schrieb:

    Jetzt hab ich noch ne dumme Frage hintendran, was sind die Voteile etwas zur Laufzeit zu machen? Im Endeffekt könnte man das Ganze unglaublich einfach zur Laufzeit machen. Ein Vorteil fällt mir grad noch ein, der Name sagt es ja schon, Compile-Zeit, d.h. das was sonst die Laufzeit langsamer machen würde kann jetzt zur Compile-Zeit gemacht werden, oder nicht??

    manche sachen gehen nur zur compilezeit. arraygrößen und marken in switch zum beispiel.

    class A
    {
       ...
       enum {TYPE=COUNTER};
       ...
    };
    INC_COUNTER;
    class B
    {
       ...
       enum {TYPE=COUNTER};
       ...
    };
    ...
    void readAllArmiesOfMySuperiousWarGame(ostream& in)
    {
       int type;
       while(in>>type)
       {
          switch(A::TYPE)
             armies.push_pack(new A(in));break;
          switch(B::TYPE)
             armies.push_pack(new B(in));break;
       }
    };
    

    das könnte die bisherigen hashtables krass outperformen. oh, yeah!



  • Den Code verstehe ich nicht

    void readAllArmiesOfMySuperiousWarGame(ostream& in) 
    { 
       int type; 
       while(in>>type) 
       { 
    
          // Was machen die beiden switches? Ist das überhaupt korrekter Code?
    
          switch(A::TYPE) 
             armies.push_pack(new A(in));break; 
          switch(B::TYPE) 
             armies.push_pack(new B(in));break; 
       } 
    };
    


  • der_held schrieb:

    Den Code verstehe ich nicht

    kein wunder. der ist auch ein bißchen falsch.
    sollte vermutlich eher so aussehen:

    void readAllArmiesOfMySuperiousWarGame(ostream& in) 
    { 
       int type; 
       while(in>>type) 
       { 
          switch(type)
          {
             case A::TYPE:
                armies.push_pack(new A(in));break; 
             case B::TYPE:
                armies.push_pack(new B(in));break;
          } 
       }
    };
    

Anmelden zum Antworten