为什么我需要双层间接宏?

时间:2011-11-22 18:37:16

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

在: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

所以为什么我需要这样做(来自网页):

  

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

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

相信我 - 你真的需要这样做   这个! (并且请没有人写信说我有时候没有   第二层间接。尝试连接符号   __ LINE__然后看看会发生什么。)

编辑:有人可以在下面声明之前解释他为什么使用NAME2_HIDDEN吗?在使用它之前定义NAME2_HIDDEN宏似乎更合乎逻辑。这是某种伎俩吗?

5 个答案:

答案 0 :(得分:30)

C规范的相关部分:

  

6.10.3.1参数替换

     

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

确定是否需要双重间接的关键部分是第二个句子及其中的例外 - 如果参数涉及###操作(例如mymacroNAME2_HIDDEN中的参数,然后在执行###之前,不会展开参数中的任何其他宏。另一方面,如果宏体中没有###立即(与NAME2一样),则参数ARE中的​​其他宏将被扩展。

所以它归结为你想要的东西 - 有时候你希望所有的宏都先扩展,然后做###(在这种情况下你想要双层间接),有时你不要首先扩展宏(在这种情况下你不能有双层宏,你需要直接进行扩展。)

答案 1 :(得分:4)

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

答案 2 :(得分: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

唯一重要的是在扩展点定义符号,无论定义在何处。

答案 3 :(得分:2)

我从这里所有链接收集的最非技术性答案和链接链接;)是单层间接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

答案 4 :(得分:1)

声明宏的顺序并不重要,它们的使用顺序是。如果你在声明它之前实际使用那个宏 - (在实际的代码中,不是在召唤之前一直处于休眠状态的宏中)那么你会得到一个错误的但是因为大多数理智的人都没有去做这些东西,编写一个宏,然后编写一个使用尚未进一步定义的宏的函数等等......似乎你的问题不只是一个问题,而是我只回答那一部分。我想你应该把它打破一点。