检测宏参数是否为类型名
Detecting if a macro argument is a typename
在 C11/gnuC11 中是否可以编写一个宏,如果宏参数是或不是类型名称或至少是一个 returns 值分别为 1 或 0 的整数常量表达式宏可以区分整数常量表达式和类型名称(即,如果可以检测到参数不是其中之一,它可以假定它是另一个)?
#define IS_TYPENAME(X) /*???*/
_Static_assert( IS_TYPENAME(int), "" );
_Static_assert( !IS_TYPENAME(42), "" );
动机:
我的动机是用一个宏包装 _Aligna
s,如果建议的对齐方式(类型或整数表达式)小于当前的对齐方式(正常 _Alignas
with a较小的对齐会导致错误)所以我也想接受类型名或整数 expr,但现在我想只需要一个整数 expr(你总是可以通过应用 _Alignof
从类型名中获得)将是 simpler/clearer 的方式。
为此,您需要检查参数是否为整数类型,并且需要检查它是类型还是表达式。
检查宏参数(可能是类型或表达式)是否为整数类型:
这可以用 _Generic
来完成。 _Generic
表达式不能包含两个相同的类型,因此如果仅与所有 stdint.h 类型进行比较就足够了。因为这些将与默认整数类型别名,但不会相互冲突(例如 int
和 long
可能)。
现在 _Generic
不接受类型作为操作数,因此您必须调整输入以始终成为表达式。
我刚才发明的技巧,就是利用括号运算符和强制转换运算符之间的歧义,同时利用一元+和二元+运算符之间的歧义。
给定 (x)+0
。
- 如果
x
是类型,()
成为转换运算符,+0
是应用于整数常量的一元加法运算符。
- 如果
x
是一个表达式,它会被加括号,然后+
是二元加法运算符。
所以你可以这样做:
#define IS_INT(x) _Generic((x)+0, \
uint8_t: 1, int8_t: 1, \
uint16_t: 1, int16_t: 1, \
uint32_t: 1, int32_t: 1, \
uint64_t: 1, int64_t: 1, \
default: 0)
这适用于所有整数、字符和浮点类型,以及指针。它不适用于 struct/union 类型(编译器错误)。它不适用于 void*
,可能不适用于 NULL
(编译器错误,无法进行指针运算)。
检查宏参数(可能是类型或表达式)是否是表达式:
这也可以使用与上面相同的技巧来完成,利用不同运算符之间的歧义。例如:
#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)
- 如果
x
是一个 non-zero 整数常量表达式,我们得到 1 + 0 + 1 = 2
.
- 如果
x
是零整数常量表达式,我们得到 0 + 1 + 1 = 2
.
- 如果
x
是一个类型,我们得到 !!(int)+!(int)+1
等于 0
。 + 都是一元的。
不过这对浮点数和整数没有区别,所以我们需要将这个技巧与 IS_INT
宏结合起来。
解法:
#define IS_INTCONSTEXPR(x) ( IS_INT(x) && IS_EXPR(x) )
带有测试用例的完整示例,如果整数常量表达式打印 1,否则打印 0:
#include <stdint.h>
#include <stdio.h>
#define IS_INT(x) _Generic((x)+0, \
uint8_t: 1, int8_t: 1, \
uint16_t: 1, int16_t: 1, \
uint32_t: 1, int32_t: 1, \
uint64_t: 1, int64_t: 1, \
default: 0)
#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)
#define IS_INTCONSTEXPR(x) ( IS_INT(x) && IS_EXPR(x) )
#define test(arg) printf("%d %s\n", IS_INTCONSTEXPR(arg),(#arg))
int main (void)
{
test(42);
test(sizeof(int));
test(1+1);
test(int);
test(unsigned int);
test(42.0);
test(double);
test(uint32_t);
test(uint32_t*);
test(_Bool);
_Static_assert( !IS_INTCONSTEXPR(int), "" ); // OK, passed
_Static_assert( IS_INTCONSTEXPR(42), "" ); // OK, passed
return 0;
}
输出:
1 42
1 sizeof(int)
1 1+1
0 int
0 unsigned int
0 42.0
0 double
0 uint32_t
0 uint32_t*
0 _Bool
在 C11/gnuC11 中是否可以编写一个宏,如果宏参数是或不是类型名称或至少是一个 returns 值分别为 1 或 0 的整数常量表达式宏可以区分整数常量表达式和类型名称(即,如果可以检测到参数不是其中之一,它可以假定它是另一个)?
#define IS_TYPENAME(X) /*???*/
_Static_assert( IS_TYPENAME(int), "" );
_Static_assert( !IS_TYPENAME(42), "" );
动机:
我的动机是用一个宏包装 _Aligna
s,如果建议的对齐方式(类型或整数表达式)小于当前的对齐方式(正常 _Alignas
with a较小的对齐会导致错误)所以我也想接受类型名或整数 expr,但现在我想只需要一个整数 expr(你总是可以通过应用 _Alignof
从类型名中获得)将是 simpler/clearer 的方式。
为此,您需要检查参数是否为整数类型,并且需要检查它是类型还是表达式。
检查宏参数(可能是类型或表达式)是否为整数类型:
这可以用 _Generic
来完成。 _Generic
表达式不能包含两个相同的类型,因此如果仅与所有 stdint.h 类型进行比较就足够了。因为这些将与默认整数类型别名,但不会相互冲突(例如 int
和 long
可能)。
现在 _Generic
不接受类型作为操作数,因此您必须调整输入以始终成为表达式。
我刚才发明的技巧,就是利用括号运算符和强制转换运算符之间的歧义,同时利用一元+和二元+运算符之间的歧义。
给定 (x)+0
。
- 如果
x
是类型,()
成为转换运算符,+0
是应用于整数常量的一元加法运算符。 - 如果
x
是一个表达式,它会被加括号,然后+
是二元加法运算符。
所以你可以这样做:
#define IS_INT(x) _Generic((x)+0, \
uint8_t: 1, int8_t: 1, \
uint16_t: 1, int16_t: 1, \
uint32_t: 1, int32_t: 1, \
uint64_t: 1, int64_t: 1, \
default: 0)
这适用于所有整数、字符和浮点类型,以及指针。它不适用于 struct/union 类型(编译器错误)。它不适用于 void*
,可能不适用于 NULL
(编译器错误,无法进行指针运算)。
检查宏参数(可能是类型或表达式)是否是表达式:
这也可以使用与上面相同的技巧来完成,利用不同运算符之间的歧义。例如:
#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)
- 如果
x
是一个 non-zero 整数常量表达式,我们得到1 + 0 + 1 = 2
. - 如果
x
是零整数常量表达式,我们得到0 + 1 + 1 = 2
. - 如果
x
是一个类型,我们得到!!(int)+!(int)+1
等于0
。 + 都是一元的。
不过这对浮点数和整数没有区别,所以我们需要将这个技巧与 IS_INT
宏结合起来。
解法:
#define IS_INTCONSTEXPR(x) ( IS_INT(x) && IS_EXPR(x) )
带有测试用例的完整示例,如果整数常量表达式打印 1,否则打印 0:
#include <stdint.h>
#include <stdio.h>
#define IS_INT(x) _Generic((x)+0, \
uint8_t: 1, int8_t: 1, \
uint16_t: 1, int16_t: 1, \
uint32_t: 1, int32_t: 1, \
uint64_t: 1, int64_t: 1, \
default: 0)
#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)
#define IS_INTCONSTEXPR(x) ( IS_INT(x) && IS_EXPR(x) )
#define test(arg) printf("%d %s\n", IS_INTCONSTEXPR(arg),(#arg))
int main (void)
{
test(42);
test(sizeof(int));
test(1+1);
test(int);
test(unsigned int);
test(42.0);
test(double);
test(uint32_t);
test(uint32_t*);
test(_Bool);
_Static_assert( !IS_INTCONSTEXPR(int), "" ); // OK, passed
_Static_assert( IS_INTCONSTEXPR(42), "" ); // OK, passed
return 0;
}
输出:
1 42
1 sizeof(int)
1 1+1
0 int
0 unsigned int
0 42.0
0 double
0 uint32_t
0 uint32_t*
0 _Bool