Re: Minor cleanup after adding more warnings to CFLAGS
Steffen Nurpmeso <[email protected]>
| Newsgroups | gmane.mail.mutt.devel |
|---|---|
| Message-ID | <20260417231014.gmUM9Hd6@steffen%sdaoden.eu> |
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)