Zähl-Makro?



  • 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?



  • Variadische Templates braucht das nicht, aber variadische Makros, die einige Compiler (*hust* MSVC *hust*) nicht so richtig beherrschen. Du kannst aber auch direkt eine Boost.PP-Sequenz benutzen:

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

    Sieht nicht ganz so hübsch aus, aber läuft.



  • volkard schrieb:

    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.

    Compiler, auf denen das nicht klappt, implementieren Makros richtig. Makros werden erst im Body expandiert.



  • volkard schrieb:

    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.

    Was spricht gegen:

    constexpr std::size_t LINESTART = __LINE__;
    

    ?



  • Nathan schrieb:

    volkard schrieb:

    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.

    Was spricht gegen:

    constexpr std::size_t LINESTART = __LINE__;
    

    ?

    Gut.
    Dann muss man die anderen aber auch alle mit constexpr machen und hat gar kein #define mehr.

    Hmm, wenn wir soweit gehen, tuts ja sogar ein enum.

    #include <iostream>
    
    enum FILEMODE{
        LINESTART = __LINE__,
        FILE_READ = (1<<(__LINE__-LINESTART-1)),
        FILE_WRITE = (1<<(__LINE__-LINESTART-1)),
        FILE_COPY_ON_WRITE = (1<<(__LINE__-LINESTART-1)),
        FILE_SEQUENTIAL = (1<<(__LINE__-LINESTART-1)),
        FILE_TEMPORARY = (1<<(__LINE__-LINESTART-1)),
    };
    
    int main(){
        std::cout<<FILE_WRITE<<'\n';
        std::cout<<FILE_READ<<'\n';
    }
    

    oder

    #include <iostream>
    
    #define BEGINFLAGS _line_start = __LINE__
    #define NEXTFLAG (1<<(__LINE__-_line_start-1))
    
    enum file_mode{
        BEGINFLAGS,
        read = NEXTFLAG,
        write = NEXTFLAG,
        copy_on_write = NEXTFLAG,
        sequential = NEXTFLAG,
        temporary = NEXTFLAG,
    };
    
    int main(){
        std::cout<<file_mode::write<<'\n';
        std::cout<<file_mode::read<<'\n';
    }
    

Anmelden zum Antworten