Using array_length macro outside function
Commit Message
* TAMUKI Shoichi:
> [1] to calculate the number of elements in the array.
>
> [1] https://sourceware.org/ml/libc-alpha/2017-10/msg01054.html
>
> Now, I tried to further improve this as patched below;
>
> ----------------------------------------------------------------------
> diff --git a/time/tst-strftime2.c b/time/tst-strftime2.c
> index 57d2144..6c2d359 100644
> --- a/time/tst-strftime2.c
> +++ b/time/tst-strftime2.c
> @@ -25,8 +25,10 @@
> #include <string.h>
>
> static const char *locales[] = { "ja_JP.UTF-8", "lo_LA.UTF-8", "th_TH.UTF-8" };
> +#define nlocales array_length (locales)
> +static char ref[nlocales][nformats][ndates][100];
The other problem is that array_length (locales) is not a constant
expression because a statement expression is never a constant
expression. I had not considered these problems when I wrote the
array_length macro.
I would like to retain the verification that the macro argument is not a
pointer. What do you think about the patch below?
Thanks,
Florian
array_length: Make usable as a constant expression
Do not use a statement expression and _Static_assert in array_length,
so that array_length can be used at file scope and as a constant
expression.
The compiler error message looks like this, so this is still
acceptable:
t.c:3:28: error: zero width for bit-field ‘ARGUMENT_NOT_AN_ARRAY’
0 * sizeof (struct { int ARGUMENT_NOT_AN_ARRAY: \
^~~~~~~~~~~~~~~~~~~~~
t.c:10:7: note: in expansion of macro ‘array_length’
int b[array_length (c) + 1];
^~~~~~~~~~~~
2019-02-04 Florian Weimer <fweimer@redhat.com>
* include/array_length.h (array_length): Do not use a statement
expression and _Static_assert, so that array_length can be used at
file scope and as a constant expression.
Comments
On Mon, 4 Feb 2019, Florian Weimer wrote:
> Do not use a statement expression and _Static_assert in array_length,
> so that array_length can be used at file scope and as a constant
> expression.
>
> The compiler error message looks like this, so this is still
> acceptable:
>
> t.c:3:28: error: zero width for bit-field ‘ARGUMENT_NOT_AN_ARRAY’
> 0 * sizeof (struct { int ARGUMENT_NOT_AN_ARRAY: \
You can use _Static_assert inside a struct; you don't need to do things
with bit-field sizes.
On 2/4/19 6:34 AM, Florian Weimer wrote:
> +#define array_length(var) \
> + (sizeof (var) / sizeof ((var)[0]) \
> + + 0 * sizeof (struct { int ARGUMENT_NOT_AN_ARRAY: \
> + 1 - (__builtin_types_compatible_p \
> + (__typeof (var), __typeof (&(var)[0]))); }))
In Gnulib apps we've needed static assertions in expressions so often
that we have a macro 'verify_expr (R, E)' that means "verify R
statically, and yield the value of E". With this macro, you could
rewrite the above definition as:
#define array_length(var) \
verify_expr (!__builtin_types_compatible_p (__typeof (var), \
__typeof (&(var)[0])), \
sizeof (var) / sizeof ((var)[0]))
which is quite a bit easier to follow. So I suggest appending something
like the following to include/verify.h:
#define verify_expr(r, e) \
(sizeof (struct { _Static_assert (r, #r); char __dummy; }) \
? (e) : (e))
Then we can have array_length.h include verify.h and use the
abovementioned simpler array_length.
(The Gnulib verify_expr caters to non-GNU and pre-C99 compilers and so
is more complicated, but we don't need that complexity in glibc.)
Hello Florian-san,
From: Florian Weimer <fweimer@redhat.com>
Subject: Re: Using array_length macro outside function
Date: Mon, 04 Feb 2019 15:34:09 +0100
> The other problem is that array_length (locales) is not a constant
> expression because a statement expression is never a constant
> expression. I had not considered these problems when I wrote the
> array_length macro.
>
> I would like to retain the verification that the macro argument is not a
> pointer. What do you think about the patch below?
Thank you. Applying your patch, I have confirmed that the test can be
compiled successfully.
Regards,
TAMUKI Shoichi
@@ -21,13 +21,11 @@
/* array_length (VAR) is the number of elements in the array VAR. VAR
must evaluate to an array, not a pointer. */
-#define array_length(var) \
- __extension__ ({ \
- _Static_assert (!__builtin_types_compatible_p \
- (__typeof (var), __typeof (&(var)[0])), \
- "argument must be an array"); \
- sizeof (var) / sizeof ((var)[0]); \
- })
+#define array_length(var) \
+ (sizeof (var) / sizeof ((var)[0]) \
+ + 0 * sizeof (struct { int ARGUMENT_NOT_AN_ARRAY: \
+ 1 - (__builtin_types_compatible_p \
+ (__typeof (var), __typeof (&(var)[0]))); }))
/* array_end (VAR) is a pointer one past the end of the array VAR.
VAR must evaluate to an array, not a pointer. */