在编译时在c / c ++预处理宏中validation参数是ARRAY类型

有没有办法在ac宏的编译时validation参数是一个数组?

例如在这两个宏中:

#define CLEAN_ARRAY(arr) \ do { \ bzero(arr, sizeof(arr)); \ } while (0) 

 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 

我尝试了一些使用CTC(X)宏的东西,但是找不到任何方法来validation/警告arr是不是一个数组。

这是纯C中的一个解决方案,它不会调用任何未定义的行为:

 #define IS_INDEXABLE(arg) (sizeof(arg[0])) #define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg))) 

如果需要确保该值是一个数组(如果没有,则导致编译时错误),您可以简单地将它用作枚举语句(或静态变量)的初始化程序,如下所示:

 static int __ ## arg ## _is_array = IS_ARRAY(arg); // works for an array, fails for pointer. 

我不完全确定VLA会发生什么,但玩一下​​应该会找到相当快的答案。


旧答案:

由于这是标记C(和GCC),我将在这里尝试解决方案:

 #define IS_ARRAY(arg) __builtin_choose_expr(__builtin_types_compatible_p(typeof(arg[0]) [], typeof(arg)), 1, 0) 

另一个解决方案,使用C11的_Genericfunction和typeof

 #define IS_ARRAY(arg) _Generic((arg),\ typeof(arg[0]) *: 0,\ typeof(arg[0]) [sizeof(arg) / sizeof(arg[0])]: 1\ ) 

基本上,它所做的只是使用GCC的一些奇特的function来确定参数的类型是否与参数元素类型的数组兼容。 它将返回0或1,如果您愿意,可以用创建编译时错误的东西替换0。

纯C99解决方案:

 enum { must_be_an_array_1 = ((void *) &(arr)) == ((void *) (arr)) }; typedef char must_be_an_array_2[((void *) &(arr)) == ((void *) (arr)) ? 1 : -1]; 

这利用了一个事实,即数组的地址与其第一个成员的地址相同,并且枚举成员必须是一个整数常量。 如果编译器足够聪明,可以告诉指向指针的指针具有不同的地址,那么它将会阻塞第二个语句。

我们仍然需要第一个语句,因为否则支持运行时大小的数组(例如gcc 4.7)的编译器将在运行时执行地址比较并调用未定义的行为,因为运行时数组的大小是负的(例如,在gcc下程序段错误)。

完整计划:

 #include  #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; } 

如何在c宏中validation参数是否为ARRAY类型

在宏内使用std::is_array 。 或者忘记themacro并使用std::is_array

关于ARRAY_SIZE

 constexpr size_t size(T const (&)[N]) { return N; } 

据我所知,还没有人提供一种方法来确保ARRAY_SIZE的参数实际上是一个数组。

编辑:
刚发现拒绝指针的Array-size宏

根据我的评论:

如果类型不是“可索引”类型,则sizeof((x)[0])将给出错误 – 但是,如果它恰好是指针,它将很乐意接受它。 而且,如果有一个类型为xoperator[]

C中很难做到这一点,但C ++可能允许一些模板类型的解决方案(我实际上并不知道如何做到这一点,因为我从未尝试过这样做,或者与模板类似的东西)。

(对于C ++)我在VS2010中的当前清晰宏是:

 #define CLEAR(v) do { __pragma(warning(suppress: 4127 4836)) typedef std::remove_reference< decltype(v)>::type T; static_assert( std::is_pod::value || (__has_trivial_constructor(T) && __has_trivial_destructor(T)), "must not CLEAR a non-POD!" ); static_assert( !std::is_pointer::value, "pointer passed to CLEAR!" ); memset(&(v), 0, sizeof(v)); } while(0) 

您可以使用type_traits标头中的内容来弥补您的变体。

带有解释的格式化validation:

 #define CLEAR(v) \ do { \ __pragma(warning(suppress: 4127 4836)) \ typedef std::remove_reference< decltype(v)>::type T; \ static_assert( \ std::is_pod::value \ || (__has_trivial_constructor(T) && __has_trivial_destructor(T)), \ "must not CLEAR a non-POD!" ); \ static_assert( !std::is_pointer::value, "pointer passed to CLEAR!" ); \ memset(&(v), 0, sizeof(v)); \ } while(0) 

外部do-while使其在所有地方都可以像真正的function一样使用,包括if / else。

需要remove_reference,因此它适用于左值,单独的decltype使int *和int *&不同,而is_pointer报告后者为false。

is_pod检查适用于一般情况,附加条件允许结构A1:A; 案例工作,其中A是POD,A1只添加更多POD成员。 对于is_pod来说,它是错误的,但要清除它就会有同样的意义。

当你在指针上得到间接错误或者在混乱中传递struct的地址时,is_pointer检查会保护预期的错误类型。 请使用= NULL清除指针。 😉

__pragma用于抑制否则发出的L4警告。

在C中,这应该工作:

 #define VALIDATE_ARRAY(arr) (void)(sizeof((arr)[0])) int *a, b; int main() { VALIDATE_ARRAY(a); VALIDATE_ARRAY(b); return 0; } 

该程序将无法编译,因为b不是数组。 这是因为b[0]无效。 它不会区分指针和数组 – 我认为你不能这样做。

这种forms的宏只能在函数内部使用。 如果要在函数外部使用它,则必须对其进行修改(例如,声明一个extern数组)。