|
|
Message-ID: <20251126150222.GY1827@brightrain.aerifal.cx>
Date: Wed, 26 Nov 2025 10:02:22 -0500
From: Rich Felker <dalias@...c.org>
To: Alejandro Colomar <alx@...nel.org>
Cc: musl@...ts.openwall.com
Subject: Re: [PATCH v2 1/1] include/string.h: Implement QChar wrappers
standardized in C23
On Wed, Nov 26, 2025 at 03:56:21PM +0100, Alejandro Colomar wrote:
> Hi Rich,
>
> On Wed, Nov 26, 2025 at 09:17:11AM -0500, Rich Felker wrote:
> > On Wed, Nov 26, 2025 at 01:22:17PM +0100, Alejandro Colomar wrote:
> > > Hi Rich,
> > >
> > > On Tue, Nov 25, 2025 at 08:34:39PM -0500, Rich Felker wrote:
> > > > On Tue, Nov 25, 2025 at 10:49:29PM +0100, Alejandro Colomar wrote:
> > > [...]
> > > > > +# if __STDC_VERSION__ >= 202311L
> > > > > +# define strchrnul(s, ...) ((register __QCharof(s) *){ strchrnul(s, __VA_ARGS__)})
> > > > > +# define strcasestr(s, ...) ((register __QCharof(s) *){strcasestr(s, __VA_ARGS__)})
> > > > > +# endif
> > > > > #endif
> > > > >
> > > > > #ifdef __cplusplus
> > > > > --
> > > > > 2.51.0
> > > >
> > > > Could you explain a bit about your motivations for doing it this way?
> > > > Why are compound literals needed at all instead of just a value cast?
> > >
> > > Yup. I try to have 0 casts in my code. They silence several classes of
> > > diagnostics, and allow arbitrary conversions.
> > >
> > > For example, they could silence a bug in the prototype of strchr(3) if
> > > it accidentally returned an int*. Admittedly, <string.h> is so stable
> > > that it's not realistically going to have such bugs, so most of the
> > > issues it prevents are theoretical.
> >
> > Yes, that is not an issue for libc.
> >
> > > In my own projects, I don't trust myself so much, so I perform all
> > > return-value conversions through compound literals, which only allow
> > > implicit conversions.
> >
> > Understandable, but it's not something we need to copy into musl.
> >
> [...]
> >
> > > However, if you prefer it with a cast, I can change it. It's slightly
> > > less safe, but could be okay given how stable <string.h> should be.
> >
> > It's absolutely safe; you can't define unsafety of one thing on the
> > basis of making another disallowed change that's world-breakingly
> > unsafe already.
>
> Except that once these functions are wrapped through these macros, it
> wouldn't break the world anymore, so a future typo in the return type of
> strchr(3) could go unnoticed. The only code that would break with such
> an accidental change would be function pointers. But how often are
> function pointers to these function taken? I've never seen such code.
It doesn't have to be functionally breaking common code in the wild to
be breaking the *contract*. You'd not allowed to change them because
the specification fixes the signatures in stone.
> > > > Also, can't __Qcharof just be defined as something like
> > > > typeof(1?(s):"") without any fancy _Generic machinery?
> > >
> > > This wouldn't accept void*, and these functions should accept void*
> > > arguments.
> >
> > I was thinking the ternary of char* and void* would produce char*, but
> > indeed I'm always wrong about that.
>
> You weren't totally wrong. There's special case where that's true: NULL
>
> alx@...uan:~/tmp$ cat typeof.c
> #include <stdio.h>
> int
> main(void)
> {
> const char *cc;
> const void *cv;
> char *c;
> void *v;
>
> _Generic(typeof(1?cc:""), const char *: 0);
> _Generic(typeof(1?cv:""), const char *: 0);
> _Generic(typeof(1? c:""), char *: 0);
> _Generic(typeof(1? v:""), char *: 0);
>
> _Generic(typeof(1?(void *) 0:""), const char *: 0);
> _Generic(typeof(1?(const void *)0:""), char *: 0);
> }
> alx@...uan:~/tmp$ gcc typeof.c
> typeof.c: In function ‘main’:
> typeof.c:11:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> 11 | _Generic(typeof(1?cv:""), const char *: 0);
> | ^~~~~~
> typeof.c:13:18: error: ‘_Generic’ selector of type ‘void *’ is not compatible with any association
> 13 | _Generic(typeof(1? v:""), char *: 0);
> | ^~~~~~
> typeof.c:15:18: error: ‘_Generic’ selector of type ‘char *’ is not compatible with any association
> 15 | _Generic(typeof(1?(void *) 0:""), const char *: 0);
> | ^~~~~~
> typeof.c:16:18: error: ‘_Generic’ selector of type ‘const void *’ is not compatible with any association
> 16 | _Generic(typeof(1?(const void *)0:""), char *: 0);
> | ^~~~~~
>
> As you can see, typeof(1?(void*)0:"") produces a char*. Any other void*
> doesn't. This means that the type of NULL is a magic type different
> from void*, but which is compatible with void*. This is slightly
> different from nullptr_t in that _Generic(3) is able to distinguish
> nullptr_t form void*, but not the type of NULL from void*, but it's
> pretty similar.
typeof(1?"":(typeof(s))0) maybe? I think it works!
Rich
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.