Hi Bruno, Paul, On 2026-02-22T09:52:11+0100, Bruno Haible wrote: > Paul Eggert wrote: > > C23 says that functions like strchr are obsolescent, so arguably it's OK > > if Gnulib strnul doesn't introduce an immediately-obsolescent feature, > > i.e., if strnul is only a macro, not a function. > > Indeed: n3220.pdf § 7.26.5.1.(2) footnote 365. > > > However, Gnulib strnul strays from glibc strchr in a different way. > > Gnulib strnul rejects arguments of type void * (or void const *) at > > compile time, whereas glibc strchr allows these arguments and treats > > them like char * (or char const *). C23 implies (but does not explicitly > > state) that the glibc approach is correct. Also, the sample > > implementation on page 7 of > > <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3020.pdf> agrees with > > glibc. > > But the prototype given in n3020.pdf page 11, and in n3220.pdf § 7.26.5.3, > has an argument type 'QChar *', explained in n3220.pdf § 7.26.5.1.(2). > > So, to me, accepting a 'void *' argument here looks like a bug in > n3020.pdf page 7 and in glibc.
Hmmm, I have two conflicting opinions.
- [const] void* is intended to be allowed anywhere where a pointer type
would be allowed. Consistency would be in favour of accepting it.
- It is extremely rare to actually want to pass a [const] void* to
strnul(3). Type safety would be in favour of rejecting it.
And FWIW, in my own code, I have wrappers for several libc APIs where
I do favour type safety over consistency. Here are for example my type-
safe wrappers for lsearch(3) and qsort(3):
// lsearch_T - linear search-and-insert type-safe
#define lsearch_T(T, ...) lsearch_T_(typeas(T), __VA_ARGS__)
#define lsearch_T_(T, k, a, n, cmp) do \
{ \
_Generic(k, T *: (void)0, const T *: (void)0); \
_Generic(a, T *: (void)0); \
lsearch(k, a, n, sizeof(T), cmp); \
} while (0)
// qsort_T - sort type-safe
#define qsort_T(T, ...) qsort_T_(typeas(T), __VA_ARGS__)
#define qsort_T_(T, a, n, cmp) do \
{ \
_Generic(a, T *: (void)0); \
qsort(a, n, sizeof(T), cmp); \
} while (0)
I think I'm going to agree that rejecting void* is a good choice, as
I made the same exact choice.
I also agree the standard is not clear whether it should be accepted.
Although, I think it would be reasonable to remain ambiguous for some
time, and come back to revising that after some years, when
implementations have had time to implement this and share their
experience. I think it'll be useful to have at least gnulib rejecting
void*, for when that discussion comes.
Have a lovely day!
Alex
> > 1. Gnulib lib/string.in.h should contain a comment saying that its
> > strnul is stricter with void pointers than glibc's strchr, and why.
>
> I would prefer to get this fixed in glibc.
>
> > 2. The Gnulib internal function should be renamed from gl_strchr to
> > __gl_strchr to make it obvious to the reader that it's private to the
> > implementation.
>
> I disagree with this: Gnulib is application code and therefore has no
> business in defining symbols that start with '__', which are reserved
> for use by the system (= compiler and libc).
>
> > 3. The Gnulib test for whether _Generic is supported is semi-duplicated
> > in lib/cdefs.h (copied from glibc) and in lib/string.in.h. The tests
> > should agree. A quick look suggests that neither approach strictly
> > dominates the other so a merge needs to take place.
>
> In my reading, the one in lib/string.in.h and lib/string-desc.h is a
> superset of the one in lib/cdefs.h.
>
> The definition in lib/cdefs.h (copied from glibc) should better be
> updated
> - to reflect that _Generic works also in C++ mode with clang,
> - to add (defined __SUNPRO_C && __SUNPRO_C >= 0x5150)
>
> Bruno
>
>
>
--
<https://www.alejandro-colomar.es>
signature.asc
Description: PGP signature
