Problem mit Kompilierzeitrekursion



  • Gleich vornweg: ich weiß das es den header limits gibt. Das folgende hbe ich nur aus Spaß an der Freude zu implementieren versucht. Also, mein code sieht so aus:

    template<class T>
    class get_max_size
    {
    private:
    	template<T t>
    	struct u
    	{
    		enum{temp=((1+t)>t?u<1+t>::temp:t)};
    	};
    public:
    	enum{size=u<0>::temp};
    };
    

    Wenn ich nun sowas schreibe:

    cout<<static_cast<unsigned>(get_max_size<unsigned char>::size);
    

    erhalte ich als Errors:
    -ständig wird mir gemeldet, das eine Konstante verkürzt wird
    -wenn die Rekursion am Ende ist, sagt man mir, dass temp nicht definiert ist
    -und dann fatal error, weiterverarbeitung nicht möglich
    Wieso? Das klingt so als ob die Rekursion falsch ist. Aber wo?



  • (t+1)>t?u<t+1>::temp:t)
    

    was willst du damit ausrechnen?
    ersteinmal landest du damit immer in einer endlosschleife, da t+1 immer größer als t ist.

    dh der compiler versucht temp solange rekursiv aufzurufen, bis die maximale template tiefe erreicht ist, dann bricht er ab. da dieser abgebrochene wert eigentlich völlig kaputt ist, da der letzte "wert" ein nicht spezialisiertes template ist, liefert er dir den fehelr zurück, dass temp nicht integer konstant ist.



  • #include <iostream>
    #include <limits>
    using namespace std;
    
    int main()
    {
    	cout << numeric_limits<int>::max()     << '\n' 
    		 << numeric_limits<int>::max() + 1 << endl;
    }
    

    Ausgabe
    2147483647
    -2147483648

    Das wäre zwar keine Endlosschleife, allerdings wären
    2^31 Schritte schon verdammt viel nur um das Maximum
    herauszufinden ..



  • Unabhängig davon funktioniert ?: zur Compilezeit anders als zur Laufzeit. Zur Compile-Zeit werden *immer* beide Seiten ausgewertet, egal wie die Bedingung aussieht. Deshalb kann man ?: in Compile-Zeit-Programmen quasi nicht gebrauchen. Man muß immer auf Lösungen wie template-Spezialisierung zurückgreifen.

    MfG Jester



  • @Jester: thx, durch die Erklärung zu ?: ist mir klar geworden warums nicht geht...
    @alle anderen: danke für die Hilfe, ich weiß das das große Rekursionen werden, mal gucken ob ich das irgendwie hinkrieg (läuft nicht mal mit unsigned short)

    Nachdem mir Jester erklärte, warum es nicht geht, habe ich gleich eine Funktionierende Lösung geschrieben, für alle dies interessiert:

    #ifndef NDEBUG
    #define STATIC_CHECK_2(expr,id)\
    Loki::CompileTimeError<expr> ERROR_##id
    #else
    #define STATIC_CHECK_2(expr,id) 0
    #endif
    
    template<class T>
    class get_max_size
    {
    private:
    	template<T t>
    	struct u
    	{
    		enum{temp=(1+u<1+t>::temp)};
    	};
    	template<>
    	struct u<0>
    	{
    		enum{temp=1};
    	};
    public:
    #ifndef NDEBUG
    	const static bool test_bool=(static_cast<T>(-1)>static_cast<T>(0));
    	STATIC_CHECK_2(test_bool,THIS_WORKS_ONLY_FOR_UNSIGNEDS);
    #endif
    	enum{size=u<1>::temp};
    };
    

    (Warum ist STATIC_CHECK so komisch implementiert?)



  • was genau meinst Du mit "komisch" implementiert?



  • Na, STATIC_CHECK aus static_check.h der loki-Bibliothek sieht so aus:

    #define STATIC_CHECK(expr, msg) \
        { Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
    

    Das ist:
    -deutlich komplizierter als meins
    -2 Anwisungen (statt einer)
    ->So wie ich das gerade zeigte nicht zu benutzen.
    Also, nicht das das jemand falsch versteht, ich bin fasziniert von der Lib und habe und habe sicher ne Menge davon gelernt, aber warum ist das so implementiert? Hat das nen bestimmten Grund?



  • Also der Block {} wird aufgemacht, damit Du auch zweimal hintereinander den check mit der gleichen id benutzen kannst. Sonst gibt's Fehler, daß es die Variable schon gibt. Die zweite Anweisung ist vermutlich gegen Unused-Variable-Warnings vom Compiler. warum expr allerdings mit 0 verglichen wird weiß ich auch nicht.



  • Ja, mit dem Block hast du recht. Aber leider kann man nicht sowas wie

    struct test
    {
        int a;
        {
            int noch_ein_int;
        };
    };
    

    schreiben.



  • Stimmt, aber in ner Funktion kann man sowas machen.


Anmelden zum Antworten