检测宏参数是否为类型名 [英] Detecting if a macro argument is a typename

查看:99
本文介绍了检测宏参数是否为类型名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C11 / gnuC11内,如果宏参数为类型名称或不是类型名称,或者至少一个宏可以区分整数常量表达式和宏,则可以编写一个分别返回值1或0的整数常量表达式的宏。类型名称(即,如果可以检测到参数不是其中一个,则可以假设是另一个)?

Within C11/gnuC11 is it possible to write a macro that returns an integer constant expression of value 1 or 0 respectively if the macro argument is or isn't a type name or at least a macro can distinguish between integer constant expressions and typenames (i.e., if can detect the argument isn't one of these, it can assume it is the other)?

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

动机:

我的动机是用一个宏包装 _Aligna ,如果建议的对齐方式(类型或整数表达式)小于当前对齐方式(正常 _Alignas),宏将不执行任何操作使用较小的对齐方式会导致错误),所以我也想接受类型名称或整数expr,但是现在我想只需要一个整数expr(您总是可以从通过应用 _Alignof 输入typename将会是更简单/更清晰的方法。

My motivation was to wrap _Alignas with a macro that would simply do nothing if the suggested alignment (either type or integer expression) was less than the current one (normal _Alignas with a smaller alignment results in an error) and so I wanted to also accept either a typename or an integer expr, but now I'm thinking simply requiring an integer expr (which you can always get from a typename by applying _Alignof) will be the simpler/clearer way to go.

推荐答案

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

In order to do this, you will need to check if the parameter is of type integer, and you need to check if it is a type or an expression.

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

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

This can be done with _Generic. A _Generic expression cannot contain two types that are identical, so it should suffice if you compare against all the stdint.h types only. Since these will alias with the default integer types, but not collide with each other (like for example int and long might).

现在 _Generic 不接受类型作为操作数,因此您必须将输入调整为始终

Now _Generic doesn't accept a type as operand, so you have to tweak the input to always become an expression.

我刚刚发明的技巧是使用括号运算符和强制转换运算符之间的歧义,同时使用之间的歧义。一元+和二进制+运算符。

The trick that I invented just now, is to use the ambiguity between the parenthesis operator and the cast operator, and at the same time use the ambiguity between unary + and binary + operators.

给出(x)+0


  • 如果 x 是一种类型,则()成为强制转换运算符, +0 是应用于整数常量的一元加法运算符。

  • 如果 x 是一个表达式,将被加括号,然后 + 是二进制加法运算符。

  • If x is a type, () becomes the cast operator and +0 is the unary addition operator applied to an integer constant.
  • If x is an expression, it will get parenthesized and then + is the binary addition operator.

因此,您可以这样做:

#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 (编译器错误,不能执行指针运算)。

This will work for all integer, character and float types, as well as pointers. It will not work on struct/union types (compiler error). It will not work with void* and probably not with NULL (compiler error, can't do pointer arithmetic).

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

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

This can also be done using the same trick as above, use the ambiguity between different operators. For example:

#define IS_EXPR(x) (!!(x) + !(x) + 1 == 2)




  • 如果 x 是一个非零整数常量表达式,我们得到 1 + 0 +1 = 2

  • 如果 x 是一个零整数常量表达式,我们得到 0 +1 + 1 = 2

  • 如果 x 是一个类型,我们得到 !!(int)+!(int)+1 等于 0 。两者+都是一元的。

    • If x is a non-zero integer constant expression, we get 1 + 0 + 1 = 2.
    • If x is a zero integer constant expression, we get 0 + 1 + 1 = 2.
    • If x is a type, we get !!(int)+!(int)+1 which equals 0. Both + are unary.
    • 尽管这在float和integer之间没有区别,所以我们需要将此技巧与 IS_INT 宏。

      This doesn't make a difference between float and integers though, so we need to combine this trick with the IS_INT macro.

      解决方案:

      #define IS_INTCONSTEXPR(x) ( IS_INT(x) && IS_EXPR(x) )
      

      带有测试用例的完整示例,如果为整型常量表达式,则输出1,否则为0:

      Complete example with test cases, printing 1 if integer constant expression, otherwise 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
      

      这篇关于检测宏参数是否为类型名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆