Zähl-Makro?



  • Hi,

    meine Faulheit kennt keine Grenzen. Ich habe eine Reihe von Konstanten, deren Werte 2er-Potenzen entsprechen:

    const unsigned int VALUE1 = 1 << 0;
    const unsigned int VALUE2 = 1 << 1;
    //...
    

    Wenn ich in der Mitte etwas einfüge, was für die Lesbarkeit Sinn ergibt, muss ich bei allen folgenden Stellen die Zahl erhöhen. Das birgt Fehlerpotenzial und kostet mich wertvolle zwei Minuten.

    Ich hätte gerne statt 0/1/... ein Makro oder Template COUNT, was von selbst hochzählt. Hat jemand eine Idee, wie ich so etwas gestalten könnte?

    Beste Grüße,
    Eisflamme


  • Mod

    const unsigned int VALUE1 = 1;
    const unsigned int VALUE2 = VALUE1 << 1;
    const unsigned int VALUE3 = VALUE2 << 1;
    


  • Hm ja, das wäre schon Mal schön. Die Namen sind halt deutlich länger als VALUE1 und dann werden das alles sehr lange Zeilen. Ein Makro hätte ich halt CNT oder so genannt und nach dem Procedere wieder undefined. Ginge auch so was?



  • Kannst du nicht als Initialwert eine Funktion aufrufen, die jeweils den nächsten Wert zurückgibt? Innerhalb einer Übersetzungseinheit ist die Definitionsreihenfolge eindeutig.

    namespace
    {
        unsigned int next()
        {
            static unsigned int shift = 0;
            return 1 << shift++;
        }
    }
    
    const unsigned int VALUE1 = next();
    const unsigned int VALUE2 = next();
    ...
    


  • Nexus schrieb:

    Kannst du nicht als Initialwert eine Funktion aufrufen, die jeweils den nächsten Wert zurückgibt? Innerhalb einer Übersetzungseinheit ist die Definitionsreihenfolge eindeutig.

    namespace
    {
        unsigned int next()
        {
            static unsigned int shift = 0;
            return 1 << shift++;
        }
    }
    
    const unsigned int VALUE1 = next();
    const unsigned int VALUE2 = next();
    ...
    

    Flags will man schon Compilezeitkonstant.



  • Edit: Ich hab das Problem nicht verstanden.



  • Solche COUNT-Funktionen gehen nicht ohne den aktuellen Wert. Da müsste intern etwas erhöht werden, aber das ist nicht möglich, da zur Compilezeit nur Konstanten existieren.



  • Sone schrieb:

    Für C++98:

    #define LIST4(x, N) N<<(x), N<<((x)+1), N<<((x)+2), N<<((x)+3)
    #define LIST8(x, N) LIST4(x, N), LIST4(x+4, N)
    #define LIST16(x, N) LIST8(x, N), LIST8(x+8, N)
    #define LIST32(x, N) LIST16(x, N), LIST16(x+16, N)
    #define LIST64(N) LIST32(0, N), LIST32(32, N)
    
    const unsigned values[] = { LIST32(0, 1u) }; // angenommen unsigned hat 32-Bit. Suffix nicht vergessen!
    
    #undef LIST4
    #undef LIST8
    #undef LIST16
    #undef LIST32
    #undef LIST64
    
    #define DEF(N) \
        const unsigned int VALUE##N = values[N];
    
    DEF(0)
    DEF(1)
    DEF(2)
    
    #undef DEF
    
    #include <iostream>
    
    int main()
    {
        std::cout << VALUE0 << ' ' << VALUE1 << ' ' << VALUE2;
    }
    

    ? (http://ideone.com/ebF7wy)

    Kannst du nicht als Initialwert eine Funktion aufrufen, die jeweils den nächsten Wert zurückgibt?

    C++11 hilft ab:

    constexpr unsigned value(unsigned size)
    { return 1 << size; }
    
    #define DEF(N) \
        const unsigned int VALUE##N = value(N);
    
    DEF(0)
    DEF(1)
    DEF(2)
    
    #undef DEF
    
    #include <iostream>
    
    int main()
    {
        std::cout << VALUE0 << ' ' << VALUE1 << ' ' << VALUE2;
    }
    

    Die werden natürlich nicht VALUE1 VALUE2 VALUE3... heißen sollen, das war nur ein Beispiel.

    Also erstmal sollte das hier gehen, ohne die Zweierpotenzen aufzählen zu müssen.

    #define FILE_READ 1
    #define FILE_WRITE 2
    #define FILE_COPY_ON_WRITE 4
    #define FILE_SEQUENTIAL 8
    #define FILE_TEMPORARY 16
    

    Und dann, ups, ich habe FILE_APPEND vergessen und das soll aber nach WRITE rein.

    #define FILE_READ 1
    #define FILE_WRITE 2
    #define FILE_APPEND 4 //inserted
    #define FILE_COPY_ON_WRITE 4 //must change
    #define FILE_SEQUENTIAL 8 //must change
    #define FILE_TEMPORARY 16 //must change
    


  • Sone schrieb:

    Solche COUNT-Funktionen gehen nicht ohne den aktuellen Wert. Da müsste intern etwas erhöht werden, aber das ist nicht möglich, da zur Compilezeit nur Konstanten existieren.

    __LINE__ ändert sich. Falls man das als Template-Argument nehmen kann, kann man die aufsteigende Folge der __LINE__-Werte mit Templates stauchen zu 0,1,2,3,4... und dann einfach mit << zu Zweierpotenzen machen.
    Und falls das nicht geht, könnte der Compiler ein __COUNT__-Makro haben.



  • Sone schrieb:

    Solche COUNT-Funktionen gehen nicht ohne den aktuellen Wert. Da müsste intern etwas erhöht werden, aber das ist nicht möglich, da zur Compilezeit nur Konstanten existieren.

    Da geht immer was.
    Eisflamme ist sicherlich nicht drauf festgelegt, wie es aufzurufen ist.
    Vielleicht

    MAKE_FLAGS(FILE,FILE_READ,FILE_WRITE,FILE_COPY_ON_WRITE,FILE_SEQUENTIAL,FILE_TEMPORARY)
    

    oder irgendwas anderes Spaßiges.



  • __LINE__ ändert sich.

    Also __LINE__ - 34.
    Dann muss das ganze in eine Separate Header-Datei. Wenn das nämlich in einer größeren ist, und jemand nicht Bescheid weiß dass nach jedem Ändern des Quelltexts diese eine magic-constant geändert werden muss, dann gibt das Ärger.

    Eisflamme, du Faulpelz 🤡



  • würde an deiner stelle das mit den makros komplett lassen und konstanten benützen,
    da es einfach typensicher ist



  • Ich verstehe auch nicht, was das define mit ner Zahl bringt, dafür will ich ja Konstanten nutzen.

    Das mit __LINE__ ist klug, ich verstehe aber den Einwand nicht:

    #define LE_VALUE __LINE__ - 34
    
    const unsigned int VALUE1 = 2 << LE_VALUE; // MUST BE LINE NUMBER XY!!
    const unsigned int VALUE2 = 2 << LE_VALUE;
    

    Das gefällt mir super. 😋



  • Sone schrieb:

    __LINE__ ändert sich.

    Also __LINE__ - 34.
    Dann muss das ganze in eine Separate Header-Datei. Wenn das nämlich in einer größeren ist, und jemand nicht Bescheid weiß dass nach jedem Ändern des Quelltexts diese eine magic-constant geändert werden muss, dann gibt das Ärger.

    Eisflamme, du Faulpelz 🤡

    Leider klappt folgendes nicht auf jedem Compiler:

    #define LINESTART __LINE__
    #define FILE_READ (1<<(__LINE__-LINESTART))
    #define FILE_WRITE (1<<(__LINE__-LINESTART))
    #define FILE_COPY_ON_WRITE (1<<(__LINE__-LINESTART))
    #define FILE_SEQUENTIAL (1<<(__LINE__-LINESTART))
    #define FILE_TEMPORARY (1<<(__LINE__-LINESTART))
    

    LINESTART ist dann auf machen als 34 definiert und auf manchen als __LINE__ definiert.



  • __LINE__ mag nett erscheinen, bis jemand mal auf die Idee kommt, zu gruppieren oder Kommentare einzufügen. Als ob Leute ständig nachfolgenden Code prüfen würden, wenn sie irgendwo eine Zeile einfügen.

    Sowas ist extrem heikel -- und kann zu qualvoller Bugsuche führen, weil niemand das Problem hier vermutet. Das Risiko wäre mir die minimale Schreibaufwandersparnis definitiv nicht wert. Dann schon eher was in Richtung Boost.Preprocessor...



  • Nexus schrieb:

    Dann schon eher was in Richtung Boost.Preprocessor...

    Und das wäre Overkill für so ein lächerliches Problem.

    volkard schrieb:

    Leider klappt folgendes nicht auf jedem Compiler:

    Hab schon Probiert. 😉



  • Eisflamme schrieb:

    Das mit __LINE__ ist klug, ich verstehe aber den Einwand nicht:

    Was, wenn diese PP-Direktive auf einmal nicht mehr in Zeile 34, sondern 35 ist?


  • Mod

    #include <boost/preprocessor/seq/for_each.hpp>
    #include <boost/preprocessor/variadic/to_seq.hpp>
    
    #define DEF_VALUE( num, data, name ) const unsigned name = 1 << ( num - 2 );
    
    #define DEF_VALUES( ... ) BOOST_PP_SEQ_FOR_EACH( DEF_VALUE, _, BOOST_PP_VARIADIC_TO_SEQ( __VA_ARGS__ ) )
    
    DEF_VALUES( FILE_READ, FILE_WRITE, FILE_COPY_ON_WRITE, FILE_SEQUENTIAL, FILE_TEMPORARY )
    


  • Das sieht gut aus! Klappt das auch ohne Vartemps im Compiler?



  • Was sind Vartemps?


Log in to reply