我们应该如何使用C ++标准解释以下宏定义?请注意,主要问题是AA
的替换列表包含嵌入式逗号(for, S
)
#define AA for, S //<---note the embedded comma
#define VALUE_TO_STRING(x) ^x!
#define VALUE(x) VALUE_TO_STRING(x)
int _tmain(int argc, _TCHAR* argv[])
{
VALUE(AA)
return 0;
}
我已经使用VC ++ 2010进行了测试,上面的最终结果如下所示,没有任何错误,但我解释了使用C ++ 03得出结果所需的步骤(或C ++ 11)标准:
int wmain(int argc, _TCHAR* argv[])
{
^for, S!
return 0;
}
我已经用VC ++ 2010做了一些一步一步的测试。首先,我评论了第二个宏,看看第一步发生了什么:
#define AA for, S
//#define VALUE_TO_STRING(x) ^x!
#define VALUE(x) VALUE_TO_STRING(x)
宏替换是直接的,并产生一个看起来像另一个具有两个参数的函数式宏的序列:
int wmain(int argc, _TCHAR* argv[])
{
VALUE_TO_STRING(for, S)
return 0;
}
根据[cpp.rescan],下一步是重新扫描这个以获取更多宏名称。这里的问题是这个新宏应该被解释为一个类似函数的宏,带有2个参数或1个参数“for, S
”。
正常的解释是考虑VALUE_TO_STRING()给出2个无效的参数,因此产生预处理器错误。但是为什么VC ++会出现没有任何错误的结果呢?显然,VC ++采取的第二步是将for, S
视为单个参数,这个参数没有意义,也没有被C ++标准定义。
答案 0 :(得分:2)
我已经使用VC ++ 2010进行了测试......
MS的预处理器从未成为标准。 They phrase it this odd way:C99 __func__和预处理器规则 ...对于C99预处理器规则,&#34;部分&#34;列出是因为支持可变参数宏。
换句话说,&#34;我们支持可变参数宏;因此,我们有资格作为部分合规&#34;。 MS团队认为预处理器的AFAIK标准符合性非常低。所以我不倾向于使用VC或VC ++作为标准预处理器的模型。 gcc是这里标准预处理器的更好模型。
由于这是关于预处理器的,我将把这个故事集中在这个片段上:
#define AA for, S
#define VALUE_TO_STRING(x) ^x!
#define VALUE(x) VALUE_TO_STRING(x)
VALUE(AA)
我将在这里引用ISO-14882 2011,其使用的数字与1998/2003不同。使用这些数字,这里是从扩展步骤开始,一步一步发生的事情......除了我在这里不相关的步骤之外。
预处理器看到VALUE(AA)
,它是对先前定义的类函数宏的函数式调用。所以它首先做的是参数识别,参考16.3第4段:
[if not variadic]在类函数宏的调用中,参数的数量(包括那些由没有预处理标记组成的参数)应该等于宏定义中的参数数量
......以及16.3.1第1段的一部分:
在识别出类似函数宏的调用参数后,
在此步骤中,预处理器确定确实存在一个参数,即宏是使用一个参数定义的,并且参数x
与调用参数AA
匹配。到目前为止,参数匹配和x is AA
就是这样。
然后我们进入下一步,即参数扩展。关于此步骤,替换列表唯一真正重要的是参数在其中的位置,以及参数是否是字符串化(# x
)或粘贴(x ## ...
或... ## x
)。如果替换列表中的参数都不是,则那些参数被扩展(参数的字符串化或粘贴版本在此步骤中不计算)。在调用之前发生任何其他有趣事情之前,首先进行此扩展,并且就像预处理器仅扩展调用参数一样。
在这种情况下,替换列表为VALUE_TO_STRING(x)
。同样,VALUE_TO_STRING
可能是一个类似函数的宏,但由于我们现在正在进行论证扩展,所以我们真的不在乎。我们唯一关心的是x
就在那里,并且它没有被字符串化或粘贴。 x
正在调用AA
,因此预处理器会评估AA
,就好像AA
在一行而不是VALUE(AA)
。 AA
是一个类似对象的宏,可以扩展为for, S
。因此,替换列表会转换为VALUE_TO_STRING(for, S)
。
这是16.3.1第1段的其余部分:
替换列表中的一个参数,除非在其中包含的所有宏都被展开后,[stringified或pasted]被相应的参数替换,就好像它们形成了预处理文件的其余部分一样
到目前为止一切顺利。但现在我们在16.3.4中达到下一部分:
替换列表中的所有参数都被替换后,[此处未发生的事情]生成的预处理标记序列 重新扫描,以及源文件的所有后续预处理标记,以替换更多的宏名称。
这部分评估VALUE_TO_STRING(for, S)
,好像那是预处理令牌集(除了它还暂时忘记VALUE
是每16.3.4p2的一个宏,但是它没有进入在这里玩)。该评估将VALUE_TO_STRING识别为类似函数的宏,被调用为一个,因此参数识别再次开始。仅在此处,VALUE_TO_STRING被定义为接受一个参数,但是使用两个参数调用。那失败了16.3 p 4。
答案 1 :(得分:0)
我认为答案是为了扩大。
您对预处理器扩展的模拟,即您选择哪个宏首先扩展,在我看来与预处理器的作用不相符。
我作为预处理器(根据我最初认为的标准,但评论相矛盾),将按此顺序扩展您的代码:
VALUE(AA)
VALUE_TO_STRING(AA)
^AA!
^for, S!
这匹配原始代码的预处理器的结果。
请注意,按此顺序,它永远不会看到代码VALUE_TO_STRING(for, S)
,它最接近的是VALUE_TO_STRING(AA)
。该代码不会引起有关参数数量的问题。
我没有引用标准中的任何内容,我认为你的报价就足够了。
正如下面的评论所述,我的答案现在是尝试如何解释结果,而不假设符合预处理器。任何解释符合行为的答案肯定更好。
顺便说一下,作为编译器,我可能不会理解
^anything!
作为从值中创建字符串的方法。但这不是问题,我假设当你准备了最小的例子时,意思就失去了。那当然完全没问题。然而,它可能会影响扩展,如果它扩展到引用的宏名称,例如"AA"
。这将停止扩张,结果可能揭示发生的事情。