Collin Funk wrote:
> Thanks for the review. I pushed the patches with that change to the
> manual and Bruno's formatting suggestions.

Unfortunately, the CI reports a compilation error on glibc systems
and musl libc systems. It can be reproduced with a testdir:

$ ./gnulib-tool --create-testdir --dir=../testdir --symlink --with-c++-tests 
nullptr getline

This is the error:

g++ -DHAVE_CONFIG_H -I. -I../../gltests -I..  -DGNULIB_STRICT_CHECKING=1 
-DIN_GNULIB_TESTS=1 -I. -I../../gltests -I.. -I../../gltests/.. -I../gllib 
-I../../gltests/../gllib -I/media/develdata/devel/inst-x86_64-64/include -Wall 
-Wno-error -Wno-error -g -O2 -MT test-nullptr-c++.o -MD -MP -MF 
.deps/test-nullptr-c++.Tpo -c -o test-nullptr-c++.o 
../../gltests/test-nullptr-c++.cc
In file included from /gnu-inst-gcc/15.2.0/include/c++/15.2.0/cstdio:47,
                 from 
/gnu-inst-gcc/15.2.0/include/c++/15.2.0/ext/string_conversions.h:47,
                 from 
/gnu-inst-gcc/15.2.0/include/c++/15.2.0/bits/basic_string.h:4444,
                 from /gnu-inst-gcc/15.2.0/include/c++/15.2.0/string:56,
                 from 
/gnu-inst-gcc/15.2.0/include/c++/15.2.0/bits/locale_classes.h:42,
                 from 
/gnu-inst-gcc/15.2.0/include/c++/15.2.0/bits/ios_base.h:43,
                 from /gnu-inst-gcc/15.2.0/include/c++/15.2.0/ios:46,
                 from /gnu-inst-gcc/15.2.0/include/c++/15.2.0/bits/ostream.h:43,
                 from /gnu-inst-gcc/15.2.0/include/c++/15.2.0/ostream:42,
                 from /gnu-inst-gcc/15.2.0/include/c++/15.2.0/iostream:43,
                 from ../../gltests/test-nullptr-c++.cc:25:
/gnu-inst-gcc/15.2.0/include/c++/15.2.0/bits/basic_string.tcc:1032:5: error: 
template-id ‘rpl_getline<>’ for ‘std::basic_istream<char>& 
std::rpl_getline(basic_istream<char>&, string&)’ does not match any template 
declaration
 1032 |     getline(basic_istream<char>&, string&);
      |     ^~~~~~~

The cause is a std::basic_istream::getline member function, that
is visible in <string> or <istream>.

The usual workaround to #include <string> and/or #include <istream>
does not work here, because some definitions from <stdio.h> would not
be available.

So, the fix is to define getline as an inline function rather than as a
macro. But additionally, this gets complicated by an inline function in
glibc's <bits/stdio.h>:

__STDIO_INLINE __ssize_t
getline (char **__lineptr, size_t *__n, FILE *__stream)
{
  return __getdelim (__lineptr, __n, '\n', __stream);
}

We can't override an inline function with an inline function. The
workaround therefore gets complicated:


2025-10-13  Bruno Haible  <[email protected]>

        getline: Fix compilation error in C++ mode (regression 2025-10-10).
        * lib/stdio.in.h (getdelim): On glibc, define __getdelim instead of
        rpl_getdelim.
        (getline): In C++ mode, define getline as an inline function instead of
        as a macro.
        * lib/getdelim.c (getdelim): On glibc, don't test whether fp is NULL.

diff --git a/lib/stdio.in.h b/lib/stdio.in.h
index f3ab7969d1..2c70bc049f 100644
--- a/lib/stdio.in.h
+++ b/lib/stdio.in.h
@@ -1044,6 +1044,11 @@ _GL_CXXALIASWARN (getchar);
 #   undef getdelim
 #   define getdelim rpl_getdelim
 #  endif
+#  if __GLIBC__ >= 2
+/* Arrange for the inline definition of getline() in <bits/stdio.h>
+   to call our getdelim() override.  */
+#   define rpl_getdelim __getdelim
+#  endif
 _GL_FUNCDECL_RPL (getdelim, ssize_t,
                   (char **restrict lineptr, size_t *restrict linesize,
                    int delimiter,
@@ -1085,14 +1090,27 @@ _GL_WARN_ON_USE (getdelim, "getdelim is unportable - "
    Return the number of bytes read and stored at *LINEPTR (not including the
    NUL terminator), or -1 on error or EOF.  */
 # if @REPLACE_GETLINE@
-#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
-#   undef getline
-#   define getline rpl_getline
-#  endif
 _GL_FUNCDECL_RPL (getline, ssize_t,
                   (char **restrict lineptr, size_t *restrict linesize,
                    FILE *restrict stream),
                   _GL_ARG_NONNULL ((1, 2, 3)) _GL_ATTRIBUTE_NODISCARD);
+#  if defined __cplusplus
+/* The C++ standard library defines std::basic_istream::getline in <istream>
+   or <string>.  */
+#   if !(__GLIBC__ >= 2)
+extern "C" {
+inline ssize_t
+getline (char **restrict lineptr, size_t *restrict linesize,
+         FILE *restrict stream)
+{
+  return rpl_getline (lineptr, linesize, stream);
+}
+}
+#   endif
+#  else
+#   undef getline
+#   define getline rpl_getline
+#  endif
 _GL_CXXALIAS_RPL (getline, ssize_t,
                   (char **restrict lineptr, size_t *restrict linesize,
                    FILE *restrict stream));
diff --git a/lib/getdelim.c b/lib/getdelim.c
index 2576d376f0..4cd5eca189 100644
--- a/lib/getdelim.c
+++ b/lib/getdelim.c
@@ -65,7 +65,13 @@ getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
   ssize_t result;
   size_t cur_len = 0;

-  if (lineptr == NULL || n == NULL || fp == NULL)
+  if (lineptr == NULL || n == NULL
+      /* glibc already declares this function as __nonnull ((4)).
+         Avoid a gcc warning "‘nonnull’ argument ‘fp’ compared to NULL".  */
+#if !(__GLIBC__ >= 2)
+      || fp == NULL
+#endif
+     )
     {
       errno = EINVAL;
       return -1;




Reply via email to