This is a follow-up to: What is the new QChar* in C23?, where @Lundin showed a sample implementation of a function that takes
a QChar *
with _Generic
, after which I implemented strchrnul()
like so:
#define util_strchrnul(s, c) \
_Generic((s), \
char *: util_strchrnul_vanilla, \
const char *: util_strchrnul_const)(s, c)
#define gen_strchrnul(version, ret_type, arg_type) \
ret_type util_strchrnul_##version(arg_type s[static 1], int c) { \
while (*s && *s != c) { ++s; } \
return s; \
}
gen_strchrnul(vanilla, char *, char)
gen_strchrnul(const, const char *, const char)
Prior to C23, this code would have been valid:
void *p = "he\0llo";
*strchrnul(p, '\0') = '\0';
But with my implementation, passing a void *
or const void *
to util_strchrnul()
would not compile.
Does the ISO C Standard say anything about passing void *
/const void *
to functions that accept a QChar *
? Is it allowed? Would void *
return char *
, and const void *
return const char *
?
strchr
is implemented with_Generic
with cases forchar *
andconst char *
._Generic
matches by type compatibility, so it will not matchvoid *
orconst void *
unless those are explicitly included as cases, which I do not expect. You can work around this with#undef strchr
, using parentheses around the function name, as in(strchr)(p, '\0')
, or converting to corresponding type, as instrchr((char *) p, '\0')
. (Late drafts of C 2023 note in 7.26.5.1 that avoiding the macro to expose the concrete function is obsolescent.)void
, then C23 wouldn't have made a separate QVoid type. That functions do not compile when passed avoid*
rather than the specific type is arguably a feature rather than a problem. Because there's no telling where thevoid*
might point at. Forcing a cast on the caller side is a good thing IMO. C++ has worked that way since forever.const char*
parameter regardless of if the argument passed was achar*
orconst char*
.