Paul Eggert wrote:
> >> 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.
> 
> Regardless of whether it's fixed in glibc, we should document the 
> discrepancy/bug/whatever, as the issue exists now with current glibc and 
> current Gnulib.

Oh, I see now what you mean: In ISO C, any function call that takes
a 'char *' argument can also take a 'void *' argument. In his definition
of __glibc_const_generic, Joseph simulated this behaviour by explicitly
allowing 'void *' and 'const void *' arguments, in all const-generic
macros. I withdraw the claim it's a glibc bug; this choice makes perfect
sense (for backward compatibility with older glibc versions where strchr
was a plain function).

Reproduced with the attached foo.c.


2026-02-23  Bruno Haible  <[email protected]>

        strnul: Accept 'void *' and 'const void *' arguments in C mode.
        Reported by Paul Eggert in
        <https://lists.gnu.org/archive/html/bug-gnulib/2026-02/msg00170.html>.
        * lib/string.in.h (strnul): Use a conditional expression in _Generic.
        * tests/test-strnul.c (main): Add test cases with 'void *' argument.

diff --git a/lib/string.in.h b/lib/string.in.h
index 599203c44d..33422a06e7 100644
--- a/lib/string.in.h
+++ b/lib/string.in.h
@@ -1271,10 +1271,16 @@ template <> inline       char *strnul<      char *> (   
   char *s)
       || (defined __SUNPRO_C && __SUNPRO_C >= 0x5150) \
       || (__STDC_VERSION__ >= 201112L && !defined __GNUC__)
 /* The compiler supports _Generic from ISO C11.  */
+/* Since in C (but not in C++!), any function that accepts a '[const] char *'
+   also accepts a '[const] void *' as argument, we make sure that the function-
+   like macro does the same, by mapping its type first:
+     char *, void *             -> void *
+     const char *, const void * -> const void *
+   This mapping is done through the conditional expression.  */
 #   define strnul(s) \
-      _Generic (s, \
-                char *       : (char *) gl_strnul (s), \
-                const char * : gl_strnul (s))
+      _Generic (1 ? (s) : (void *) 99, \
+                void *       : (char *) gl_strnul (s), \
+                const void * : gl_strnul (s))
 #  else
 #   define strnul(s) \
       ((char *) gl_strnul (s))
diff --git a/tests/test-strnul.c b/tests/test-strnul.c
index b9b4970ad6..7c068d8f1e 100644
--- a/tests/test-strnul.c
+++ b/tests/test-strnul.c
@@ -33,5 +33,13 @@ main ()
   ASSERT (ro_nul - ro == 3);
   ASSERT (rw_nul - rw == 3);
 
+#if !defined __cplusplus
+  const char *rov_nul = strnul ((const void *) ro);
+  char *rwv_nul = strnul ((void *) rw);
+
+  ASSERT (rov_nul - ro == 3);
+  ASSERT (rwv_nul - rw == 3);
+#endif
+
   return test_exit_status;
 }
#include <string.h>

#if 1
extern char *my_strchr (const char *, int);
#define strchr my_strchr
#endif

extern const char *gl_strnul (const char *string);
#   define strnul(s) \
      _Generic (1 ? (s) : (void *) (s), \
                void *       : (char *) gl_strnul (s), \
                const void * : gl_strnul (s))


char str[] = "foo";

int main ()
{
  const char *a = strchr ((const char *) str, 'o');
        char *b = strchr ((char *) str, 'o');
  const char *c = strchr ((const void *) str, 'o');
        char *d = strchr ((void *) str, 'o');

  const char *na = strnul ((const char *) str);
        char *nb = strnul ((char *) str);
  const char *nc = strnul ((const void *) str);
        char *nd = strnul ((void *) str);

  return (a != b) + (c != d) + (na != nb) + (nc != nd);
}

Reply via email to