【问题标题】:Why does sequence iteration work in C macro?为什么序列迭代在 C 宏中起作用?
【发布时间】:2017-10-15 02:22:04
【问题描述】:

在编写C宏时,有一个技巧叫“序列迭代”。如下所示:

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define FUNCTION(name) void name();
#define FUNCTION_TABLE(seq) CAT(FUNCTION_TABLE_1 seq, _END)
#define FUNCTION_TABLE_1(x) FUNCTION(x) FUNCTION_TABLE_2
#define FUNCTION_TABLE_2(x) FUNCTION(x) FUNCTION_TABLE_1
#define FUNCTION_TABLE_1_END
#define FUNCTION_TABLE_2_END

FUNCTION_TABLE((x) (y) (z) (e))

序列,即FUCTION_TABLE的参数,将被一一处理。但是,据我所知,一个令牌不会在同一范围内扩展两次。因为它是“涂成蓝色的”。当 FUNCTION_TABLE_2 展开时,宏 FUNCTION_TABLE_1 已经被绘制。为什么还要扩容?

【问题讨论】:

  • @user694733 对不起,我的错。我修改了问题并添加了定义。

标签: c++ c macros


【解决方案1】:

类函数宏替换是一个递归过程:

  • 宏首先以非递归方式展开,### 除外
  • 然后,递归地扩展任何不是### 操作数的标记
  • ###(如果有)应用于结果操作数
  • 转到 1,直到步骤 1-3 没有任何变化。

(描述起来比实现起来要复杂一些!)。所以对于你的代码:

  • FUNCTION_TABLE((x) (y) (z) (e)) 扩展为 CAT(FUNCTION_TABLE_1 (x) (y) (z) (e), _END)
  • CAT(X, _END) 扩展为 PRIMITIVE_CAT(expand(X), _END) 。现在锻炼expand(X)
    • FUNCTION_TABLE_1 (x) (y) (z) (e) 扩展为 FUNCTION(x) FUNCTION_TABLE_2 (y) (z) (e)
      • FUNCTION(x) 扩展为 void x();
      • FUNCTION_TABLE_2 (y) (z) (e) 扩展为 FUNCTION(y) FUNCTION_TABLE_1 (z) (e)
      • FUNCTION(y) 扩展为 void y();
      • FUNCTION_TABLE_1 (z) (e) 扩展为 FUNCTION(z) FUNCTION_TABLE_2(e)
        • FUNCTION(z) 扩展为 void z();
        • FUNCTION_TABLE_2(e) 扩展为 FUNCTION(e) FUNCTION_TABLE_1
        • FUNCTION(e) 扩展为 void e();
        • 结果: void e(); FUNCTION_TABLE_1
        • 结果: void z(); void e(); FUNCTION_TABLE_1
      • 结果: void y(); void z(); void e(); FUNCTION_TABLE_1
      • 结果: void x(); void y(); void z(); void e(); FUNCTION_TABLE_1
  • 所以,已经完全扩展了X,让我们回顾一下我们现在的位置:PRIMITIVE_CAT(void x(); void y(); void z(); void e(); FUNCTION_TABLE_1, _END)
  • 这扩展为void x(); void y(); void z(); void e(); FUNCTION_TABLE_1_END
  • 重新扫描后,得到void x(); void y(); void z(); void e();

【讨论】:

  • 所以如果有3个宏等待展开,整个body会被扫描3次。每次完全扩展宏后,都会进行重新扫描。对吗?
  • 嗯...很难理解这个处理过程。 ? FUNCTION_TABLE_1(x) 扩展为 void x(); FUNCTION_TABLE_2。此时,FUNCTION_TABLE_2 还不是宏。但是在替换之后,FUNCTION_TABLE_2(y) 被扩展为一个宏。似乎处理器重新扫描了替换的结果。
  • 我相信我做对了;这是我自己的解释方式,例如:stackoverflow.com/a/66130832/1599699
【解决方案2】:

这个想法是,在宏扩展中,所有参数的扩展都将以相同的 BLUE-SET 开始。

FUNCTION_TABLE(seq) 内,FUNCTION_TABLE_1 seq 内所有参数的扩展将始终以 BLUE-SET={seq} 开始。因此,在进入 FUNCTION_TABLE_1 之后,BLUE-SET 被添加了x,但是当它完成后,它又回到了FUNCTION_TABLE 的范围内,扩展又以BLUE-SET={seq} 开始。

所以,第一次扩展 FUNCTION_TABLE_1(x) 并在此扩展内 BLUE-SET={seq,x} 但是当 FUNCTION_TABLE_1 的扩展完成时,它会返回到 FUNCTION_TABLE 并且它将在此范围内扩展下一个 @987654330 @ 和 INSIDE FUNCTION_TABLE_2 BLUE-SET={seq, x} 再次等等。

【讨论】:

  • 谢谢 :) 但我还有一个问题。在处理过程中,FUNCTION_TABLE_1(x) 扩展为void x(); FUNCTION_TABLE_2。此时,FUNCTION_TABLE_2 还不是宏。但是在替换之后,FUNCTION_TABLE_2(y) 被扩展为一个宏。似乎处理器重新扫描了替换的结果。真的有重新扫描吗?
  • 是的,预处理器在每次评估后都会生成一个新流,并且它会一直重新扫描新流。新流将具有预处理标记,每个标记都有一个新的 BLUE-SET,因此递归是不可能的。注意:蓝色套装附加在每个预处理令牌上,而不是每个流上。
  • 非常感谢!它对我有很大帮助:)
猜你喜欢
  • 2023-03-23
  • 1970-01-01
  • 2021-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多