[Toybox] [SC22WG14.32312] Statement expressions

enh enh at google.com
Mon Jul 14 08:04:46 PDT 2025


On Mon, Jul 14, 2025 at 10:58 AM Alejandro Colomar
<une+c at alejandro-colomar.es> wrote:
>
> On Mon, Jul 14, 2025 at 04:48:58PM +0200, Alejandro Colomar wrote:
> > Hi Chris, Jakub,
> >
> > I was talking with Elliott Hughes (Bionic maintainer) and Rob Landley
> > (toybox), and Elliott reminded me something:
> >
> > Why did the committee standardize typeof() at all without standardizing
> > ({})?  They almost always come together.
> >
> > ({}) (or do-while(0)) together with typeof() are used to implement what
> > C++ has as templates.
> >
> > Here's how it's used in shadow-utils:
> >
> >       $ grepc -tu typeof .
> >       ./lib/search/sort/qsort.h:#define QSORT(a, n)  do                                               \
> >       {                                                                     \
> >               __auto_type  p_ = a;                                          \
> >                                                                             \
> >               qsort(p_, n, sizeof(*p_), CMP(typeof(p_)));                   \
> >       } while (0)
> >       ./lib/search/l/lfind.h:#define LFIND(k, a, n)                                                \
> >       ({                                                                    \
> >               __auto_type  k_ = k;                                          \
> >               __auto_type  a_ = a;                                          \
> >                                                                             \
> >               static_assert(is_same_typeof(k_, a_), "");                    \
> >                                                                             \
> >               (typeof(k_)) lfind_(k_, a_, n, sizeof(*k_), CMP(typeof(k_))); \
> >       })
> >       ./lib/search/l/lsearch.h:#define LSEARCH(k, a, n)                                              \
> >       ({                                                                    \
> >               __auto_type  k_ = k;                                          \
> >               __auto_type  a_ = a;                                          \
> >                                                                             \
> >               static_assert(is_same_typeof(k_, a_), "");                    \
> >                                                                             \
> >               (typeof(k_)) lsearch(k_, a_, n, sizeof(*k_), CMP(typeof(k_)));\
> >       })
> >       ./lib/atoi/a2i/a2i.h:#define a2i(TYPE, n, s, ...)                                                  \
> >       (                                                                             \
> >               _Generic((void (*)(TYPE, typeof(s))) 0,                               \
> >                       void (*)(short,              const char *):  a2sh_c,          \
> >                       void (*)(short,              const void *):  a2sh_c,          \
> >                       void (*)(short,              char *):        a2sh_nc,         \
> >                       void (*)(short,              void *):        a2sh_nc,         \
> >                       void (*)(int,                const char *):  a2si_c,          \
> >                       void (*)(int,                const void *):  a2si_c,          \
> >                       void (*)(int,                char *):        a2si_nc,         \
> >                       void (*)(int,                void *):        a2si_nc,         \
> >                       void (*)(long,               const char *):  a2sl_c,          \
> >                       void (*)(long,               const void *):  a2sl_c,          \
> >                       void (*)(long,               char *):        a2sl_nc,         \
> >                       void (*)(long,               void *):        a2sl_nc,         \
> >                       void (*)(long long,          const char *):  a2sll_c,         \
> >                       void (*)(long long,          const void *):  a2sll_c,         \
> >                       void (*)(long long,          char *):        a2sll_nc,        \
> >                       void (*)(long long,          void *):        a2sll_nc,        \
> >                       void (*)(unsigned short,     const char *):  a2uh_c,          \
> >                       void (*)(unsigned short,     const void *):  a2uh_c,          \
> >                       void (*)(unsigned short,     char *):        a2uh_nc,         \
> >                       void (*)(unsigned short,     void *):        a2uh_nc,         \
> >                       void (*)(unsigned int,       const char *):  a2ui_c,          \
> >                       void (*)(unsigned int,       const void *):  a2ui_c,          \
> >                       void (*)(unsigned int,       char *):        a2ui_nc,         \
> >                       void (*)(unsigned int,       void *):        a2ui_nc,         \
> >                       void (*)(unsigned long,      const char *):  a2ul_c,          \
> >                       void (*)(unsigned long,      const void *):  a2ul_c,          \
> >                       void (*)(unsigned long,      char *):        a2ul_nc,         \
> >                       void (*)(unsigned long,      void *):        a2ul_nc,         \
> >                       void (*)(unsigned long long, const char *):  a2ull_c,         \
> >                       void (*)(unsigned long long, const void *):  a2ull_c,         \
> >                       void (*)(unsigned long long, char *):        a2ull_nc,        \
> >                       void (*)(unsigned long long, void *):        a2ull_nc         \
> >               )(n, s, __VA_ARGS__)                                                  \
> >       )
> >       ./lib/typetraits.h:#define is_unsigned(x)                                                        \
> >       (                                                                             \
> >               (typeof(x)) -1 > 1                                                    \
> >       )
> >       ./lib/typetraits.h:#define is_signed(x)                                                          \
> >       (                                                                             \
> >               (typeof(x)) -1 < 1                                                    \
> >       )
> >       ./lib/typetraits.h:#define is_same_typeof(a, b)                                                  \
> >       (                                                                             \
> >               is_same_type(typeof(a), typeof(b))                                    \
> >       )
> >
> > Admittedly, typeof() has some uses outside of that.
>
> BTW, the uses we have outside of that, such as is_signed() and
> is_unsigned(), are used to implement what we'll (hopefully) standardize
> as _Minof() and _Maxof() quite soon.  And there's the one use whithin a
> _Generic() that selects two types at once (shown above).

trying to move bionic's <sys/param.h> max() and min() over to ({}) to
make them safe against double-evaluation, it turns out that llvm
disallows use of ({}) outside of functions (allegedly because gcc
does, though the bug in question is from 2009, so that may have
changed) ... which is problematic because max() is fairly often used
to size arrays.

> >  Which reminded me
> > of something more egregious: Why did the committee standardize auto (as
> > __auto_type) without ({})??  They always come together.
> >
> > Here are the uses of auto in shadow-utils:
> >
> >       $ grepc -tu __auto_type .
> >       ./lib/search/sort/qsort.h:#define QSORT(a, n)  do                                               \
> >       {                                                                     \
> >               __auto_type  p_ = a;                                          \
> >                                                                             \
> >               qsort(p_, n, sizeof(*p_), CMP(typeof(p_)));                   \
> >       } while (0)
> >       ./lib/search/l/lfind.h:#define LFIND(k, a, n)                                                \
> >       ({                                                                    \
> >               __auto_type  k_ = k;                                          \
> >               __auto_type  a_ = a;                                          \
> >                                                                             \
> >               static_assert(is_same_typeof(k_, a_), "");                    \
> >                                                                             \
> >               (typeof(k_)) lfind_(k_, a_, n, sizeof(*k_), CMP(typeof(k_))); \
> >       })
> >       ./lib/search/l/lsearch.h:#define LSEARCH(k, a, n)                                              \
> >       ({                                                                    \
> >               __auto_type  k_ = k;                                          \
> >               __auto_type  a_ = a;                                          \
> >                                                                             \
> >               static_assert(is_same_typeof(k_, a_), "");                    \
> >                                                                             \
> >               (typeof(k_)) lsearch(k_, a_, n, sizeof(*k_), CMP(typeof(k_)));\
> >       })
> >       ./lib/string/strchr/strnul.h:#define strnul(s)                                                             \
> >       ({                                                                            \
> >               __auto_type  s_ = s;                                                  \
> >                                                                                     \
> >               s_ + strlen(s_);                                                      \
> >       })
> >       ./lib/string/strspn/stpspn.h:#define stpspn(s, accept)                                                     \
> >       ({                                                                            \
> >               __auto_type  s_ = s;                                                  \
> >                                                                                     \
> >               s_ + strspn(s_, accept);                                              \
> >       })
> >       ./lib/string/strspn/stprcspn.h:#define stprcspn(s, reject)                                                   \
> >       ({                                                                            \
> >               __auto_type  s_ = (s);                                                \
> >                                                                                     \
> >               s_ + strrcspn(s_, reject);                                            \
> >       })
> >       ./lib/string/strspn/stprspn.h:#define stprspn(s, accept)                                                    \
> >       ({                                                                            \
> >               __auto_type  s_ = (s);                                                \
> >                                                                                     \
> >               s_ + strrspn_(s_, accept);                                            \
> >       })
> >
> > Exactly zero uses outside of ({}) or do-while(0).
> >
> > What's the reason for auto in the first place???
> >
> > While one can use auto _can_ be used outside of ({}) or do-while(0),
> > that's something we should not encourage, which is BTW why I think this
> > feature should have been called _Auto: to keep it as a "there be
> > dragons" feature to be used in macros, like _Generic().
> >
> >
> > Have a lovely day!
> > Alex
> >
> > --
> > <https://www.alejandro-colomar.es/>
>
>
>
> --
> <https://www.alejandro-colomar.es/>


More information about the Toybox mailing list