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)
