C 预处理器中的递归是否滥用了标准中的不一致?
Does recursion in the C preprocessor abuse an inconsistency in the standard?
考虑这段代码:
#define MAP_OUT
#define A(x) B MAP_OUT (x)
#define B(x) A MAP_OUT (x)
A(x)
然后 A(x)
扩展为 B MAP_OUT (x)
,然后 B (x)
。现在看看标准:
After all parameters in the replacement list have been substituted and # and ## processing has taken place, all placemarker preprocessing tokens are removed. The resulting preprocessing token sequence is then rescanned, along with all subsequent preprocessing tokens of the source file, for more macro names to replace.
B (x)
是否属于“生成的预处理令牌序列以替换更多宏名称”?我尝试过的所有编译器都不会在单次扫描期间扩展 B (x)
,但是标准本身呢?
你是 cherry-picking。该标准要求停止重新扫描和替换。
还有其他段落,自 C++98 以来的每个 C++ 标准中都有相同的措辞(不仅仅是您引用的那个)实际上 控制您观察到的行为。
After all parameters in the replacement list have been substituted, the resulting preprocessing token sequence is rescanned with all subsequent preprocessing tokens of the source file for more macro names to replace.
If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts
in which that macro name preprocessing token would otherwise have been replaced.
正如我所说,每个 C++ 标准中的措辞都是相同的。只有章节和段落编号发生变化。
- 在 C++98 中,以上引用是第 16.3.4 节“重新扫描和进一步替换”,第 1 和第 2 段;
- 在 C++17 中,上面的引用是第 19.3.4 节“重新扫描和进一步替换”,第 1 段和第 2 段;
- 在最新的 C++20 草案(至少是我访问过的最新草案)中,上面的引述是第 15.6.4 节“重新扫描和进一步替换”,第 1 段和第 3 段(添加了第 2 段带有说明性示例,而不是规范性文本)。
Does B (x) belong to "resulting preprocessing token sequence for more macro names to replace"?
不,绝对不是。再读一遍:
After all parameters in the replacement list have been substituted and # and ## processing has taken place, all placemarker preprocessing tokens are removed. The resulting preprocessing token sequence is then rescanned.
参数替换in A(x)
得到的预处理token序列就是B MAP_OUT (x)
,仅此而已。然后扫描此序列以查找更多要替换的宏,一次。那里只有一个符合条件的宏可以替换,MAP_OUT
。然后扫描MAP_OUT
的替换,没有找到,继续处理
没有任何迹象表明 B MAP_OUT (x)
中的 B
应该被扫描两次。
考虑这段代码:
#define MAP_OUT
#define A(x) B MAP_OUT (x)
#define B(x) A MAP_OUT (x)
A(x)
然后 A(x)
扩展为 B MAP_OUT (x)
,然后 B (x)
。现在看看标准:
After all parameters in the replacement list have been substituted and # and ## processing has taken place, all placemarker preprocessing tokens are removed. The resulting preprocessing token sequence is then rescanned, along with all subsequent preprocessing tokens of the source file, for more macro names to replace.
B (x)
是否属于“生成的预处理令牌序列以替换更多宏名称”?我尝试过的所有编译器都不会在单次扫描期间扩展 B (x)
,但是标准本身呢?
你是 cherry-picking。该标准要求停止重新扫描和替换。
还有其他段落,自 C++98 以来的每个 C++ 标准中都有相同的措辞(不仅仅是您引用的那个)实际上 控制您观察到的行为。
After all parameters in the replacement list have been substituted, the resulting preprocessing token sequence is rescanned with all subsequent preprocessing tokens of the source file for more macro names to replace.
If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.
正如我所说,每个 C++ 标准中的措辞都是相同的。只有章节和段落编号发生变化。
- 在 C++98 中,以上引用是第 16.3.4 节“重新扫描和进一步替换”,第 1 和第 2 段;
- 在 C++17 中,上面的引用是第 19.3.4 节“重新扫描和进一步替换”,第 1 段和第 2 段;
- 在最新的 C++20 草案(至少是我访问过的最新草案)中,上面的引述是第 15.6.4 节“重新扫描和进一步替换”,第 1 段和第 3 段(添加了第 2 段带有说明性示例,而不是规范性文本)。
Does B (x) belong to "resulting preprocessing token sequence for more macro names to replace"?
不,绝对不是。再读一遍:
After all parameters in the replacement list have been substituted and # and ## processing has taken place, all placemarker preprocessing tokens are removed. The resulting preprocessing token sequence is then rescanned.
参数替换in A(x)
得到的预处理token序列就是B MAP_OUT (x)
,仅此而已。然后扫描此序列以查找更多要替换的宏,一次。那里只有一个符合条件的宏可以替换,MAP_OUT
。然后扫描MAP_OUT
的替换,没有找到,继续处理
没有任何迹象表明 B MAP_OUT (x)
中的 B
应该被扫描两次。