Alejandro Colomar wrote:
>       # define countof(a)  (sizeof(a) / sizeof((a)[0]) + must_be(is_array(a)))
>       #endif
> 
> must_be(is_array(a)) is a trick to assert that the argument is an array.
> <https://stackoverflow.com/questions/37538/how-do-i-determine-the-size-of-my-array-in-c/57537491#57537491>

Now that is very nice! The ability to detect wrong use of function parameters
is valuable already now. You referenced <https://lkml.org/lkml/2015/9/3/428>.

I'm therefore adding a module 'stdcountof-h' already now; see below.

> > While I understand this, it still means that gnulib — which strives to make
> > the same code usable in C++ like in C — will probably have to override
> > <stdcountof.h> roughly like this:
> > 
> >   #ifdef __cplusplus
> >   template <typename T, size_t N>
> >   constexpr size_t countof(T const (&)[N]) {
> >     return N;
> >   }
> >   #else
> >   # define countof _Countof
> >   #endif
> 
> You'll need to take into account the case where _Countof is not
> available, and fall back to sizeof division

Yeah. In the end I ended up not using _Countof at all, because when
compilers will have _Countof they will most likely also have <stdcountof.h>.

>       #ifdef __cplusplus
>       template <typename T, size_t N>
>       constexpr size_t countof(T const (&)[N]) {
>         return N;
>       }

It turns out it's not that easy: Older C++ compilers reject function
parameters of type   T (&) [N]. So, I've used the same approach for
C++ as for C. [As usual, C++ is a big time sink.]

> testsuite that I added in GCC for the operator.
> 
>       $ find gcc/testsuite/ | grep countof
>       gcc/testsuite/gcc.dg/countof-pedantic.c
>       gcc/testsuite/gcc.dg/countof-zero.c
>       gcc/testsuite/gcc.dg/countof-no-compat.c
>       gcc/testsuite/gcc.dg/countof-compat.c
>       gcc/testsuite/gcc.dg/countof-vla.c
>       gcc/testsuite/gcc.dg/countof-vmt.c
>       gcc/testsuite/gcc.dg/countof.c
>       gcc/testsuite/gcc.dg/countof-compile.c
>       gcc/testsuite/gcc.dg/countof-pedantic-errors.c
>       gcc/testsuite/gcc.dg/countof-stdcountof.c
>       gcc/testsuite/gcc.dg/countof-zero-compile.c

There's no test case for countof(nullptr) in there. That's not a bug,
just not 100% test coverage.


2025-06-02  Bruno Haible  <br...@clisp.org>

        stdcountof-h: Add tests.
        * tests/test-stdcountof-h.c: New file.
        * tests/test-stdcountof-h-c++.cc: New file.
        * modules/stdcountof-h-tests: New file.
        * modules/stdcountof-h-c++-tests: New file.

        stdcountof-h: New module.
        * lib/stdcountof.in.h: New file.
        * m4/stdcountof_h.m4: New file.
        * modules/stdcountof-h: New file.


>From 8617124d257924483e6a9784c3fa839b9ca510ee Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 2 Jun 2025 23:42:17 +0200
Subject: [PATCH 1/2] stdcountof-h: New module.

* lib/stdcountof.in.h: New file.
* m4/stdcountof_h.m4: New file.
* modules/stdcountof-h: New file.
---
 ChangeLog            |  7 ++++
 lib/stdcountof.in.h  | 95 ++++++++++++++++++++++++++++++++++++++++++++
 m4/stdcountof_h.m4   | 24 +++++++++++
 modules/stdcountof-h | 39 ++++++++++++++++++
 4 files changed, 165 insertions(+)
 create mode 100644 lib/stdcountof.in.h
 create mode 100644 m4/stdcountof_h.m4
 create mode 100644 modules/stdcountof-h

diff --git a/ChangeLog b/ChangeLog
index a1d88c839d..3c05726d95 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2025-06-02  Bruno Haible  <br...@clisp.org>
+
+	stdcountof-h: New module.
+	* lib/stdcountof.in.h: New file.
+	* m4/stdcountof_h.m4: New file.
+	* modules/stdcountof-h: New file.
+
 2025-06-02  Bruno Haible  <br...@clisp.org>
 
 	stddef-h: Fix compilation errors in C++ mode (regression 2025-05-27).
diff --git a/lib/stdcountof.in.h b/lib/stdcountof.in.h
new file mode 100644
index 0000000000..3bbb78390a
--- /dev/null
+++ b/lib/stdcountof.in.h
@@ -0,0 +1,95 @@
+/* Copyright 2025 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published
+   by the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   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/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2025.  */
+
+#ifndef _GL_STDCOUNTOF_H
+#define _GL_STDCOUNTOF_H
+
+#if defined __cplusplus
+/* Get size_t.  */
+# include <stddef.h>
+#endif
+
+/* Returns the number of elements of the array A, as a value of type size_t.
+   Example declarations of arrays:
+     extern int a[];
+     extern int a[10];
+     static int a[10][20];
+     void func () { int a[10]; ... }
+   Attempts to produce an error if A is a pointer, e.g. in
+     void func (int a[10]) { ... }
+ */
+#define countof(a) \
+  (sizeof (a) / sizeof (a[0]) + 0 * _gl_verify_is_array (a))
+
+/* Attempts to verify that A is an array.  */
+#if defined __cplusplus
+/* Borrowed from verify.h.  */
+# if !GNULIB_defined_struct__gl_verify_type
+template <int w>
+  struct _gl_verify_type {
+    unsigned int _gl_verify_error_if_negative: w;
+  };
+#  define GNULIB_defined_struct__gl_verify_type 1
+# endif
+# if __cplusplus >= 201103L
+#  if 1
+  /* Use decltype.  */
+/* Default case.  */
+template <typename T>
+  struct _gl_array_type_test { static const int is_array = -1; };
+/* Unbounded arrays.  */
+template <typename T>
+  struct _gl_array_type_test<T[]> { static const int is_array = 1; };
+/* Bounded arrays.  */
+template <typename T, size_t N>
+  struct _gl_array_type_test<T[N]> { static const int is_array = 1; };
+#   define _gl_verify_is_array(a) \
+     sizeof (_gl_verify_type<_gl_array_type_test<decltype(a)>::is_array>)
+#  else
+  /* Use template argument deduction.
+     Use sizeof to get a constant expression from an unknown type.  */
+/* Default case.  */
+template <typename T>
+  struct _gl_array_type_test { double large; };
+/* Unbounded arrays.  */
+template <typename T>
+  struct _gl_array_type_test<T[]> { char small; };
+/* Bounded arrays.  */
+template <typename T, size_t N>
+  struct _gl_array_type_test<T[N]> { char small; };
+/* The T& parameter is essential here: it prevents decay (array-to-pointer
+   conversion).  */
+template <typename T> _gl_array_type_test<T> _gl_array_type_test_helper(T&);
+#   define _gl_verify_is_array(a) \
+     sizeof (_gl_verify_type<(sizeof (_gl_array_type_test_helper(a)) < sizeof (double) ? 1 : -1)>)
+#  endif
+# else
+/* The compiler does not have the necessary functionality.  */
+#  define _gl_verify_is_array(a) 0
+# endif
+#else
+/* In C, we can use typeof and __builtin_types_compatible_p.  */
+# if _GL_GNUC_PREREQ (3, 1) || defined __clang__
+#  define _gl_verify_is_array(a) \
+    sizeof (struct { unsigned int _gl_verify_error_if_negative : __builtin_types_compatible_p (typeof (a), typeof (&*(a))) ? -1 : 1; })
+# else
+/* The compiler does not have the necessary built-ins.  */
+#  define _gl_verify_is_array(a) 0
+# endif
+#endif
+
+#endif /* _GL_STDCOUNTOF_H */
diff --git a/m4/stdcountof_h.m4 b/m4/stdcountof_h.m4
new file mode 100644
index 0000000000..23b31c10fb
--- /dev/null
+++ b/m4/stdcountof_h.m4
@@ -0,0 +1,24 @@
+# stdcountof_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.
+
+AC_DEFUN_ONCE([gl_STDCOUNTOF_H],
+[
+  AC_CHECK_HEADERS_ONCE([stdcountof.h])
+  if test $ac_cv_header_stdcountof_h = yes; then
+    HAVE_STDCOUNTOF_H=1
+  else
+    HAVE_STDCOUNTOF_H=0
+  fi
+  AC_SUBST([HAVE_STDCOUNTOF_H])
+
+  if test $HAVE_STDCOUNTOF_H = 1; then
+    GL_GENERATE_STDCOUNTOF_H=false
+  else
+    GL_GENERATE_STDCOUNTOF_H=true
+  fi
+])
diff --git a/modules/stdcountof-h b/modules/stdcountof-h
new file mode 100644
index 0000000000..1408e5ea2b
--- /dev/null
+++ b/modules/stdcountof-h
@@ -0,0 +1,39 @@
+Description:
+An <stdcountof.h> that is like C23.
+
+Files:
+lib/stdcountof.in.h
+m4/stdcountof_h.m4
+
+Depends-on:
+gen-header
+
+configure.ac:
+gl_STDCOUNTOF_H
+gl_CONDITIONAL_HEADER([stdcountof.h])
+AC_PROG_MKDIR_P
+
+Makefile.am:
+BUILT_SOURCES += $(STDCOUNTOF_H)
+
+# We need the following in order to create <stdcountof.h> when the system
+# doesn't have one that works with the given compiler.
+if GL_GENERATE_STDCOUNTOF_H
+stdcountof.h: stdcountof.in.h $(top_builddir)/config.status
+@NMD@	$(AM_V_GEN)$(MKDIR_P) '%reldir%'
+	$(gl_V_at)$(SED_HEADER_STDOUT) $(srcdir)/stdcountof.in.h > $@-t
+	$(AM_V_at)mv $@-t $@
+else
+stdcountof.h: $(top_builddir)/config.status
+	rm -f $@
+endif
+MOSTLYCLEANFILES += stdcountof.h stdcountof.h-t
+
+Include:
+<stdcountof.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+Bruno Haible
-- 
2.43.0

>From 3b239ab56fd3b79acf1f1326c4fe7383362fc99d Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 2 Jun 2025 23:44:55 +0200
Subject: [PATCH 2/2] stdcountof-h: Add tests.

* tests/test-stdcountof-h.c: New file.
* tests/test-stdcountof-h-c++.cc: New file.
* modules/stdcountof-h-tests: New file.
* modules/stdcountof-h-c++-tests: New file.
---
 ChangeLog                      |  6 ++++
 modules/stdcountof-h-c++-tests | 17 ++++++++++
 modules/stdcountof-h-tests     | 12 +++++++
 tests/test-stdcountof-h-c++.cc | 17 ++++++++++
 tests/test-stdcountof-h.c      | 60 ++++++++++++++++++++++++++++++++++
 5 files changed, 112 insertions(+)
 create mode 100644 modules/stdcountof-h-c++-tests
 create mode 100644 modules/stdcountof-h-tests
 create mode 100644 tests/test-stdcountof-h-c++.cc
 create mode 100644 tests/test-stdcountof-h.c

diff --git a/ChangeLog b/ChangeLog
index 3c05726d95..7f50173924 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2025-06-02  Bruno Haible  <br...@clisp.org>
 
+	stdcountof-h: Add tests.
+	* tests/test-stdcountof-h.c: New file.
+	* tests/test-stdcountof-h-c++.cc: New file.
+	* modules/stdcountof-h-tests: New file.
+	* modules/stdcountof-h-c++-tests: New file.
+
 	stdcountof-h: New module.
 	* lib/stdcountof.in.h: New file.
 	* m4/stdcountof_h.m4: New file.
diff --git a/modules/stdcountof-h-c++-tests b/modules/stdcountof-h-c++-tests
new file mode 100644
index 0000000000..e0d2638cfe
--- /dev/null
+++ b/modules/stdcountof-h-c++-tests
@@ -0,0 +1,17 @@
+Files:
+tests/test-stdcountof-h-c++.cc
+
+Status:
+c++-test
+
+Depends-on:
+ansi-c++-opt
+
+configure.ac:
+
+Makefile.am:
+if ANSICXX
+TESTS += test-stdcountof-h-c++
+check_PROGRAMS += test-stdcountof-h-c++
+test_stdcountof_h_c___SOURCES = test-stdcountof-h-c++.cc
+endif
diff --git a/modules/stdcountof-h-tests b/modules/stdcountof-h-tests
new file mode 100644
index 0000000000..8024a999a9
--- /dev/null
+++ b/modules/stdcountof-h-tests
@@ -0,0 +1,12 @@
+Files:
+tests/test-stdcountof-h.c
+tests/macros.h
+
+Depends-on:
+stdcountof-h-c++-tests
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-stdcountof-h
+check_PROGRAMS += test-stdcountof-h
diff --git a/tests/test-stdcountof-h-c++.cc b/tests/test-stdcountof-h-c++.cc
new file mode 100644
index 0000000000..55ef7e6818
--- /dev/null
+++ b/tests/test-stdcountof-h-c++.cc
@@ -0,0 +1,17 @@
+/* Test <stdcountof.h> in C++ mode.
+   Copyright 2025 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include "test-stdcountof-h.c"
diff --git a/tests/test-stdcountof-h.c b/tests/test-stdcountof-h.c
new file mode 100644
index 0000000000..619a666186
--- /dev/null
+++ b/tests/test-stdcountof-h.c
@@ -0,0 +1,60 @@
+/* Test <stdcountof.h>.
+   Copyright 2025 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <stdcountof.h>
+
+#include "macros.h"
+
+extern int integer;
+extern int unbounded[];
+extern int bounded[10];
+extern int multidimensional[10][20];
+
+static void
+test_func (int parameter[3])
+{
+  int local_bounded[20];
+
+  (void) local_bounded;
+
+  (void) _gl_verify_is_array (unbounded);
+  (void) _gl_verify_is_array (bounded);
+  (void) _gl_verify_is_array (multidimensional);
+
+  ASSERT (countof (bounded) == 10);
+  ASSERT (countof (multidimensional) == 10);
+  ASSERT (countof (local_bounded) == 20);
+
+#if 0 /* These produce compilation errors.  */
+  (void) _gl_verify_is_array (integer);
+  (void) _gl_verify_is_array (parameter);
+
+  ASSERT (countof (integer) >= 0);
+  ASSERT (countof (unbounded) >= 0);
+#endif
+}
+
+int
+main ()
+{
+  int x[3];
+
+  test_func (x);
+
+  return test_exit_status;
+}
-- 
2.43.0

Reply via email to