如果以范围开头,__ VA_ARGS__扩展将失败

时间:2017-06-28 06:23:59

标签: c++11 visual-studio-2015

我想在宏中使用__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_CLASSUSE_VARIADIC_CONVERTER都已定义。

有点漫无边际

我发现有关以前版本的Visual Studio无法展开__VA_ARGS__的信息,但由于代码可以正常使用enum而不是enum class,我猜Visual Studio 2015确实修复了至少一些以前的问题。在一个真正的应用程序中,我会使用类似的宏一次,所以这可以节省我很多行,并防止忘记添加代码 稍后添加的枚举值(添加到NAME_LIST就足够了。)

小样本

这会导致上述错误。注释掉定义USE_ENUM_CLASSUSE_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;
};

问题

如何重写/扩展宏以避免这些错误?

2 个答案:

答案 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::name1name1成员的有效参考资料:其他成员无法使用他们不合格的名字。

此更改为enumenum将其成员导出到封闭范围内,因此e::name1name2引用成员。然后,表达式被解析为由逗号运算符分隔的多个值。现在这在语法上是有效的,但仍然没有做你想要的 - 它只处理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