【问题标题】:Why do I need double layer of indirection for macros?为什么我需要宏的双层间接?
【发布时间】:2012-01-04 03:23:54
【问题描述】:

在:C++ FAQ - Miscellaneous technical issues - [39.6] What should be done with macros that need to paste two tokens together?

谁能解释一下为什么?我读到的只是相信我,但我不能仅仅因为有人这么说就相信某事。

我尝试了该方法,但找不到任何出现的错误:

#define mymacro(a) int a ## __LINE__
mymacro(prefix) = 5;
mymacro(__LINE__) = 5;
int test = prefix__LINE__*__LINE____LINE__; // fine

那么为什么我需要这样做(引自网页):

但是,当您使用## 时,您需要双层间接。 基本上,您需要为“令牌粘贴”创建一个特殊的宏,例如 如:

 #define NAME2(a,b)         NAME2_HIDDEN(a,b)
 #define NAME2_HIDDEN(a,b)  a ## b 

相信我——你真的需要这样做 这! (请没有人给我写信说它有时没有 第二层间接。尝试将符号与 __ LINE__ 看看会发生什么。)

编辑: 有人能解释一下为什么他在下面声明之前使用NAME2_HIDDEN 吗?在我使用之前定义NAME2_HIDDEN 宏似乎更合乎逻辑。这是什么诡计吗?

【问题讨论】:

标签: c++ visual-c++ macros


【解决方案1】:

C 规范的相关部分:

6.10.3.1 参数替换

在确定了调用类函数宏的参数后, 发生参数替换。替换列表中的参数,除非前面有 由 # 或 ## 预处理标记或后跟 ## 预处理标记(见下文),是 在其中包含的所有宏都被替换后由相应的参数替换 扩大。在被替换之前,每个参数的预处理标记是 完全替换宏,就好像它们形成了预处理文件的其余部分;没有其他 预处理令牌可用。

决定是否要双重间接寻址的关键部分是第二句及其中的异常——如果参数涉及###操作(如@987654323中的params @ 和 NAME2_HIDDEN),则在执行 ### 之前,不会扩展参数中的任何其他宏。另一方面,如果宏体中没有### IMMEDIATELY(与NAME2 一样),则参数中的其他宏将被扩展。

所以这取决于你想要什么 - 有时你希望所有宏首先展开,然后执行 ###(在这种情况下你想要双层间接),有时你不想要宏首先展开(在这种情况下你不能有双层宏,你需要直接做。)

【讨论】:

  • 确实如此。我发现网站告诉我总是使用它非常令人不安......而在我的情况下,我不想以这种方式使用它。
【解决方案2】:

__LINE__ 是一个特殊的宏,应该解析为当前行号。但是,当您直接使用__LINE__ 进行令牌粘贴时,它没有机会解决,因此您最终会使用令牌prefix__LINE__ 而不是prefix23,就像您可能期望的那样你会在野外编写这段代码。

【讨论】:

  • edit:哦,等等,你的意思是有人希望在变量名中给出一个行号?嗯嗯。这就是它会导致的所有问题吗?
  • 您能否解释一下为什么他以“错误”的顺序使用宏?例如。他甚至在声明之前引用了宏NAME2_HIDDEN。这被认为是好的做法,还是某种技巧?我测试了两种方式,似乎得到了相同的结果。
【解决方案3】:

Chris Dodd 对您问题的第一部分有很好的解释。至于第二部分,关于定义序列,简短的版本是 #define 指令本身根本不被评估;只有在文件的其他地方找到符号时,它们才会被评估和扩展。例如:

#define A a  //adds A->a to the symbol table
#define B b  //adds B->b to the symbol table

int A;

#undef A     //removes A->a from the symbol table
#define A B  //adds A->B to the symbol table

int A;

第一个int A; 变为int a;,因为这是在文件中该点定义A 的方式。第二个int A; 在两次扩展后变为int b;。它首先扩展为int B;,因为A 在该文件中被定义为B。然后,预处理器在检查符号表时识别出B 是一个宏。然后B 扩展为b

唯一重要的是符号在扩展点的定义,不管定义在哪里。

【讨论】:

  • 哦,是的,我只是认为将它们按您使用它们的顺序排列会更合乎逻辑,这对我来说更有意义。谢谢你的解释。
【解决方案4】:

我从这里的所有链接和链接的链接中收集到的最非技术性的答案是,单层间接macro(x) #x 将输入的宏的名称字符串化,但通过使用双层,它将字符串化输入宏的值。

#define valueOfPi 3
#define macroHlp(x) #x
#define macro(x) macroHlp(x)  
#define myVarOneLayer "Apprx. value of pi = " macroHlp(valueOfPi)
#define myVarTwoLayers "Apprx. value of pi = " macro(valueOfPi)

printf(myVarOneLayer); // out: Apprx. value of pi = valueOfPi 
printf(myVarOTwoLayers); // out: Apprx. value of pi = 3

printf(myVarOneLayer)会发生什么

printf(myVarOneLayer) 扩展为printf("Apprx. value of pi = " macroHlp(valueOfPi))

macroHlp(valueOfPi) 尝试对输入进行字符串化,输入本身不被评估。生活中唯一的目的就是接受输入并进行字符串化。所以它扩展为"valueOfPi"

那么,printf(myVarTwoLayers) 会发生什么

printf(myVarTwoLayers) 扩展为printf("Apprx. value of pi = " macro(valueOfPi)

macro(valueOfPi)没有字符串化操作,即它的扩展中没有#x,但是有一个x,所以它必须评估x并将值输入到macroHlp进行字符串化。它扩展为macroHlp(3),这反过来将字符串化数字3,因为它使用#x

【讨论】:

  • +1 用于实际提供一个工作示例作为答案的一部分。这有助于我更好地理解问题和解决方案。
  • @M_M 太好了!谢谢
【解决方案5】:

宏的声明顺序并不重要,它们的使用顺序很重要。如果您要在声明之前实际使用该宏 - (在实际代码中,即不是在调用之前一直处于休眠状态的宏中),那么您会遇到各种各样的错误,但由于大多数理智的人不会四处走动这类事情,编写一个宏,然后编写一个使用尚未进一步定义的宏的函数,等等等等……看来您的问题不仅仅是一个问题,我只会回答那一部分。我认为你应该再细分一下。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-25
    • 2021-02-23
    • 2018-03-08
    • 2013-11-09
    • 2021-06-21
    • 2015-10-03
    • 2010-10-28
    • 1970-01-01
    相关资源
    最近更新 更多