In C we have pointers to arrays objects and arrays of constant known size.
The NetBSD defines an __arraycount macro, which is defined as #define __arraycount(x) (sizeof(x)/sizeof(x[0]))
.
I wouls like to define my own __arraycount
macro, which will not compile when given a pointer to array. To do that, i need a __is_array_of_constant_known_size
macro.
I would like to have a macro that is a compile time constant which for arrays of constant known size will return true/nonzero and for pointers to array objects (or any other pointers) will return false/zero.
This is what i've come up with:
#if __GNUC__
#define __is_array_of_constant_known_size(x) ( __builtin_constant_p((void*)x == (void*)&x)?1:0 )
#else
#define __is_array_of_constant_known_size(x) ( (void*)&x[0] == (void*)&x )
#endif
// __arraycount may look like this:
#define __arraycount(x) ( sizeof(char[__is_array_of_constant_known_size(x) ? 1 : -1]) ? (sizeof(x)/sizeof(x[0])) : -1 )
// tests:
static char var1[5];
typedef char type1[__is_array_of_constant_known_size(var1)?1:-1];
static const int int1 = __is_array_of_constant_known_size(var1);
static const int sizeof1 = sizeof(char[__is_array_of_constant_known_size(var1)?1:-1]);
static char * var2;
typedef char type2[__is_array_of_constant_known_size(var2)?1:-1]; // should error
static const int int2 = __is_array_of_constant_known_size(var2);
static const int sizeof2 = sizeof(char[__is_array_of_constant_known_size(var2) 1:-1]); // should error
For GNUC compilers (clang6.0.0 and gcc8.1) tests are ok, ie. the lines containing type2
and sizeof2
fail to compile with error: size of array is negative
.
However when GNUC is not defined, then i get:
<source>:8:1: warning: variably modified 'type1' at file scope
typedef char type1[__is_array_of_constant_known_size(var1)?1:-1];
^~~~~~~
<source>:10:21: error: initializer element is not constant
const int sizeof1 = sizeof(char[__is_array_of_constant_known_size(var1)?1:-1]);
^~~~~~
<source>:13:14: error: variably modified 'type2' at file scope
typedef char type2[__is_array_of_constant_known_size(var2)?1:-1];
^~~~~
<source>:4:47: error: initializer element is not constant
#define __is_array_of_constant_known_size(x) ( (void*)&x[0] == (void*)&x )
^
<source>:14:18: note: in expansion of macro '__is_array_of_constant_known_size'
const int int2 = __is_array_of_constant_known_size(var2)?1:-1;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:15:21: error: initializer element is not constant
const int sizeof2 = sizeof(char[__is_array_of_constant_known_size(var2)?1:-1]);
The line int1 = __is_array_of_constant_known_size(var1)
compiles without error. That means, that __is_array_of_constant_known_size(var1)
is a constant expression, because
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals from 6.7.8 in C99.
But the line sizeof1 = sizeof(char[__is_array_of_constant_known_size(var1)])
fails to compile with error: initializer is not compile time constant
. Why is that?
Why (void*)&var1[0] == (void*)&var1
is a constant expression, but sizeof(char[((void*)&var1[0] == (void*)&var1)])
is not?
Is there a better way to implement compile time assertion inside the __arraycount
macro rather then sizeof(char[__is_array_of_constant_known_size(var1)?1:-1])
?