• ({

    From Alan Mackenzie@3:633/280.2 to All on Fri Mar 21 05:27:26 2025
    Hello, C++.

    I'm having some difficulty (amending Emacs's C++ Mode) reconciling the
    two conflicting uses in C++ of ({.

    Firstly, it is used as a "statement expression", a GCC enhancement also
    found in C, allowing a more relaxed and natural way to write an
    expression as the end result of a sequence of statements:

    ({ int y = foo (); int z;
    if (y > 0) z = y;
    else z = - y;
    z; })

    .. I think this usage is very old.

    Secondly, there's initialisation expressions like:

    void f4 (int a, int b, int c)
    {
    std::vector<ABC> abcList2(
    {{a+6,
    b+6,
    c+6}
    }
    );
    ....
    }

    .. Here the ( on the std::vector line, together with the next {, can be confused as a statement expression, though it's clearly not meant that
    way. I think this syntax is much newer than the other one, though I may
    be wrong here.

    In calculating the indentation for source lines in these constructs, the ambiguity causes mis-indentation for one or the other of them.

    Now to my question: how common is GCC's statement expression in the wide
    world of C++ source code? How much would be lost if I simply removed the statement expression from C++ Mode's parsing functions?

    --
    Alan Mackenzie (Nuremberg, Germany).


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: muc.de e.V. (3:633/280.2@fidonet)
  • From Bonita Montero@3:633/280.2 to All on Fri Mar 21 19:58:53 2025
    Am 20.03.2025 um 19:27 schrieb Alan Mackenzie:
    Hello, C++.

    I'm having some difficulty (amending Emacs's C++ Mode) reconciling the
    two conflicting uses in C++ of ({.

    Firstly, it is used as a "statement expression", a GCC enhancement also
    found in C, allowing a more relaxed and natural way to write an
    expression as the end result of a sequence of statements:

    ({ int y = foo (); int z;
    if (y > 0) z = y;
    else z = - y;
    z; })

    If the sign of y is badly predictible you may chose:

    z = (y >> sizeof(y) * CHAR_BIT - 1) * y;


    . I think this usage is very old.

    Secondly, there's initialisation expressions like:

    void f4 (int a, int b, int c)
    {
    std::vector<ABC> abcList2(
    {{a+6,
    b+6,
    c+6}
    }
    );
    ....
    }

    . Here the ( on the std::vector line, together with the next {, can be confused as a statement expression, though it's clearly not meant that
    way. I think this syntax is much newer than the other one, though I may
    be wrong here.

    In calculating the indentation for source lines in these constructs, the ambiguity causes mis-indentation for one or the other of them.

    Now to my question: how common is GCC's statement expression in the wide world of C++ source code? How much would be lost if I simply removed the statement expression from C++ Mode's parsing functions?



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Paavo Helde@3:633/280.2 to All on Fri Mar 21 21:54:31 2025
    On 20.03.2025 20:27, Alan Mackenzie wrote:
    Hello, C++.

    I'm having some difficulty (amending Emacs's C++ Mode) reconciling the
    two conflicting uses in C++ of ({.

    Firstly, it is used as a "statement expression", a GCC enhancement also
    found in C, allowing a more relaxed and natural way to write an
    expression as the end result of a sequence of statements:

    ({ int y = foo (); int z;
    if (y > 0) z = y;
    else z = - y;
    z; })

    . I think this usage is very old.

    Secondly, there's initialisation expressions like:

    void f4 (int a, int b, int c)
    {
    std::vector<ABC> abcList2(
    {{a+6,
    b+6,
    c+6}
    }
    );
    ....
    }

    . Here the ( on the std::vector line, together with the next {, can be confused as a statement expression, though it's clearly not meant that
    way. I think this syntax is much newer than the other one, though I may
    be wrong here.

    This braced initialization (called "List-initialization") was introduced
    in C++11, it is standardized and in wide and growing use.


    In calculating the indentation for source lines in these constructs, the ambiguity causes mis-indentation for one or the other of them.

    Now to my question: how common is GCC's statement expression in the wide world of C++ source code? How much would be lost if I simply removed the statement expression from C++ Mode's parsing functions?

    It appears the statement expressions are a gcc extension which does not
    even compile in standard C++, and is probably not needed for anything in
    C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.

    During the last 25 years I do not recall having encountered this feature
    ever, and I do not even recall anyone bashing it. So I guess it could be ditched pretty easily.




    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Sat Mar 22 06:27:59 2025
    Paavo Helde <eesnimi@osa.pri.ee> writes:
    [...]
    It appears the statement expressions are a gcc extension which does
    not even compile in standard C++, and is probably not needed for
    anything in C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.
    [...]

    Statement expressions don't compile in *standard* C or C++.

    gcc supports them as an extension for both C and C++ (and Objective-C).

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Bonita Montero@3:633/280.2 to All on Sat Mar 22 20:09:19 2025
    Am 21.03.2025 um 20:27 schrieb Keith Thompson:

    Statement expressions don't compile in *standard* C or C++.

    In C++ you could use lambdas for that.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Alan Mackenzie@3:633/280.2 to All on Sat Mar 22 21:53:41 2025
    Paavo Helde <eesnimi@osa.pri.ee> wrote:
    On 20.03.2025 20:27, Alan Mackenzie wrote:
    Hello, C++.

    I'm having some difficulty (amending Emacs's C++ Mode) reconciling the
    two conflicting uses in C++ of ({.

    Firstly, it is used as a "statement expression", a GCC enhancement also
    found in C, allowing a more relaxed and natural way to write an
    expression as the end result of a sequence of statements:

    ({ int y = foo (); int z;
    if (y > 0) z = y;
    else z = - y;
    z; })

    . I think this usage is very old.

    Secondly, there's initialisation expressions like:

    void f4 (int a, int b, int c)
    {
    std::vector<ABC> abcList2(
    {{a+6,
    b+6,
    c+6}
    }
    );
    ....
    }

    . Here the ( on the std::vector line, together with the next {, can be
    confused as a statement expression, though it's clearly not meant that
    way. I think this syntax is much newer than the other one, though I may
    be wrong here.

    This braced initialization (called "List-initialization") was introduced
    in C++11, it is standardized and in wide and growing use.

    Yes, thanks.

    In calculating the indentation for source lines in these constructs, the
    ambiguity causes mis-indentation for one or the other of them.

    Now to my question: how common is GCC's statement expression in the wide
    world of C++ source code? How much would be lost if I simply removed the
    statement expression from C++ Mode's parsing functions?

    It appears the statement expressions are a gcc extension which does not
    even compile in standard C++, and is probably not needed for anything in
    C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.

    I'm not sure what you meant by templated functions here, but an inline
    function has the disadvantage of fragmenting the code. Rather than have
    a few lines of code where they're used, you need to look somewhere else
    to see what they do.

    There are some uses of the statement expression in the (C) Linux kernel sources.

    During the last 25 years I do not recall having encountered this feature ever, and I do not even recall anyone bashing it. So I guess it could be ditched pretty easily.

    Thanks, that was what I thought. But in the last couple of days, I might
    have come up with a way to parse both of the ({ constructs without (too
    much) ambiguity.

    --
    Alan Mackenzie (Nuremberg, Germany).


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: muc.de e.V. (3:633/280.2@fidonet)
  • From Alan Mackenzie@3:633/280.2 to All on Sat Mar 22 22:00:34 2025
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Paavo Helde <eesnimi@osa.pri.ee> writes:
    [...]
    It appears the statement expressions are a gcc extension which does
    not even compile in standard C++, and is probably not needed for
    anything in C++ as there are better options like templated and inlined
    functions.
    In C there might be some usage case for it.
    [...]

    Statement expressions don't compile in *standard* C or C++.

    More precisely, they're not standard C or C++. I think it is a shame
    they never became part of the standard, as no doubt their developers
    intended.

    gcc supports them as an extension for both C and C++ (and Objective-C).

    There are instances of statement expressions in the Linux kernel,
    developed under GCC, and clang was enhanced to support them too so as to
    be able to compile Linux. Or so I've heard.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --
    Alan Mackenzie (Nuremberg, Germany).


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: muc.de e.V. (3:633/280.2@fidonet)
  • From Alan Mackenzie@3:633/280.2 to All on Sat Mar 22 22:26:31 2025
    Bonita Montero <Bonita.Montero@gmail.com> wrote:
    Am 21.03.2025 um 20:27 schrieb Keith Thompson:

    Statement expressions don't compile in *standard* C or C++.

    In C++ you could use lambdas for that.

    The sort of thing statement expressions are good at are the likes of (C example):

    while ({
    ch = getch ();
    ch = toupper (ch);
    ch != 'Q';
    })
    switch (ch) {
    ....
    }

    , the loop of a command processing function which takes single case
    insensitive commands from the keyboard. While it is true that this
    example could be rewritten using the comma operator in place of the
    statement expression, it would only need to be slightly more complicated
    to make this impractical.

    Yes, in C++, a lambda could be used instead, but this is somewhat
    artificial and would have more boilerplate cluttering up the actual code.

    Anyhow, I haven't actually done any real C++ hacking for about 20 years,
    I just try to keep Emacs's C++ Mode working.

    --
    Alan Mackenzie (Nuremberg, Germany).


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: muc.de e.V. (3:633/280.2@fidonet)
  • From Scott Lurndal@3:633/280.2 to All on Sun Mar 23 01:09:21 2025
    Reply-To: slp53@pacbell.net

    Alan Mackenzie <acm@muc.de> writes:
    Paavo Helde <eesnimi@osa.pri.ee> wrote:
    On 20.03.2025 20:27, Alan Mackenzie wrote:

    It appears the statement expressions are a gcc extension which does not
    even compile in standard C++, and is probably not needed for anything in
    C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.

    I'm not sure what you meant by templated functions here, but an inline >function has the disadvantage of fragmenting the code. Rather than have
    a few lines of code where they're used, you need to look somewhere else
    to see what they do.

    An inline function name should be chosen such that it is sufficient to
    define exactly what the effect of the function call will be, while
    still hiding the implementation and improving readability.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: UsenetServer - www.usenetserver.com (3:633/280.2@fidonet)
  • From Bonita Montero@3:633/280.2 to All on Sun Mar 23 02:14:42 2025
    Am 21.03.2025 um 09:58 schrieb Bonita Montero:

    ({ int y = foo (); int z;
    if (y > 0) z = y;
    else z = - y;
    z; })

    If the sign of y is badly predictible you may chose:

    z = (y >> sizeof(y) * CHAR_BIT - 1) * y;

    Sorry, your code is more efficient since it can be substituted by
    a negate i a second register and a conditional move. But some archi-
    tectures like RISC-V don't have conditional moves and my code would
    be the most efficient way to handle this.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Paavo Helde@3:633/280.2 to All on Sun Mar 23 02:18:58 2025
    On 22.03.2025 12:53, Alan Mackenzie wrote:
    Paavo Helde <eesnimi@osa.pri.ee> wrote:
    It appears the statement expressions are a gcc extension which does not
    even compile in standard C++, and is probably not needed for anything in
    C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.

    I'm not sure what you meant by templated functions here, but an inline function has the disadvantage of fragmenting the code. Rather than have
    a few lines of code where they're used, you need to look somewhere else
    to see what they do.

    At the GCC page for statement expressions they say "This feature is
    especially useful in making macro definitions “safe” (so that they evaluate each operand exactly once)" and most of their examples are
    about macros.

    A macro is fragmenting the code exactly in the same way as an inline
    function, except the macros are worse than functions in multiple ways.
    In C one might argue macros are needed for supporting multiple types at
    the same time, but in C++ this is solved much better by function templates.

    Ergo, a C-style macro using statement expressions can easily replaced
    with a more regular and better behaving function or function template in
    C++, with no increase in code "fragmentation".

    For "in-place" use Bonita is right a lambda can be used to the same
    effect, but the readability probably does not go better. As per the
    first example on the GCC page (poor man implementation of abs()):

    int a = ({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; });

    can be written with a lambda in C++:

    int a = [](){int y = foo (); int z; if (y > 0) z = y; else z = - y;
    return z; }();

    I suspect Bonita might like this. I still prefer the more mundane

    int a = std::abs(foo());



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Alan Mackenzie@3:633/280.2 to All on Sun Mar 23 02:20:35 2025
    Scott Lurndal <scott@slp53.sl.home> wrote:
    Alan Mackenzie <acm@muc.de> writes:
    Paavo Helde <eesnimi@osa.pri.ee> wrote:
    On 20.03.2025 20:27, Alan Mackenzie wrote:

    It appears the statement expressions are a gcc extension which does not >>> even compile in standard C++, and is probably not needed for anything in >>> C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.

    I'm not sure what you meant by templated functions here, but an inline >>function has the disadvantage of fragmenting the code. Rather than have
    a few lines of code where they're used, you need to look somewhere else
    to see what they do.

    An inline function name should be chosen such that it is sufficient to
    define exactly what the effect of the function call will be, while
    still hiding the implementation and improving readability.

    Yes, that's fine in theory. In practice, meaning while debugging, a
    whole lot of midget functions, each called something like convert_next_char_to_upper_case_is_it_Q_p is a nightmare. That name is
    more difficult to understand than the three lines of code (see another
    of my posts in this thread) it replaces.

    While debugging, _nothing_ can be trusted to do what it says, and a
    hidden implementation makes things worse - rather than having to scroll
    a window to the inline function, it's probably in a different file.

    Even a single midget function which isn't a coherent thing makes
    debugging more irksome than simply having the three lines of code
    physically in the larger function where they are actually used.

    --
    Alan Mackenzie (Nuremberg, Germany).


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: muc.de e.V. (3:633/280.2@fidonet)
  • From Scott Lurndal@3:633/280.2 to All on Sun Mar 23 02:53:50 2025
    Reply-To: slp53@pacbell.net

    Alan Mackenzie <acm@muc.de> writes:
    Scott Lurndal <scott@slp53.sl.home> wrote:
    Alan Mackenzie <acm@muc.de> writes:
    Paavo Helde <eesnimi@osa.pri.ee> wrote:
    On 20.03.2025 20:27, Alan Mackenzie wrote:

    It appears the statement expressions are a gcc extension which does not >>>> even compile in standard C++, and is probably not needed for anything in >>>> C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.

    I'm not sure what you meant by templated functions here, but an inline >>>function has the disadvantage of fragmenting the code. Rather than have >>>a few lines of code where they're used, you need to look somewhere else >>>to see what they do.

    An inline function name should be chosen such that it is sufficient to
    define exactly what the effect of the function call will be, while
    still hiding the implementation and improving readability.

    Yes, that's fine in theory. In practice, meaning while debugging, a
    whole lot of midget functions, each called something like >convert_next_char_to_upper_case_is_it_Q_p is a nightmare.

    Strawman. Nobody suggested that a meaningful name
    requires forty characters.

    inline bool is_ep(void) const { return flags & PCC_EP;}

    is perfectly cromulent.

    Gdb() has no problem stepping through that, and shows the
    corresponding source.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: UsenetServer - www.usenetserver.com (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Sun Mar 23 03:45:49 2025
    On 22/03/2025 16:18, Paavo Helde wrote:
    On 22.03.2025 12:53, Alan Mackenzie wrote:
    Paavo Helde <eesnimi@osa.pri.ee> wrote:
    It appears the statement expressions are a gcc extension which does not
    even compile in standard C++, and is probably not needed for anything in >>> C++ as there are better options like templated and inlined functions.
    In C there might be some usage case for it.

    I'm not sure what you meant by templated functions here, but an inline
    function has the disadvantage of fragmenting the code. Rather than have
    a few lines of code where they're used, you need to look somewhere else
    to see what they do.

    At the GCC page for statement expressions they say "This feature is especially useful in making macro definitions “safe” (so that they evaluate each operand exactly once)" and most of their examples are
    about macros.

    A macro is fragmenting the code exactly in the same way as an inline function, except the macros are worse than functions in multiple ways.
    In C one might argue macros are needed for supporting multiple types at
    the same time, but in C++ this is solved much better by function templates.

    Ergo, a C-style macro using statement expressions can easily replaced
    with a more regular and better behaving function or function template in C++, with no increase in code "fragmentation".

    For "in-place" use Bonita is right a lambda can be used to the same
    effect, but the readability probably does not go better. As per the
    first example on the GCC page (poor man implementation of abs()):

    int a = ({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; });


    It can also combine well with type inference, such as the example from
    GCC's reference:

    #define max(a, b) \
    ({ __auto_type _a = (a); __auto_type _b = (b); \
    _a > _b ? _a : _b; })

    (In C23, "auto" is approximately equivalent to "auto" in C++, so you
    don't need the gcc "__auto_type" extension any more.)

    Of course such function-like macros are usually best replaced with
    template functions in C++. I can't think off-hand of a situation where statement expressions in C++ would be significantly better than either
    an inline function / function template, or an immediately-invoked lambda.




    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Jakob Bohm@3:633/280.2 to All on Tue Apr 1 15:59:03 2025
    On 2025-03-21 11:54, Paavo Helde wrote:
    On 20.03.2025 20:27, Alan Mackenzie wrote:
    Hello, C++.

    I'm having some difficulty (amending Emacs's C++ Mode) reconciling the
    two conflicting uses in C++ of ({.

    Firstly, it is used as a "statement expression", a GCC enhancement also
    found in C, allowing a more relaxed and natural way to write an
    expression as the end result of a sequence of statements:

    ({ int y = foo (); int z;
    if (y > 0) z = y;
    else z = - y;
    z; })

    . I think this usage is very old.

    Secondly, there's initialisation expressions like:

    void f4 (int a, int b, int c)
    {
    std::vector<ABC> abcList2(
    {{a+6,
    b+6,
    c+6}
    }
    );
    ....
    }

    . Here the ( on the std::vector line, together with the next {, can be
    confused as a statement expression, though it's clearly not meant that
    way. I think this syntax is much newer than the other one, though I may
    be wrong here.

    This braced initialization (called "List-initialization") was introduced
    in C++11, it is standardized and in wide and growing use.


    Actually, it is from K&R C, except (perhaps) the option to put the list expression in a pair of regular parenthesis with the usual identical
    meaning. The basic K&R form is:

    int smallprimes[4] = { 2, 3, 5, 7 };
    int identitymatrix[2][2] = { { 1, 0 }, { 0, 1 } };

    In ANSI C, these can be declared const and placed in a read-only part of
    the resulting program file. This was broken by the desire to allocate
    all data on the heap via heap-centric classes like vector<int> .


    Enjoy

    Jakob

    --
    Jakob Bohm, MSc.Eng., I speak only for myself, not my company
    This public discussion message is non-binding and may contain errors
    All trademarks and other things belong to their owners, if any.

    --- MBSE BBS v1.1.1 (Linux-x86_64)
    * Origin: Privat (3:633/280.2@fidonet)