Hi Bruno,

Bruno Haible <br...@clisp.org> writes:

> I agree that when the system has a working implementation of a .h file,
> we should interfere with it to the least possible amount.
>
> But the problem here is
>   1) The term "working implementation" depends on the language in effect.
>      It may be working C and not working in C++ — that is the case that
>      Collin observed with GCC 15.
>      Or it may be working in C++ and not working in C — that is another
>      case.
>   2) The standards may extend the expected contents of <stdckdint.h>
>      in the future. In that case, our current dichotomy of "generate
>      the file" or "not generate the file" would break down as well.
>
> I would therefore propose a more future-proof approach: based on the same
> approach as we have been taken for so many other .h files.
>
>   1) In lib/stdckdint.in.h include the system's .h file if it exists,
>      via include_next. (Fortunately all known compilers that have 2 copies
>      of stdckdint.h support include_next. The fallback with
>        #include "absolute file name of stdckdint.h"
>      will work with other compilers.)
>   2) Parameterize this file with 4 AC_SUBSTed variables:
>      - one that tells whether the C compiler has <stdckdint.h>,
>      - one that tells whether the <stdckdint.h> seen by the C compiler
>        is complete (in the sense that it provides all that the GNU
>        replacement would do),
>      - one that tells whether the C++ compiler has <stdckdint.h>,
>      - one that tells whether the <stdckdint.h> seen by the C++ compiler
>        is complete.
>   3) In the .m4 code, make two C compiler invocations to determine the
>      first two of these values and - if $CXX != no - two C++ compiler
>      invocations to determine the other two values.
>   4) Determine GL_GENERATE_STDCKDINT_H based on these 4 values.
>
> This way, we would be prepared for changes in the standards, bugs in
> specific compilers, and handle C++ independently from C (which seems
> to be required in this case).

Can you check the attached patch? I think it matches what you said
above.

With GCC 15 and C++26 the result is (*):

    $ ./configure CFLAGS='-std=gnu99' CXXFLAGS='-std=gnu++26' | grep stdckdint
    checking for stdckdint.h... yes
    checking whether stdckdint.h can be included in C... yes
    checking whether stdckdint.h defines ckd_add, ckd_sub, ckd_mul in C... yes
    checking whether stdckdint.h can be included in C++... yes
    checking whether stdckdint.h defines ckd_add, ckd_sub, ckd_mul in C++... yes
    $ make
    $ ls gllib/stdckdint.h
    ls: cannot access 'gllib/stdckdint.h': No such file or directory

And with GCC 15 and C++14:

    $ ./configure CFLAGS='-std=gnu99' CXXFLAGS='-std=gnu++14' | grep stdckdint
    checking for stdckdint.h... yes
    checking whether stdckdint.h can be included in C... yes
    checking whether stdckdint.h defines ckd_add, ckd_sub, ckd_mul in C... yes
    checking whether stdckdint.h can be included in C++... yes
    checking whether stdckdint.h defines ckd_add, ckd_sub, ckd_mul in C++... no
    $ make
    $ ls gllib/stdckdint.h
    gllib/stdckdint.h

In the second case, when compiling a C program <stdckdint.h> simply does
an #include_next.

Collin

(*) I find it strange that the C++ declarations are hidden depending on
the C++ standard, but the C ones are not. I would expect -std=(gnu|c)99
to disable them like the C++ version.

>From 97dbb1f1a349c4817e91ed2b5fcc5c30b484360b Mon Sep 17 00:00:00 2001
Message-ID: <97dbb1f1a349c4817e91ed2b5fcc5c30b484360b.1747882664.git.collin.fu...@gmail.com>
From: Collin Funk <collin.fu...@gmail.com>
Date: Wed, 21 May 2025 19:55:47 -0700
Subject: [PATCH] stdckdint-h: Don't generate header if it is not needed.

Suggested by Paul Eggert in:
<https://lists.gnu.org/archive/html/bug-gnulib/2025-05/msg00216.html>.

* m4/stdckdint_h.m4: New file.
* modules/stdckdint-h (Files): Add m4/stdckdint_h.m4.
(configure.ac): Remove checks and just invoke gl_STDCKDINT_H.
(Makefile.am): Replace variables set by ./configure.
* lib/stdckdint.in.h: Include the compilers header if it exists.
(ckd_add, ckd_sub, ckd_mul): Only define if the compilers definitions do
not work.
---
 ChangeLog           |  13 +++++
 lib/stdckdint.in.h  |  31 +++++++---
 m4/stdckdint_h.m4   | 136 ++++++++++++++++++++++++++++++++++++++++++++
 modules/stdckdint-h |  21 +++----
 4 files changed, 184 insertions(+), 17 deletions(-)
 create mode 100644 m4/stdckdint_h.m4

diff --git a/ChangeLog b/ChangeLog
index b85c7c6e6e..f946fc5d91 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2025-05-21  Collin Funk  <collin.fu...@gmail.com>
+
+	stdckdint-h: Don't generate header if it is not needed.
+	Suggested by Paul Eggert in:
+	<https://lists.gnu.org/archive/html/bug-gnulib/2025-05/msg00216.html>.
+	* m4/stdckdint_h.m4: New file.
+	* modules/stdckdint-h (Files): Add m4/stdckdint_h.m4.
+	(configure.ac): Remove checks and just invoke gl_STDCKDINT_H.
+	(Makefile.am): Replace variables set by ./configure.
+	* lib/stdckdint.in.h: Include the compilers header if it exists.
+	(ckd_add, ckd_sub, ckd_mul): Only define if the compilers definitions do
+	not work.
+
 2025-05-19  Collin Funk  <collin.fu...@gmail.com>
 
 	stdckdint-h: Work around missing declarations with g++ 15.0.
diff --git a/lib/stdckdint.in.h b/lib/stdckdint.in.h
index 83277b728e..2d317b9588 100644
--- a/lib/stdckdint.in.h
+++ b/lib/stdckdint.in.h
@@ -15,10 +15,26 @@
    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-#ifndef _GL_STDCKDINT_H
-#define _GL_STDCKDINT_H
+#ifndef _@GUARD_PREFIX@_STDCKDINT_H
 
-#include "intprops-internal.h"
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+#if defined __cplusplus ? @HAVE_CXX_STDCKDINT_H@ : @HAVE_C_STDCKDINT_H@
+#@INCLUDE_NEXT@ @NEXT_STDCKDINT_H@
+#endif
+
+/* Do nothing but include the system header if it works properly.  */
+#if defined __cplusplus ? @HAVE_WORKING_CXX_STDCKDINT_H@ : @HAVE_WORKING_C_STDCKDINT_H@
+
+/* Avoid redefining macros.  */
+# undef ckd_add
+# undef ckd_sub
+# undef ckd_mul
+
+# include "intprops-internal.h"
 
 /* Store into *R the low-order bits of A + B, A - B, A * B, respectively.
    Return 1 if the result overflows, 0 otherwise.
@@ -28,8 +44,9 @@
    These are like the standard macros introduced in C23, except that
    arguments should not have side effects.  */
 
-#define ckd_add(r, a, b) ((bool) _GL_INT_ADD_WRAPV (a, b, r))
-#define ckd_sub(r, a, b) ((bool) _GL_INT_SUBTRACT_WRAPV (a, b, r))
-#define ckd_mul(r, a, b) ((bool) _GL_INT_MULTIPLY_WRAPV (a, b, r))
+# define ckd_add(r, a, b) ((bool) _GL_INT_ADD_WRAPV (a, b, r))
+# define ckd_sub(r, a, b) ((bool) _GL_INT_SUBTRACT_WRAPV (a, b, r))
+# define ckd_mul(r, a, b) ((bool) _GL_INT_MULTIPLY_WRAPV (a, b, r))
 
-#endif /* _GL_STDCKDINT_H */
+#endif /* defined __cplusplus ? @HAVE_WORKING_CXX_STDCKDINT_H@ : @HAVE_WORKING_C_STDCKDINT_H@ */
+#endif /* _@GUARD_PREFIX@_STDCKDINT_H */
diff --git a/m4/stdckdint_h.m4 b/m4/stdckdint_h.m4
new file mode 100644
index 0000000000..f8e99a1920
--- /dev/null
+++ b/m4/stdckdint_h.m4
@@ -0,0 +1,136 @@
+# stdckdint_h.m4
+# serial 1
+dnl Copyright 2025 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl This file is offered as-is, without any warranty.
+
+dnl Written by Collin Funk.
+
+AC_DEFUN_ONCE([gl_STDCKDINT_H],
+[
+  gl_CHECK_NEXT_HEADERS([stdckdint.h])
+  if test $ac_cv_header_stdckdint_h = yes; then
+    HAVE_STDCKDINT_H=1
+  else
+    HAVE_STDCKDINT_H=0
+  fi
+  AC_SUBST([HAVE_STDCKDINT_H])
+
+  if test $HAVE_STDCKDINT_H == 1; then
+    AC_CACHE_CHECK([whether stdckdint.h can be included in C],
+      [gl_cv_header_c_stdckdint_h],
+      [AC_COMPILE_IFELSE(
+         [AC_LANG_PROGRAM(
+            [[#include <stdckdint.h>
+            ]])],
+         [gl_cv_header_c_stdckdint_h=yes],
+         [gl_cv_header_c_stdckdint_h=no])])
+    if test $gl_cv_header_c_stdckdint_h = yes; then
+      HAVE_C_STDCKDINT_H=1
+      AC_CACHE_CHECK([whether stdckdint.h defines ckd_add, ckd_sub, ckd_mul in C],
+        [gl_cv_header_c_stdckdint_h_works],
+        [AC_COMPILE_IFELSE(
+           [AC_LANG_PROGRAM(
+              [[#include <stdckdint.h>
+              ]],
+              [[int r;
+                int a = 1;
+                int b = 1;
+                return !!(ckd_add (&r, a, b) || ckd_sub (&r, a, b)
+                          || ckd_mul (&r, a, b));
+              ]])],
+           [gl_cv_header_c_stdckdint_h_works=yes],
+           [gl_cv_header_c_stdckdint_h_works=no])])
+      if test $gl_cv_header_c_stdckdint_h_works = yes; then
+        HAVE_WORKING_C_STDCKDINT_H=1
+      else
+        HAVE_WORKING_C_STDCKDINT_H=0
+      fi
+    else
+      HAVE_C_STDCKDINT_H=0
+      HAVE_WORKING_C_STDCKDINT_H=0
+    fi
+    if test "$CXX" != no; then
+      AC_CACHE_CHECK([whether stdckdint.h can be included in C++],
+        [gl_cv_header_cxx_stdckdint_h],
+        [dnl We can't use AC_LANG_PUSH([C++]) and AC_LANG_POP([C++]) here, due to
+         dnl an autoconf bug <https://savannah.gnu.org/support/?110294>.
+         cat > conftest.cpp <<\EOF
+#include <stdckdint.h>
+EOF
+         gl_command="$CXX $CXXFLAGS $CPPFLAGS -c conftest.cpp"
+         if AC_TRY_EVAL([gl_command]); then
+           gl_cv_header_cxx_stdckdint_h=yes
+         else
+           gl_cv_header_cxx_stdckdint_h=no
+         fi
+         rm -fr conftest*
+        ])
+      if test $gl_cv_header_cxx_stdckdint_h = yes; then
+        HAVE_CXX_STDCKDINT_H=1
+        AC_CACHE_CHECK([whether stdckdint.h defines ckd_add, ckd_sub, ckd_mul in C++],
+          [gl_cv_header_cxx_stdckdint_h_works],
+          [dnl We can't use AC_LANG_PUSH([C++]) and AC_LANG_POP([C++]) here, due to
+           dnl an autoconf bug <https://savannah.gnu.org/support/?110294>.
+           cat > conftest.cpp <<\EOF
+#include <stdckdint.h>
+int
+main (void)
+{
+  int r;
+  int a = 1;
+  int b = 1;
+  return !!(ckd_add (&r, a, b) || ckd_sub (&r, a, b) || ckd_mul (&r, a, b));
+}
+EOF
+           gl_command="$CXX $CXXFLAGS $CPPFLAGS -c conftest.cpp"
+           if AC_TRY_EVAL([gl_command]); then
+             gl_cv_header_cxx_stdckdint_h_works=yes
+           else
+             gl_cv_header_cxx_stdckdint_h_works=no
+           fi
+           rm -fr conftest*
+          ])
+        if test $gl_cv_header_cxx_stdckdint_h_works = yes; then
+          HAVE_WORKING_CXX_STDCKDINT_H=1
+        else
+          HAVE_WORKING_CXX_STDCKDINT_H=0
+        fi
+      else
+        HAVE_CXX_STDCKDINT_H=0
+        HAVE_WORKING_CXX_STDCKDINT_H=0
+      fi
+    fi
+  else
+    HAVE_C_STDCKDINT_H=0
+    HAVE_WORKING_C_STDCKDINT_H=0
+    HAVE_CXX_STDCKDINT_H=0
+    HAVE_WORKING_CXX_STDCKDINT_H=0
+  fi
+  AC_SUBST([HAVE_C_STDCKDINT_H])
+  AC_SUBST([HAVE_WORKING_C_STDCKDINT_H])
+  AC_SUBST([HAVE_CXX_STDCKDINT_H])
+  AC_SUBST([HAVE_WORKING_CXX_STDCKDINT_H])
+
+  if test "$CXX" != no; then
+    dnl We might need the header for C or C++.
+    if test $HAVE_C_STDCKDINT_H = 1 \
+       && test $HAVE_WORKING_C_STDCKDINT_H = 1 \
+       && test $HAVE_CXX_STDCKDINT_H = 1 \
+       && test $HAVE_WORKING_CXX_STDCKDINT_H = 1; then
+      GL_GENERATE_STDCKDINT_H=false
+    else
+      GL_GENERATE_STDCKDINT_H=true
+    fi
+  else
+    dnl We don't care about C++ here.
+    if test $HAVE_C_STDCKDINT_H = 1 \
+       && test $HAVE_WORKING_C_STDCKDINT_H = 1; then
+      GL_GENERATE_STDCKDINT_H=false
+    else
+      GL_GENERATE_STDCKDINT_H=true
+    fi
+  fi
+])
diff --git a/modules/stdckdint-h b/modules/stdckdint-h
index ff777d8d62..7f7c612bce 100644
--- a/modules/stdckdint-h
+++ b/modules/stdckdint-h
@@ -2,6 +2,7 @@ Description:
 An <stdckdint.h> that is like C23.
 
 Files:
+m4/stdckdint_h.m4
 lib/stdckdint.in.h
 lib/intprops-internal.h
 
@@ -10,16 +11,7 @@ gen-header
 bool
 
 configure.ac:
-AC_CHECK_HEADERS_ONCE([stdckdint.h])
-if test $ac_cv_header_stdckdint_h = yes; then
-  if test -n "$CXX" && test "$CXX" != no; then
-    GL_GENERATE_STDCKDINT_H=true
-  else
-    GL_GENERATE_STDCKDINT_H=false
-  fi
-else
-  GL_GENERATE_STDCKDINT_H=true
-fi
+gl_STDCKDINT_H
 gl_CONDITIONAL_HEADER([stdckdint.h])
 AC_PROG_MKDIR_P
 
@@ -32,6 +24,15 @@ if GL_GENERATE_STDCKDINT_H
 stdckdint.h: stdckdint.in.h $(top_builddir)/config.status
 @NMD@	$(AM_V_GEN)$(MKDIR_P) '%reldir%'
 	$(gl_V_at)$(SED_HEADER_STDOUT) \
+	      -e 's|@''GUARD_PREFIX''@|${gl_include_guard_prefix}|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_STDCKDINT_H''@|$(NEXT_STDCKDINT_H)|g' \
+	      -e 's|@''HAVE_C_STDCKDINT_H''@|$(HAVE_C_STDCKDINT_H)|g' \
+	      -e 's|@''HAVE_WORKING_C_STDCKDINT_H''@|$(HAVE_WORKING_C_STDCKDINT_H)|g' \
+	      -e 's|@''HAVE_CXX_STDCKDINT_H''@|$(HAVE_CXX_STDCKDINT_H)|g' \
+	      -e 's|@''HAVE_WORKING_CXX_STDCKDINT_H''@|$(HAVE_WORKING_CXX_STDCKDINT_H)|g' \
 	  $(srcdir)/stdckdint.in.h > $@-t
 	$(AM_V_at)mv $@-t $@
 else
-- 
2.49.0

Reply via email to