【问题标题】:Detecting if a macro argument is a typename检测宏参数是否为类型名
【发布时间】:2019-10-02 13:55:17
【问题描述】:

在 C11/gnuC11 中,如果宏参数是或不是类型名称,或者至少一个宏可以区分整数常量表达式和typenames(即,如果可以检测到参数不是其中之一,则可以假定它是另一个)?

#define IS_TYPENAME(X) /*???*/ 
_Static_assert( IS_TYPENAME(int), "" );
_Static_assert( !IS_TYPENAME(42), "" );

动机:

我的动机是用一个宏包装_Alignas,如果建议的对齐方式(类型或整数表达式)小于当前对齐方式(正常的_Alignas,使用较小的对齐方式会导致错误),该宏将什么也不做) 所以我也想接受类型名或整数 expr,但现在我想只需要一个整数 expr(你总是可以通过应用 _Alignof 从类型名中获取)将是更简单/更清晰的方法走。

【问题讨论】:

  • 像往常一样,我对您为什么需要这样做很感兴趣?您需要解决的真正根本问题是什么?为什么你认为这是该问题的自然解决方案?你确定没有其他方法可以解决这个问题吗?
  • 我不认为这是可能的,因为宏正在预处理,而类型名仅在编译期间才知道。
  • 你可以使用_Generic,但它并不漂亮。
  • @Bathsheba - 给定typedef int myInt; _generic 如何帮助确定myInt 是否是编译前的类型名称?
  • 如果您在编译器需要类型但它不是类型的上下文中使用标识符。编译器将诊断错误。这就是它的工作(除其他外)。由于您对假设宏所做的一切都是_Static_Assert,因此您并没有实现任何不同。所以这让我觉得毫无意义——即使它是可能的,但它似乎不是。

标签: c gcc clang c11


【解决方案1】:

为此,您需要检查参数是否为整数类型,并且需要检查它是类型还是表达式。


检查宏参数(可能是类型或表达式)是否为整数类型:

这可以通过_Generic 完成。 _Generic 表达式不能包含两种相同的类型,因此如果仅与所有 stdint.h 类型进行比较就足够了。因为这些将使用默认整数类型别名,但不会相互冲突(例如 intlong 可能)。

现在_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)

这适用于所有整数、字符和浮点类型,以及指针。它不适用于结构/联合类型(编译器错误)。它不适用于void*,也可能不适用于NULL(编译器错误,无法进行指针运算)。


检查宏参数(可能是类型或表达式)是否为表达式:

这也可以使用与上述相同的技巧来完成,使用不同运算符之间的歧义。例如:

#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)
  • 如果x 是一个非零整数常量表达式,我们得到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

【讨论】:

    猜你喜欢
    • 2014-02-23
    • 2013-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 2020-10-06
    • 1970-01-01
    相关资源
    最近更新 更多