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