我想在宏中使用__VA_ARGS__
来获得相当于写几行if(a==b) return c
的内容。
编译“小样本”时,
失败identifier "name2" is undefined (Line 18)
C2065 'name3': undeclared identifier (Line 18)
C2065 'name2': undeclared identifier (Line 18)
如果USE_ENUM_CLASS
和USE_VARIADIC_CONVERTER
都已定义。
我发现有关以前版本的Visual Studio无法展开__VA_ARGS__
的信息,但由于代码可以正常使用enum
而不是enum class
,我猜Visual Studio 2015确实修复了至少一些以前的问题。在一个真正的应用程序中,我会使用类似的宏一次,所以这可以节省我很多行,并防止忘记添加代码
稍后添加的枚举值(添加到NAME_LIST
就足够了。)
这会导致上述错误。注释掉定义USE_ENUM_CLASS
或USE_VARIADIC_CONVERTER
的行,它将编译。
#define NAME_LIST name1,name2,name3
#define USE_ENUM_CLASS
#define USE_VARIADIC_CONVERTER
#ifdef USE_ENUM_CLASS
enum class e
#else
enum e
#endif
{
name1, name2, name3
};
e int_to_e(int const i)
{
#ifdef USE_VARIADIC_CONVERTER
#define POPULATE_INT_TO_E(...) if(i==static_cast<int>(e::__VA_ARGS__)) return e::__VA_ARGS__;
POPULATE_INT_TO_E(NAME_LIST);
#else
#define POPULATE_INT_TO_E(x) if(i==static_cast<int>(e::x)) return e::x
POPULATE_INT_TO_E(name1);
POPULATE_INT_TO_E(name2);
POPULATE_INT_TO_E(name3);
#endif
throw - 1;
}
void main(void)
{
}
使用
替换枚举定义时发生同样的错误struct e
{
enum
{
name1, name2, name3
};
};
或
namespace e
{
enum
{
name1, name2, name3
};
}
或
namespace e
{
int const name1 = 1;
int const name2 = 2;
int const name3 = 3;
};
如何重写/扩展宏以避免这些错误?
答案 0 :(得分:1)
失败的原因是因为__VA_ARGS__
没有按照你的想法行事。我怀疑你是否期望类似于变量模板的行为(在扩展包时重复整个表达式),但它实际上只是简单地转储所有参数。
因此当POPULATE_INT_TO_E(NAME_LIST)
(可变版本)扩展时,您会得到:
if(i==static_cast<int>(e::name1, name2, name3))
return e::name1, name2, name3;
这不能与enum class
一起使用,因为其成员是作用域的,只有e::name1
是name1
成员的有效参考资料:其他成员无法使用他们不合格的名字。
此更改为enum
:enum
将其成员导出到封闭范围内,因此e::name1
和name2
引用成员。然后,表达式被解析为由逗号运算符分隔的多个值。现在这在语法上是有效的,但仍然没有做你想要的 - 它只处理name3
。
现在我们可以做些什么来实际修复它?使用预处理器进行迭代非常简单,因为它缺少任何形式的循环,并且很难实现递归。
但是,一种简单而强大的常见技术是X-macros。而不是单独列出您的数据,将其预先包装在X
(或任何您喜欢的)宏中:
#define NAME_LIST \
X(name1) \
X(name2) \
X(name3)
然后,您可以定义X
,展开NAME_LIST
,然后您将拥有以下行为:
#define X(name_) \
if(i==static_cast<int>(e::name_)) return e::name_;
NAME_LIST
#undef X
...扩展为:
if(i==static_cast<int>(e::name1)) return e::name1;
if(i==static_cast<int>(e::name2)) return e::name2;
if(i==static_cast<int>(e::name3)) return e::name3;
答案 1 :(得分:0)
另一个答案是迭代 VA_ARGS 。
#define NAME_LIST e::name1,e::name2,e::name3
#define USE_ENUM_CLASS
#define USE_VARIADIC_CONVERTER
#ifdef USE_ENUM_CLASS
enum class e
#else
enum e
#endif
{
name1, name2, name3
};
e int_to_e(int const i)
{
#ifdef USE_VARIADIC_CONVERTER
#define POPULATE_INT_TO_E(...) \
e _arr[] = {__VA_ARGS__}; \
int _i; \
for (_i = 0; _i < sizeof(_arr)/sizeof(_arr[0]); _i++) { \
if(i==static_cast<int>(_arr[_i])) return _arr[_i]; \
}
POPULATE_INT_TO_E(NAME_LIST);
#else
#define POPULATE_INT_TO_E(x) if(i==static_cast<int>(e::x)) return e::x
POPULATE_INT_TO_E(name1);
POPULATE_INT_TO_E(name2);
POPULATE_INT_TO_E(name3);
#endif
throw - 1;
}
int main(void)
{
}
但您必须将NAME_LIST定义为e::name1,e::name2,e::name3