visual c++预处理器标准行为
visual C++ Preprocessor Standard Behaviour
我正在研究关于预处理器确切行为的c++标准(我需要实现某种c++预处理器)。根据我的理解,下面我举的例子(帮助我理解)应该是有效的:
#define dds(x) f(x,
#define f(a,b) a+b
dds(eoe)
su)
我希望像宏调用dds(eoe)
这样的第一个函数被f(eoe,
替换(注意替换字符串中的逗号),然后在重新扫描输入时将其视为f(eoe,su)
。
但是vc++ 2010的测试给了我这个(我告诉vc++输出预处理文件):
eoe+et_leoe+et_l
su)
这是反直觉的,显然是不正确的。是vc++ 2010的bug还是我对c++标准的误解?特别是,像我这样在替换字符串的末尾放一个逗号是不正确的吗?我对c++标准语法的理解是任何preprocessing-token
都是允许的。
我没有GCC或其他版本的vc++。谁能帮我验证一下这些编译器
我的答案对C预处理器是有效的,但是根据c++预处理器与C预处理器相同吗?
选自C, A Reference Manual, 5th edition:
当遇到类函数的宏调用时,整个宏调用被调用在参数处理之后,由主体的副本替换。参数处理步骤如下。实际参数令牌字符串是与相应的形式参数名相关联。的副本然后构造主体,其中每次出现一个形式参数Name被替换为实际参数标记序列的副本与之相关。然后,主体的这个副本替换宏呼叫[…一旦宏调用被扩展,将扫描宏调用在展开的开始处恢复,以便宏的名称可以在展开符中识别,以便进一步使用宏更换。
[…调用宏的方式是写它的名字,左括号,然后每个形式参数的实际参数符号序列,然后是右括号。实际的参数标记序列为以逗号分隔。
基本上,这一切都归结为预处理器是否只在前一个扩展中重新扫描进一步的宏调用,或者它是否继续读取扩展后出现的令牌。
这可能很难想象,但我相信在您的示例中应该发生的情况是,宏名称f
在重新扫描期间被识别出来,并且由于随后的令牌处理显示了f()
的宏调用,因此您的示例是正确的,应该输出您所期望的内容。GCC和clang给出正确的输出,根据这个推理,这也是有效的(并产生等效的输出):
#define dds f
#define f(a,b) a+b
dds(eoe,su)
的确,两个例子中的预处理输出是相同的。至于你用vc++得到的输出,我会说你发现了一个bug。
这与C99第6.10.3.4节以及c++标准第16.3.4节一致,重新扫描和进一步替换:
替换列表中所有参数替换完成后,#和##处理已经完成,所有位置标记预处理令牌都被删除。然后,重新扫描产生的预处理令牌序列,以及所有后续的预处理源文件的标记,以便替换更多的宏名称。
据我所知,标准的[cpp.subst/rescan]
部分中没有任何内容使您所做的事情非法,并且clang和gcc将其扩展为eoe+su
是正确的,并且MSC (Visual c++)行为必须报告为错误。
我没能让它工作,但我设法为你找到一个丑陋的MSC解决方案,使用变量-你可能会发现它有帮助,或者你可能没有,但在任何情况下,它是:
#define f(a,b) (a+b
#define dds(...) f(__VA_ARGS__)
展开为:
(eoe+
su)
当然,这在gcc和clang下是行不通的。
嗯,我看到的问题是,预处理器做以下
ddx(x)变成f(x,
然而,f(x)也被定义了(即使它被定义为f(a,b)),所以f(x)展开为x+垃圾。
所以ddx(x)最终转换成x +垃圾(因为你定义了f(某个东西,)。
你的dds(eoe)实际上展开成a+b,其中a是eoe, b是et_l。不管出于什么原因,它会重复两次:)。
这个场景是编译器特定的,取决于预处理器选择如何处理定义展开
- #定义c-预处理器常量..我做错了什么
- 预处理器:插入结构名称中的前一个行号
- 如何在c++中实现处理器调度模拟器
- 使用CMake检测支持的C++标准
- 如何理解C++标准N3337中的expr.const.cast子句8
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 编译标准库类型
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- C/C++预处理器是否可以检测一些编译器选项
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 在clang++预处理器中确定gcc工具链版本
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 不同/较旧的处理器运行c++代码的方式是否不同
- 铸造标准::有没有回到原来的类型
- 用于交叉编译和CMake的预处理器宏的单元测试
- 有C++标准库 ifdef 或 ifndef 预处理器指令吗?
- 使用不带标准库的预处理器连接C++源文件
- 不能禁止使用预处理器语句的标准函数吗?
- c 预处理器 - C++ #include查找是否有任何标准要求
- visual c++预处理器标准行为