#define mit do{...}while(0) obsolet: Neu mit Lamdas!



  • Jeder Code hat immer mal wieder ein

    #define FOO(x) do { ... } while(0)
    

    drin.

    Heute kam mir ein Geistesblitz: Man kann neu Lambdas verwenden!

    #define FOO(x) ((void)([](){ ... })())
    

    Es ist sogar möglich, Werte zurückzugeben:

    #define FOO(x) ([](){ ... return ... }())
    

    Jetzt kann man Makros endlich mit einem Komma-Operator verwenden:

    for (...)
      FOO(x), FOO(y); // ging vorher nicht
    

    Was haltet ihr davon? 👍



  • dersinndeslambdas schrieb:

    Jeder Code hat immer mal wieder ein

    #define FOO(x) do { ... } while(0)
    

    drin.

    Nein?!! Wieso auch? (Auchw enn mir das entfernt irgendwo bekannt vorkommt, aber ich weis nicht mehr wo ich das mal gesehen hab oo)

    Daher halt ich von sowas Abstand...



  • Hast du gleich einen konkreten Fall, wo du den do-while-Trick und den Komma-Operator benötigst? Besonders Letzteres kommt mir etwas fragwürdig vor...



  • don't feed the troll



  • Skym0sh0 schrieb:

    dersinndeslambdas schrieb:

    Jeder Code hat immer mal wieder ein

    #define FOO(x) do { ... } while(0)
    

    drin.

    Nein?!! Wieso auch? (Auchw enn mir das entfernt irgendwo bekannt vorkommt, aber ich weis nicht mehr wo ich das mal gesehen hab oo)

    Daher halt ich von sowas Abstand...

    https://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html#Swallowing-the-Semicolon


  • Mod

    #define FOO(x) ((void)([](){ ... })())
    

    Der Cast ist überflüssig. Der Ausdruck hat schon Typ void wenn kein return -Statement drinsteht. Das leere Klammerpaar ist auch unnötig.
    ➡

    #define FOO(x) (([]{ ... })())
    

    Sonst schließe ich mich Nexus an.



  • Nexus schrieb:

    Hast du gleich einen konkreten Fall, wo du den do-while-Trick und den Komma-Operator benötigst? Besonders Letzteres kommt mir etwas fragwürdig vor...

    Benötigen ist das falsche Wort, aber praktisch finde ich es schon. Zum einen um zwei zusammengehörige Operationen auf einer Zeile zu halten (siehe Beispiel im ersten Post), zum andern um in if-Bedingungen einen Nebeneffekt einzubauen (z.B. SKIP_SPACES) ohne zwei ifs grundlos zu nesten.

    Selbst wenn das nicht oft vorkommt, wenn man schon die Gelegenheit hat, sollte man es richtig machen.

    Wichtig ist auch, dass nun Rückgabewerte möglich sind.

    if (FOO(x)) // geht nicht mit do-while(0)
      FOO(y);   // resp. dem {...}-Trick
    else {}     // weil das ; das if vom else trennt.
    

    Alles mit Lambdas zu machen führt auch zu Einheitlichkeit.



  • Ich persönlich brauche auch nicht den do-while-Trick bei Makros.
    Es gibt bei mir nämlich nur zwei Arten von Makros.
    Einmal die Meta-Makros zur Codegenerierung mehrerer ähnlich aussehender Funktion (was allerdings nicht durch Templates lösbar ist...) und dann Diagnostics-Makros wo ich nur Makros verwende, weil ich an die Funktions- und Dateinamen kommen will.
    Im ersteren ist das egal, beim Zweiten ist das eh nur ein einfacher Funktionsauruf.


  • Mod

    Einmal die Meta-Makros zur Codegenerierung mehrerer ähnlich aussehender Funktion

    Beispiel bitte. 👍

    Ich glaube das auch schon mal gebraucht zu haben, aber ich bin gerade im "kann man bestimmt Widerlegen-Rausch". 😃



  • @dersinndeslambdas
    Hast du mal probiert ob aktuelle Compiler das auch vernünftig optimieren können?



  • Arcoth schrieb:

    Einmal die Meta-Makros zur Codegenerierung mehrerer ähnlich aussehender Funktion

    Beispiel bitte. 👍

    Ich glaube das auch schon mal gebraucht zu haben, aber ich bin gerade im "kann man bestimmt Widerlegen-Rausch". 😃

    Das stammt aus meiner eigenen Implementierung einer iostream-Bibliothek*, genau genommen aus der Implementierung zum Pendant von std::num_put:

    #define MY_IMPL_WRITE_INTEGRAL_DIGITS(NAME, BASE) \
    template <typename IntegralT, typename OIterator, class Encoding> \
    void write_integral_digits_##NAME(IntegralT value, OIterator &iter, \
                                      const numeric<Encoding> &numeric_info) \
    { \
        streamsize num_digits = 0; \
        do \
        { \
            print_code_point(iter, numeric_info.digit_to_char(value % BASE)); \
            value /= BASE; \
            if (value > 0 && numeric_info.is_##NAME##_sep_after(num_digits++)) \
                print_code_point(iter, numeric_info.NAME##_sep()); \
        } while (value > 0); \
    }
    
    MY_IMPL_WRITE_INTEGRAL_DIGITS(decimal, 10)
    MY_IMPL_WRITE_INTEGRAL_DIGITS(hexadecimal, 16)
    MY_IMPL_WRITE_INTEGRAL_DIGITS(octal, 8)
    
    #undef MY_IMPL_WRITE_INTEGRAL_DIGITS
    

    edit:
    Ach ja, übrigens:
    https://github.com/Arcoth/VTMPL/blob/master/algorithms.hxx

    #define DEFINE_FO( name, ... ) \
    template<index_type a> \
    struct name \
    { \
    template<index_type b> \
    struct function : std::integral_constant<index_type, (__VA_ARGS__)> {}; \
    }
    
    DEFINE_FO(add, b + a);
    DEFINE_FO(xor_, b xor a);
    DEFINE_FO(multiply, b * a);
    DEFINE_FO(modulo, b % a);
    
    #undef DEFINE_FO
    #define DEFINE_FO( name, ... ) \
    template<index_type a> struct name : std::integral_constant<index_type, (__VA_ARGS__)> {}
    
    DEFINE_FO( square, a*a );
    DEFINE_FO( invert, -a );
    #undef DEFINE_FO
    

    /edit

    *Ja, ich habe sonst nichts zu tun. Deshalb implementiere ich quasi die komplette Standardbibliothek neu und "besser" (vom Design her). Vieles sind einfach nur Wrapper (mein std::vector z.B., nur an mein Iterator-Konzept angepasst und mit meinem Errorchecking), anderes habe ich komplett implementiert. Darunter auch der komplette IO-Teil. Meine Implementierung hat eine kleinere Hierachie, ist nicht ganz so konfigurierbar, einfacher zu nutzen sowohl was Manipulatoren als auch streambufs angeht, Multibyteencoding-aware und formattiert Integer ganze 20ns schneller als die Implementierung meines Compilers ( 😃 ).


  • Mod

    numeric_info.is_##NAME##_sep_after(num_digits++)

    Warum ist is_##NAME##_sep_after fest für alle Basen einzeln definiert? Warum kein Template, dass die Basis als Template-Argument nimmt?



  • Arcoth schrieb:

    numeric_info.is_##NAME##_sep_after(num_digits++)

    Warum ist is_##NAME##_sep_after fest für alle Basen einzeln definiert? Warum kein Template, dass die Basis als Template-Argument nimmt?

    virtual Memberfunction.



  • Warum es nicht wie alle normalen Menschen machen und die Basis als Funktionsparameter übergeben?


  • Mod

    Ach ja, übrigens:
    https://github.com/Arcoth/VTMPL/blob/master/algorithms.hxx

    Da haste mich glatt mit meinem eigenen Code erwischt. 👍

    (VTMPL muss ich mal wieder auffrischen, mach' mich dran)



  • warbat schrieb:

    Warum es nicht wie alle normalen Menschen machen und die Basis als Funktionsparameter übergeben?

    Ich hatte vorschnellen Optimierungswahn und wollte ifs vermeiden.
    Eine Art Tag-Dispatching wäre allerdings ok, aber jetzt ist es zu spät.



  • dersinndeslambdas schrieb:

    Jeder Code hat immer mal wieder ein

    #define FOO(x) do { ... } while(0)
    

    drin.

    oder ähnliche Schreibweisen. gcc erzwingt irgendwie while(0){}, weil er in allen anderen Schreibweisen rumwarnt.

    dersinndeslambdas schrieb:

    Heute kam mir ein Geistesblitz: Man kann neu Lambdas verwenden!

    #define FOO(x) ((void)([](){ ... })())
    

    Es ist sogar möglich, Werte zurückzugeben:

    #define FOO(x) ([](){ ... return ... }())
    

    Jetzt kann man Makros endlich mit einem Komma-Operator verwenden:

    for (...)
      FOO(x), FOO(y); // ging vorher nicht
    

    Was haltet ihr davon? 👍

    Sehr gute Idee! 👍
    Nun paßt __asm int 3; ins ASSERT und es bleibt ein Ausdruck.


Log in to reply