带有可变参数的嵌套宏在 GCC 中编译,但在 MSVC 中不编译
Posted
技术标签:
【中文标题】带有可变参数的嵌套宏在 GCC 中编译,但在 MSVC 中不编译【英文标题】:Nested macro with variadic arguments does compile in GCC but not in MSVC 【发布时间】:2019-04-27 18:07:50 【问题描述】:我使用 boost/预处理器创建了一个宏来重复创建代码。原因是我在一个非常受限的目标硬件上工作,编译器非常受限,不支持数组。
我想出的代码如下:
# define ESC(...) __VA_ARGS__
# define IF_BODY(n, condition, lhs, rhs, arg1, arg2) \
if (condition > n) \
lhs##n.arg1 = rhs[n].arg1; \
lhs##n.arg2 = rhs[n].arg2; \
# define IF_BODY_(A, B) IF_BODY(A, B)
# define IF_QUERY(z, n, vars) IF_BODY_(n, ESC vars)
BOOST_PP_REPEAT(3, IF_QUERY, \
(index, variableName, otherVariableName, latitude, longitude))
它的目的是创建几个带有升序条件的 if 查询。
我用 gcc 在 Godbolt 上测试了这段代码,它就像一个魅力,你可以see here。 现在,当我在 MSVC 中尝试相同的操作时,它不会编译。有错误是 C2065,例如未声明“变量名”。 See it here on godbolt.
这是为什么呢?这是 MSVC 中的错误吗? MSVC 不支持这些类型的宏吗?我的代码有错误吗?
【问题讨论】:
不过,您很可能不知道,您可以告诉编译器资源管理器将 boost 添加为库。查看书本图标 我知道。但是我手动添加了标题以检查问题是否与godbolt有关。这就是问题的最终结果。很抱歉造成混乱,感谢您的推荐。 【参考方案1】:我的调查让我相信这是 MSVC 中的一个错误。
如果我将代码更改为以下内容并对文件进行预处理(请注意,我的 'coord' 和 'variableName'、'otherVariableName' 标识符只是为了使您的代码的这些部分匹配某些内容,并注意如何我已经颠倒了你的 IF_BODY 和 IF_BODY_ 并且 IF_BODY_ 被 #if 0) 注释掉了:
#include <boost/preprocessor/repeat.hpp>
struct coord
int latitude, longitude;
;
# define ESC_(...) __VA_ARGS__
#define ESC(vars) ESC_ vars
#if 0
# define IF_BODY_(n, condition, lhs, rhs, arg1, arg2) \
if (condition > n) \
lhs##n.arg1 = rhs[n].arg1; \
lhs##n.arg2 = rhs[n].arg2; \
#endif
# define IF_BODY(A, B) IF_BODY_(A, ESC(B))
# define IF_QUERY(z, n, vars) IF_BODY(n, vars)
int main()
int index;
coord variableName0, otherVariableName0;
coord variableName1, otherVariableName1;
coord variableName1, otherVariableName1;
BOOST_PP_REPEAT(3, IF_QUERY, (index, variableName, otherVariableName, latitude, longitude))
我得到以下信息:
int main()
int index;
coord variableName0, otherVariableName0;
coord variableName1, otherVariableName1;
coord variableName1, otherVariableName1;
IF_BODY_(0, index, variableName, otherVariableName, latitude, longitude)
IF_BODY_(1, index, variableName, otherVariableName, latitude, longitude)
IF_BODY_(2, index, variableName, otherVariableName, latitude, longitude)
请注意,宏调用具有正确数量的参数。但是,如果我将 #if 0 更改为 #if 1 我会得到以下编译器输出: tester\tester.cpp(35):警告 C4003:没有足够的参数用于类似函数的宏调用 'IF_BODY_'
查看这种情况下的预处理结果:
int main()
int index;
coord variableName0, otherVariableName0;
coord variableName1, otherVariableName1;
coord variableName1, otherVariableName1;
if (index, variableName, otherVariableName, latitude, longitude > 0) 0. = [0].; 0. = [0].;
if (index, variableName, otherVariableName, latitude, longitude > 1) 1. = [1].; 1. = [1].;
if (index, variableName, otherVariableName, latitude, longitude > 2) 2. = [2].; 2. = [2].;
似乎 MSVC 在执行 ESC 扩展之前执行宏变量分配,B 的所有内容都被分配给“条件”,而不是被分解为正确的 IF_BODY_ 参数。我将通过 MSVS 帮助 -> 发送反馈 -> 报告问题机制将此作为反馈提交。
--
好的,我已经设法找到解决方案,但它非常丑陋。它需要对 BOOST_PP_REPEAT 系列进行可变参数扩展(我在这里只实现了部分)。可能值得将此作为提升的建议提交,但我不确定 boost.preprocessor 是否仍然保持不变。并注意 ESC_/ESC 宏在此版本中是如何消失的。
#include <boost/preprocessor/repeat.hpp>
# define BOOST_PP_REPEAT_1_1_V(m, d, ...) m(2, 0, d, __VA_ARGS__)
# define BOOST_PP_REPEAT_1_2_V(m, d, ...) BOOST_PP_REPEAT_1_1_V(m, d, __VA_ARGS__) m(2, 1, d, __VA_ARGS__)
# define BOOST_PP_REPEAT_1_3_V(m, d, ...) BOOST_PP_REPEAT_1_2_V(m, d, __VA_ARGS__) m(2, 2, d, __VA_ARGS__)
# define BOOST_PP_REPEAT_1_4_V(m, d, ...) BOOST_PP_REPEAT_1_3_V(m, d, __VA_ARGS__) m(2, 3, d, __VA_ARGS__)
# define BOOST_PP_REPEAT_1_I_V(c, m, d, ...) BOOST_PP_REPEAT_1_ ## c##_V(m, d, __VA_ARGS__)
# define BOOST_PP_REPEAT_1_V(c, m, d, ...) BOOST_PP_REPEAT_1_I_V(c, m, d, __VA_ARGS__)
#define BOOST_PP_REPEAT_V BOOST_PP_CAT(BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4)), _V)
struct coord
int latitude, longitude;
;
#if 1
# define IF_BODY_(n, condition, lhs, rhs, arg1, arg2) \
if (condition > n) \
lhs##n.arg1 = rhs[n].arg1; \
lhs##n.arg2 = rhs[n].arg2; \
#endif
#define ESC_(...) __VA_ARGS__
#define ESC(a) ESC_(a)
#define IF_BODY(a, ...) ESC_(IF_BODY_(a, __VA_ARGS__))
# define IF_QUERY(z, n, ...) IF_BODY(n, __VA_ARGS__)
int main()
int index = 0;
coord variableName0, variableName1, variableName2;
coord otherVariableName[3];
BOOST_PP_REPEAT_V(3, IF_QUERY, index, variableName, otherVariableName, latitude, longitude)
新代码扩展为:
int main()
int index = 0;
coord variableName0, variableName1, variableName2;
coord otherVariableName[3];
if (index > 0) variableName0.latitude = otherVariableName[0].latitude; variableName0.longitude = otherVariableName[0].longitude;
if (index > 1) variableName1.latitude = otherVariableName[1].latitude; variableName1.longitude = otherVariableName[1].longitude;
if (index > 2) variableName2.latitude = otherVariableName[2].latitude; variableName2.longitude = otherVariableName[2].longitude;
【讨论】:
非常感谢您的调查!你认为有办法绕过这个错误吗? 看看我的扩展答案,我确实找到了解决方法。但就像我说的那样,它非常丑陋。更好的方法可能是使用编译器(甚至 gcc)而不是反对和使用 boost 预处理器的 TUPLE 宏(这也需要摆脱 ESC 以便将分组参数作为一个单元传递)。 不错!还要感谢 TUPLE 宏的提示,我现在使用它,它就像一个魅力。 This is how it looks like now.以上是关于带有可变参数的嵌套宏在 GCC 中编译,但在 MSVC 中不编译的主要内容,如果未能解决你的问题,请参考以下文章