Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <j2v7hh66fbevcordlkun2epiq2k3xix4uj3ui7p22osasgvbqv@ybikrfxp4p67>
Date: Wed, 26 Nov 2025 15:56:21 +0100
From: Alejandro Colomar <alx@...nel.org>
To: Rich Felker <dalias@...c.org>
Cc: musl@...ts.openwall.com
Subject: Re: [PATCH v2 1/1] include/string.h: Implement QChar wrappers
 standardized in C23

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.

Also, if new functions such as strprefix()/strpfx() and
strsuffix()/strsfx() are added (I'm working on that in the C Committee),
those would have less existing uses, which would mean a typo there would
almost certainly go unnoticed.

But anyway, I understand prefering a cast for the simplicity.

> > > 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.

> > I don't know if there might be a way to write it without _Generic(3),
> > but I haven't found it.
> 
> Indeed, I can't think of any at the moment.

Okay, I'll send a revision using a cast.  If you ever change your mind
and want to use a compound literal it should use 'register' and 'const':

	#define strchr(s,c) ((register __QCharof(s) *const){strchr(s,c)})

Where register prevents
	&strchr(s,c);

And const prevents
	strchr(s,c) = NULL;


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).

Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)

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.