C语言中宏的展开

序:

  看micropython的parser代码时,出现了比较复杂的宏,于是研究一番,总结如下:

  parser中的宏:

  #define RULE_EXPAND(x) x
  #define RULE_PADDING(rule, ...) RULE_PADDING2(rule, __VA_ARGS__, RULE_PADDING_IDS(rule))
  #define RULE_PADDING2(rule, ...)  RULE_EXPAND(RULE_PADDING3(rule, __VA_ARGS__))
  #define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...)   __VA_ARGS__
  #define RULE_PADDING_IDS(r) PAD13_##r, PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r,
 
知识储备:
0. -> 推导符:因-> 果
1. 符号“#” 表示字符串化
  例子:  #define STR_PARA(x)     #x
      STR_PARA(10)  -> "10"
2. 符号“##”表示去空格连接
  例子:  #define LINK_PARA(x, y)     x  ##          y
      LINK_PARA(int, 10)  ->   int10
3. 可变参数...与__VA_ARGS__
  C99标准文档第6.10.3章节宏替换(6.10.3 Macro replacement)中描述如下
  The identifier _ _VA_ARGS_ _ shall occur only in the replacement-list of a function-like macro that uses the ellipsis notation in the arguments.
  省略号...只能出现在宏参数列表中, __VA_ARGS__出现在替换列表中。...是参数,__VA_ARGS__是实际列表
  例子:  #define LINK_PARA(x,...)     x, __VA_ARGS__     (正确)
       #define LINK_PARA(x,__VA_ARGS__ )     x, ...  (错误)
  C99标准文档第6.10.3.1章节宏替换(6.10.3.1 Argument substitution)中第2小段描述如下
  An identifier _ _VA_ARGS_ _ that occurs in the replacement list shall be treated as if it were a parameter, and the variable arguments shall form the preprocessing tokens used to replace it.
  __VA_ARGS__被当作一个参数来看,实际上是一个参数列表
 
宏替换规则:
  C99标准文档第6.10.3章节宏替换(6.10.3 Macro replacement)中第11小段描述如下
  The sequence of preprocessing tokens bounded by the outside-most matching parentheses forms the list of arguments for the function-like macro. The individual arguments within the list are separated by comma preprocessing tokens, but comma preprocessing tokens between matching inner parentheses do not separate arguments. If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the behavior is undefined. 
  C99标准文档第6.10.3.1章节宏替换(6.10.3.1 Argument substitution)中第1小段描述如下
   After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available. 
  总结来说:
  1.最外层括号中的逗号为实参分隔符,内层的逗号不分隔该层实参(暂时作为一个整体);
  2.带有预处理指令#或##的形参,不参与宏替换或宏展开
  例如上面知识储备2例子中的宏
  实际调用时:LINK_PARA(LINK_PARA(int, 16) , _t)
  分析:LINK_PARA(LINK_PARA(int, 16) , _t)
  目标宏中最外层的括号内,有一个逗号在_t左边,根据规则1判断这个逗号是实参分隔符,把LINK_PARA(int, 16) , _t原符号,分割成了LINK_PARA(int, 16) 与 _t两个参数
  那么相当于#define LINK_PARA(x, y)中的 x= LINK_PARA(int, 16) , y =_t. 因为x 后面, y前面都有 ##, 根据规则2 停止解析  LINK_PARA(int, 16)y, 这个只是分析结果,结果会报错.
 
  如果改成如下宏:
  #define LINK_PARA(x, y)           x   ## y
  #define ADD_LAYER(a, b)           LINK_PARA(a, b) 
  #define EXPAND_MACRO(x, y)   ADD_LAYER(x, y)
  EXPAND_MACRO(EXPAND_MACRO(int, 16), _t)
 
  分析EXPAND_MACRO(EXPAND_MACRO(int, 16), _t):
    1.根据规则1 分解参数如下:
      参数 x = EXPAND_MACRO(int, 16),
      参数 y = _t
      目标结果是:EXPAND_MACRO(ADD_LAYER(int, 16),_t)
    2. 根据规则2分析 参数左右两边没有“#”与“##”,所以可以继续进行。
    3. 根据规则1 分解ADD_LAYER(int, 16)的参数
      参数 a = int
      参数 b = 16
      结果是:LINK_PARA(int, 16) 即为 int##16      
    4. 根据规则2分析 参数左右两边没有“#”与“##”,所以可以继续进行。
    5根据规则1 分解ADD_LAYER(int##16, _t)的参数
      参数 a = int##16
      参数 b = _t
      LINK_PARA(int##16, _t) 即为 int##16##_t
    结果是int16_t
  其流程如下:
    EXPAND_MACRO(EXPAND_MACRO(int, 16), _t) -> EXPAND_MACRO(ADD_LAYER(int, 16), _t) ->
    EXPAND_MACRO(LINK_PARA(int, 16), _t) -> EXPAND_MACRO(int##16 , _t) ->
    ADD_LAYER(int##16 , _t) -> LINK_PARA(int##16 , _t) -> int##16 ##_t  -> int16_t
  宏替换的结论:  
    宏展开从外层进行,将外层的参数展开,最后才是对外层展开
 
 最后分析parser中的一组宏:
  例如:RULE_PADDING(1,2,3,4,5,6)
      参数:rule = 1
      参数: ... = 2,3,4,5,6
      结果:RULE_PADDING2(1,2,3,4,5,6, RULE_PADDING_IDS(1)) ->
      RULE_PADDING2(1,2,3,4,5,6, PAD13_1, PAD12_1, PAD11_1, PAD10_1, PAD9_1, PAD8_1, PAD7_1, PAD6_1, PAD5_1, PAD4_1, PAD3_1, PAD2_1, PAD1_1,)
 
    参数展开完毕,展开最外层RULE_PADDING2宏
      参数:rule = 1
      参数: ... = 2,3,4,5,6, PAD13_1, PAD12_1, PAD11_1, PAD10_1, PAD9_1, PAD8_1, PAD7_1, PAD6_1, PAD5_1, PAD4_1, PAD3_1, PAD2_1, PAD1_1,
      结果:RULE_EXPAND(RULE_PADDING3(1,2,3,4,5,6, PAD13_1, PAD12_1, PAD11_1, PAD10_1, PAD9_1, PAD8_1, PAD7_1, PAD6_1, PAD5_1, PAD4_1, PAD3_1, PAD2_1, PAD1_1,))

     展开参数:RULE_PADDING3宏
      参数:rule = 1
      参数:_1=2,                _2=3,                   _3 = 4,               _4=5,              _5=6,                _6=PAD13_1,    _7= PAD12_1, 
           _8=PAD11_1,    _9= PAD10_1,     _10=PAD9_1,    _11=PAD8_1,  _12=PAD7_1,  _13=PAD6_1
      参数: ... = PAD5_1, PAD4_1, PAD3_1, PAD2_1, PAD1_1,
      结果:RULE_EXPAND(PAD5_1, PAD4_1, PAD3_1, PAD2_1, PAD1_1,) ->  PAD5_1, PAD4_1, PAD3_1, PAD2_1, PAD1_1,
    RULE_PADDING(1,2,3,4,5,6)的最终结果是  PAD5_1, PAD4_1, PAD3_1, PAD2_1, PAD1_1,
 
  注:参考了dianhuiren的文章,详情请看https://blog.csdn.net/dianhuiren/article/details/6913079
posted @ 2020-03-19 21:00  雪蕻轩  阅读(269)  评论(0编辑  收藏  举报