我正在处理的代码使用一些非常复杂的宏voodoo来生成代码,但最后有一个看起来像这样的构造
#define ARGS 1,2,3
#define MACROFUNC_OUTER(PARAMS) MACROFUNC_INNER(PARAMS)
#define MACROFUNC_INNER(A,B,C) A + B + C
int a = MACROFUNC_OUTER(ARGS);
得到的是得到的
int a = 1 + 2 + 3;
这适用于它最初为GHS编写的编译器以及GCC,但是MSVC(2008)将 PARAMS
视为一个不会扩展的单个预处理标记,然后将 A
设置为整个 PARAM
和 B
以及 C
一无所获 . 结果就是这样
int a = 1,2,3 + + ;
虽然MSVC警告 not enough actual parameters for macro 'MACROFUNC_INNER'
.
-
是否有可能让MSVC通过一些技巧进行扩展(另一层宏强制进行第二次扩展,一些放置好##或#,......) . 承认改变构造工作的方式不是一种选择 . (即:我可以自己解决问题吗?)
-
C标准对这种角落案例有什么看法?我无法在C11规范中找到明确告诉如何处理包含参数列表的参数的任何内容 . (即:我可以与代码的作者争论,他必须再次编写它,或者只是MVSC不符合?)
3 回答
MSVC不符合要求 . 该标准实际上是明确的,虽然它没有必要提到这个特殊情况,这并不例外 .
遇到类似函数的宏调用时,预处理器:
§6.10.3/ 11标识了参数,这些参数可能是由非受保护逗号分隔的标记的空序列(如果逗号在括号()内,则受到保护) .
§6.10.3.1/ 1对宏体进行第一次传递,用相应的完全宏扩展参数替换
#
或##
操作中未使用的每个参数 . (在此步骤中,宏体中没有其他替换 . )§6.10.3.4/ 1重新扫描替换的替换标记序列,根据需要执行更多宏替换 .
(以上几乎忽略了字符串化(
#
)和标记连接(##
),这与此问题无关 . )这种操作顺序明确地导致了编写软件的人所期望的行为 .
显然(根据@dxiv,并验证here),以下符合标准的解决方法适用于某些版本的MS Visual Studio:
供参考,来自C11标准的实际语言,跳过对
#
和##
处理的引用:C11表示每个外观都是一个类似对象的宏名
[6.10.3 / 9]
类似函数的宏它说:
[6.10.3 / 4]
还有这个:
[6.10.3 / 11]
还有这个:
[6.10.3.1/1]
一般来说宏也说这个:
[6.10.3.4/1]
MSVC不正确在重新扫描此类宏的扩展之前,将参数扩展为类似函数的宏 . 似乎不太可能有任何简单的解决方法 .
UPDATE:
然而,根据@ dxiv的回答,可能毕竟有一个解决方案 . 他在符合标准的行为方面的解决方案的问题在于,需要比实际执行的扩展更多的扩展 . 这很容易提供 . 他的方法的这种变化适用于GCC,因为它应该是,并且因为它基于dxiv声称与MSVC一起工作的代码,它似乎也可以在那里工作:
我当然把它变得比它需要的更通用了,但是如果你有很多这些可以处理的话,这可能对你有用 .
足够多,以下似乎适用于MSVC(2010年和2015年测试) .
我不知道它应该按标准的字母工作,事实上我有预感不是 . 仍然可以为MSVC有条件地编译,作为一种解决方法 .
[编辑] P.S.正如评论中指出的,以上是(另一种)非标准的MSVC行为 . 相反,@rici和@JohnBollinger在相应回复中发布的替代解决方法是合规的,因此建议使用 .