2013-01-22 54 views
2

检查下面的宏:升压预处理 - 奇怪的结果

#define INPUT (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h") 

目标是增加围绕每个元组双括号导致:

((char, "microsecond", "us"))((int, "millisecond", "ms"))((int, "second", "s"))((int, "minute", "min"))((float, "hour", "h")) 

现在我用下面的宏来做这项工作:

#define ADD_PAREN_1(A, B, C) ((A, B, C)) ADD_PAREN_2 
#define ADD_PAREN_2(A, B, D) ((A, B, C)) ADD_PAREN_1 
#define ADD_PAREN_1_END 
#define ADD_PAREN_2_END 
#define OUTPUT0 ADD_PAREN_1 INPUT 
#define OUTPUT1 BOOST_PP_CAT(OUTPUT0, _END) 

结果如下:

OUTPUT0是好的:

((char, "microsecond", "us")) ((int, "millisecond", C)) ((int, "second", "s")) ((int, "minute", C)) ((float, "hour", "h")) ADD_PAREN_2 

但当BOOST_PP_CAT被称为OUTPUT1的结果是:

float 

我不理解这种行为。任何提示?

注意我使用Visual Studio 2010

+4

暗示当日:不要使用宏。 –

+2

@Alex:非常有帮助。谢谢 ! – Mark

+0

为什么不只是在地区使用搜索和替换?是否有一些原因需要预处理器而不是编辑器加倍? – AShelly

回答

2

预处理器通过扫描和展开工作。因此,当它扩展了您OUTPUT0宏,它提供了:

ADD_PAREN_1 INPUT 
^ 

然后它会扫描下一个标记,看它是否是一个括号,如果是它会调用ADD_PAREN_1作为函数宏。但是,它只会看到INPUT,所以它不会调用ADD_PAREN_1。接下来,它会扫描,并扩大了下一个标记:

ADD_PAREN_1 INPUT 
      ^

这将导致在此:

ADD_PAREN_1 (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h") 
      ^

接下来,当您尝试使用OUTPUT1,它便会扩展成:

BOOST_PP_CAT(OUTPUT0, _END) 

其中BOOST_PP_CAT将扩大OUTPUT0然后连接令牌,所以您最终会得到这个:

ADD_PAREN_1 (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h") ## _END 

正如您所看到的,您正在使用_END(不允许)与括号相连,并导致编译器错误。在Visual Studio中,您可能会看到不同的结果,因为它们的预处理器以神秘的方式工作。

最终,使它工作,你只需要在OUTPUT0宏,像这样以应用额外的扫描:

#define X(x) x 
#define OUTPUT0 X(ADD_PAREN_1 INPUT) 

这将使用C预处理器的工作,我不知道这是否准确在Visual Studio中的工作(我没有获得它现在检查),但我知道这个工程:

#define ADD_PAREN(x) BOOST_PP_CAT(ADD_PAREN_1 x, _END) 
#define ADD_PAREN_1(A, B, C) ((A, B, C)) ADD_PAREN_2 
#define ADD_PAREN_2(A, B, D) ((A, B, C)) ADD_PAREN_1 
#define ADD_PAREN_1_END 
#define ADD_PAREN_2_END 
#define OUTPUT1 ADD_PAREN(INPUT) 

这是类似的,他们如何做到这一点的提升。请参阅BOOST_FUSION_ADAPT_ASSOC_STRUCT_FILLER宏如何使用here

+1

“在Visual Studio中,您可能会看到不同的结果,因为它们的预处理器以神秘的方式工作。”对!刚刚做了一些繁重的预处理器工作,包括许多连接和重新扩展新形成的宏调用,并且发现了许多关于其预处理器的有趣事实... :)(例如,它将'__VA_ARGS__'视为单个标记,除非您展开它。)我通常调用你的'X'宏'EXPAND_ARG'。 +1 – GManNickG

+0

@Paul:非常感谢你的回答。额外扫描的窍门确实解决了这个问题。 – Mark