From c6a86ac78efbec284b444876a9ea2fbbf7e48033 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 17 Feb 2009 18:28:50 +1030 Subject: [PATCH] Finally, ARRAY_SIZE! --- ccan/array_size/_info.c | 41 +++++++++++++++++++ ccan/array_size/array_size.h | 25 +++++++++++ .../test/compile_fail-function-param.c | 21 ++++++++++ ccan/array_size/test/compile_fail.c | 14 +++++++ ccan/array_size/test/run.c | 33 +++++++++++++++ 5 files changed, 134 insertions(+) create mode 100644 ccan/array_size/_info.c create mode 100644 ccan/array_size/array_size.h create mode 100644 ccan/array_size/test/compile_fail-function-param.c create mode 100644 ccan/array_size/test/compile_fail.c create mode 100644 ccan/array_size/test/run.c diff --git a/ccan/array_size/_info.c b/ccan/array_size/_info.c new file mode 100644 index 00000000..794ba785 --- /dev/null +++ b/ccan/array_size/_info.c @@ -0,0 +1,41 @@ +#include +#include +#include "config.h" + +/** + * array_size - routine for safely deriving the size of a visible array. + * + * This provides a simple ARRAY_SIZE() macro, which (given a good compiler) + * will also break compile if you try to use it on a pointer. + * + * This can ensure your code is robust to changes, without needing a gratuitous + * macro or constant. + * + * Example: + * #include + * #include + * + * // We currently use 32 random values. + * static unsigned int vals[32]; + * + * void init_values(void) + * { + * unsigned int i; + * for (i = 0; i < ARRAY_SIZE(vals); i++) + * vals[i] = random(); + * } + * + * Licence: LGPL (2 or any later version) + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/build_assert\n"); + return 0; + } + + return 1; +} diff --git a/ccan/array_size/array_size.h b/ccan/array_size/array_size.h new file mode 100644 index 00000000..c561c0ac --- /dev/null +++ b/ccan/array_size/array_size.h @@ -0,0 +1,25 @@ +#ifndef CCAN_ARRAY_SIZE_H +#define CCAN_ARRAY_SIZE_H +#include "config.h" +#include + +/** + * ARRAY_SIZE - get the number of elements in a visible array + * @arr: the array whose size you want. + * + * This does not work on pointers, or arrays declared as [], or + * function parameters. With correct compiler support, such usage + * will cause a build error (see build_assert). + */ +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr)) + +#if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF +/* Two gcc extensions. + * &a[0] degrades to a pointer: a different type from an array */ +#define _array_size_chk(arr) \ + EXPR_BUILD_ASSERT(!__builtin_types_compatible_p(typeof(arr), \ + typeof(&(arr)[0]))) +#else +#define _array_size_chk(arr) 0 +#endif +#endif /* CCAN_ALIGNOF_H */ diff --git a/ccan/array_size/test/compile_fail-function-param.c b/ccan/array_size/test/compile_fail-function-param.c new file mode 100644 index 00000000..283646f8 --- /dev/null +++ b/ccan/array_size/test/compile_fail-function-param.c @@ -0,0 +1,21 @@ +#include +#include + +struct foo { + unsigned int a, b; +}; + +int check_parameter(const struct foo array[4]); +int check_parameter(const struct foo array[4]) +{ +#ifdef FAIL + return (ARRAY_SIZE(array) == 4); +#else + return sizeof(array) == 4 * sizeof(struct foo); +#endif +} + +int main(int argc, char *argv[]) +{ + return check_parameter(NULL); +} diff --git a/ccan/array_size/test/compile_fail.c b/ccan/array_size/test/compile_fail.c new file mode 100644 index 00000000..9f9ac650 --- /dev/null +++ b/ccan/array_size/test/compile_fail.c @@ -0,0 +1,14 @@ +#include "array_size/array_size.h" + +int main(int argc, char *argv[8]) +{ + char array[100]; +#ifdef FAIL + return ARRAY_SIZE(argv) + ARRAY_SIZE(array); +#if !HAVE_TYPEOF || !HAVE_BUILTIN_TYPES_COMPATIBLE_P +#error "Unfortunately we don't fail if _array_size_chk is a noop." +#endif +#else + return ARRAY_SIZE(array); +#endif +} diff --git a/ccan/array_size/test/run.c b/ccan/array_size/test/run.c new file mode 100644 index 00000000..3a3cdcc0 --- /dev/null +++ b/ccan/array_size/test/run.c @@ -0,0 +1,33 @@ +#include "array_size/array_size.h" +#include "tap/tap.h" + +static char array1[1]; +static int array2[2]; +static unsigned long array3[3][5]; +struct foo { + unsigned int a, b; + char string[100]; +}; +static struct foo array4[4]; + +/* Make sure they can be used in initializers. */ +static int array1_size = ARRAY_SIZE(array1); +static int array2_size = ARRAY_SIZE(array2); +static int array3_size = ARRAY_SIZE(array3); +static int array4_size = ARRAY_SIZE(array4); + +int main(int argc, char *argv[]) +{ + plan_tests(8); + ok1(array1_size == 1); + ok1(array2_size == 2); + ok1(array3_size == 3); + ok1(array4_size == 4); + + ok1(ARRAY_SIZE(array1) == 1); + ok1(ARRAY_SIZE(array2) == 2); + ok1(ARRAY_SIZE(array3) == 3); + ok1(ARRAY_SIZE(array4) == 4); + + return exit_status(); +} -- 2.39.2