1

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 *?

3
  • 2
    My expectation is the generic strchr is implemented with _Generic with cases for char * and const char *. _Generic matches by type compatibility, so it will not match void * or const 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 in strchr((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.) Commented Jun 22 at 12:54
  • 1
    If QChar was supposed to be compatible with void, then C23 wouldn't have made a separate QVoid type. That functions do not compile when passed a void* rather than the specific type is arguably a feature rather than a problem. Because there's no telling where the void* might point at. Forcing a cast on the caller side is a good thing IMO. C++ has worked that way since forever.
    – Lundin
    Commented Jun 24 at 9:42
  • Btw your function should always take a const char* parameter regardless of if the argument passed was a char* or const char*.
    – Lundin
    Commented Jun 24 at 9:48

0

Browse other questions tagged or ask your own question.