Is there any way to validate on compile time in a c macro that an argument is an array ?
e.g in this two macros:
#define CLEAN_ARRAY(arr) \
do { \
bzero(arr, sizeof(arr)); \
} while (0)
And
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
I tried something using CTC(X)
macro , but couldn't find any way to validate/warn if arr
isn't an array.
Here's a solution in pure C which invokes no undefined behavior:
#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
If you need to ensure that the value is an array (then cause a compile time error if not), you can simply use it as an initializer to an enum statement (or a static variable), like this:
static int __ ## arg ## _is_array = IS_ARRAY(arg); // works for an array, fails for pointer.
I'm not entirely sure what will happen with VLA's, but playing around a bit should find that answer out rather fast.
Old answers:
Since this is tagged C (and GCC), I will attempt a solution here:
#define IS_ARRAY(arg) __builtin_choose_expr(__builtin_types_compatible_p(typeof(arg[0]) [], typeof(arg)), 1, 0)
Another solution, using C11's _Generic
feature & typeof
:
#define IS_ARRAY(arg) _Generic((arg),\
typeof(arg[0]) *: 0,\
typeof(arg[0]) [sizeof(arg) / sizeof(arg[0])]: 1\
)
Basically, all it does is use some fancy features of GCC to determine if the type of the argument is compatible with an array of the type of the argument's elements. It will return 0 or 1, and you could replace the 0 with something that creates a compile time error if you wish.
A pure C99 solution:
enum { must_be_an_array_1 = ((void *) &(arr)) == ((void *) (arr)) };
typedef char must_be_an_array_2[((void *) &(arr)) == ((void *) (arr)) ? 1 : -1];
This exploits the fact that the address of an array is the same as the address of its first member, and that an enum member must be an integral constant. If the compiler is smart enough to tell that a pointer-to-a-pointer has a distinct address, it will choke on the second statement.
We still need the first statement because otherwise a compiler with support for runtime sized arrays (e.g. gcc 4.7) will perform the address comparison at runtime and invoke undefined behaviour as the size of the runtime array is negative (e.g. under gcc the program segfaults).
Full program:
#include <strings.h>
#define CLEAN_ARRAY(arr) \
do { \
enum { must_be_an_array_1 = ((void *) &(arr)) == ((void *) (arr)) }; \
typedef char must_be_an_array_2[((void *) &(arr)) == ((void *) (arr)) ? 1 : -1]; \
bzero(arr, sizeof(arr)); \
} while (0)
int main() {
int arr[5];
CLEAN_ARRAY(arr);
int *ptr;
CLEAN_ARRAY(ptr); // error: enumerator value for ‘must_be_an_array’ is not an integer constant
return 0;
}
How to validate in c macro the argument is of ARRAY type
Use std::is_array
inside of the macro. Or forget themacro and just use std::is_array
.
Concerning ARRAY_SIZE
,
constexpr size_t size(T const (&)[N])
{
return N;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With