Hello Alejandro.

Thanks for your explanation.

Alejandro Colomar via Mutt-dev wrote in
 <aeF5M6eOVsGnO_Jw@devuan>:
 |On 2026-04-17T01:53:43+0200, Steffen Nurpmeso wrote:
 |> ok sorry i should not post at all .. i realized in the meantime
 |> this C23 was on the table at first long ago, by Rene Kita,
 |> already..  but
 |> 
 |> Alejandro Colomar via Mutt-dev wrote in
 |>  <aeDEw-PCa6n1P_4R@devuan>:
 |>  ..
 |>|I've implemented a patch for musl (which hasn't been merged yet).
 |>|It only needs C11 features.
 |
 |Plus typeof, which I give as granted, of course.
 |
 |> This is very (, very) cool.
 |
 |Thanks!  :)

I absolutely emphasize it.

 |>|You can have a look at it to see how that is implemented:
 |>|<https://www.openwall.com/lists/musl/2026/02/23/2>
 |> 
 |>|It's much simpler than the glibc implementation, IMO.
 |>|
 |>|I'll paste here the essentials:
 |>|
 |>| #define __QVoidptrof(p)  typeof(1?(p):(void*){0})
 |>| #define __QCharptrof(s)  typeof                               \
 |>| (                                                             \
 |>|  _Generic((__QVoidptrof(s)){0},                        \
 |>|   const void *: (const char *) 0,               \
 |>|   void *:       (char *) 0                      \
 |>|  )                                                     \
 |>| )
 |>|
 |>| #define memchr(p, ...)      ((__QVoidptrof(p)) memchr(p, __VA_ARGS__))
 |>| #define memmem(p, ...)      ((__QVoidptrof(p)) memmem(p, __VA_ARGS__))
 |>| #define memrchr(p, ...)     ((__QVoidptrof(p)) memrchr(p, __VA_ARGS__))
 |>|
 |>| #define strchr(s, ...)      ((__QCharptrof(s)) strchr(s, __VA_ARGS__))
 |>| #define strrchr(s, ...)     ((__QCharptrof(s)) strrchr(s, __VA_ARGS__))
 |>| #define strpbrk(s, ...)     ((__QCharptrof(s)) strpbrk(s, __VA_ARGS__))
 |>| #define strstr(s, ...)      ((__QCharptrof(s)) strstr(s, __VA_ARGS__))
 |>| #define strchrnul(s, ...)   ((__QCharptrof(s)) strchrnul(s, __VA_ARGS__)\
 |>| )
 |>| #define strcasestr(s, ...)  ((__QCharptrof(s)) strcasestr(s, __VA_ARGS__\
 |>| ))
 |> 
 |> That __Qvoidptr() really is magic to me.
 |> I still do not get it, and your "how" did not give any glue.
 |
 |Yup, I meant how it can be done, not really an explanation of how the
 |magic works.  I guess I should have explained it, as it's really not
 |obvious.
 |
 |The macro is
 |
 | #define __QVoidptrof(p)  typeof(1?(p):(void*){0})
 |
 |The interesting thing is the third operand to the ternary operator.
 |
 |One thing to know is that the null pointer constant is magic, and
 |different from a regular null pointer of type void*.
 |
 |Here's a relevant quote from C11 about the conditional operator:
 |
 |<https://port70.net/~nsz/c/c11/n1570.html#6.5.15p6>
 |
 | If both the second and third operands are pointers
 | or one is a null pointer constant and the other is a pointer,
 | the result type is
 | a pointer to a type
 |  qualified with all the type qualifiers
 |  of the types referenced by both operands.
 | Furthermore,
 | if both operands are pointers to compatible types
 | or to differently qualified versions of compatible types,
 | the result type is
 | a pointer to
 |  an appropriately qualified version
 |  of the composite type;
 | if one operand is a null pointer constant,
 | the result has the type
 |  of the other operand;
 | otherwise,
 | one operand is a pointer to void or a qualified version of void,
 | in which case the result type is
 |  a pointer to an appropriately qualified version
 |  of void.

This, to my shame maybe even, is already in ISO C99.

 |This means that
 |
 | const    int  *p;
 | volatile int  *q;
 |
 | typeof(1 ? p : q)            x;  // const volatile int  *
 | typeof(1 ? p : NULL)         y;  // const          int  *
 | typeof(1 ? p : (void *){0})  z;  // const          void *
 |
 |In the case of x, the clause 'Furthermore, ...' applies.
 |In the case of y, the clause 'if one operand is a null pointer
 |constant, ...' applies.
 |In the case of z, since (void*){0} is not a null pointer constant, but
 |it is a pointer to void, the last clause applies, and thus we get an
 |appropriately qualified version of void.
 |
 |This can be shown with the following program:
 |
 | alx@devuan:~/tmp$ cat t.c 
 | #include <stddef.h>
 |
 | int
 | main(void)
 | {
 |  const    int  *p;
 |  volatile int  *q;
 |
 |  _Generic(typeof(1 ? p : q),           const volatile int  *: 0);
 |  _Generic(typeof(1 ? p : NULL),        const          int  *: 0);
 |  _Generic(typeof(1 ? p : (void *){0}), const          void *: 0);
 |}
 | alx@devuan:~/tmp$ gcc -Wall -Wextra -Wno-unused t.c 
 | alx@devuan:~/tmp$ 
 |
 |About why the compound literal (void *){0} is not a null pointer
 |constant, it's because the null pointer constants are very limited.
 |<https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3>
 |
 | An integer constant expression with the value 0,
 | or such an expression cast to type void *,
 | is called a null pointer constant.
 |
 |Since a compound literal is none of that, it can't be a null pointer
 |constant, which gives us the interesting properties we get here.

Very, very smart, to the point.  I myself would be massively
afraid of inventing something like this (in case i could/can) in
practice because of compatibility issues.  (For example, i had
major distress when gcc 3.4.2 came out with a hatred against
unnamed unions, but the necessity to be compatible with it,
quickly.  Or clang 5.0.1 which did not find superclass fields (of
*some* classes) unless they were explicitly mentioned via this->.
And many miscompilation problems.  You know all that.)

 |(BTW, an integer constant expression with the value 0 will eventually
 | stop being a null pointer constant.  GCC has added
 | -Wzero-as-null-pointer-constant in C mode recently, and some
 | C Committee members --including myself, and some GCC contributors-- are
 | planning the removal in the standard in a way that will have a
 | relatively smooth transition.  Then, the only null pointer constant
 | will be (void*)0, and of course the horrible nullptr, but we better
 | act as if that never existed.)

This is very much interesting (surely nitpicker etc will not fit
your picture), especially given that C++ has 0l as NULL (except
that "__cplusplus + 0 >= 201103L" has nullptr as far as
i discovered).

One thing i always "hated".  Unfortunately C++ is not simply an
extension of C, with both "variants" taking painstaking efforts
in that direction.

 |[...]
 |> 
 |> But that __Qvoidptr() totally escapes me.
 |> Ie decay (Joy Division is greeting) back and forth, i must specify
 |> a compatible pointer type, and no matter whether i say
 |>   typeof(true ? x : y)
 |> or
 |>   typeof(false ? x : y)
 |> or the order of x and y, i will always get a "void [const]*".
 |> 
 |> I mean, "all pointer types implicitly convert to void*".
 |> But it is total magic!
 |
 |:-)

I will not forget it.  Thanks for your explanation!

 |Have a lovely night!

You too.

--steffen
|
|Der Kragenbaer,                The moon bear,
|der holt sich munter           he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)

Reply via email to