通过可变参数创建枚举?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过可变参数创建枚举?相关的知识,希望对你有一定的参考价值。

在其他语言中,您可以指定枚举以及状态,例如:

public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);
   ...

这在中不难模仿-只是它很容易变得有些冗长。

我知道这已经被问过,并且存在类似的问题,例如在Build an enum using variadic template parameters中,但我想再问一遍,并有稍微不同的观点。

考虑以下“手写”方法:

#pragma warning(error : 4062)    

enum tvtype {
    black_and_white,
    color
};    

struct TvType {
    tvtype tvtype_;
    constexpr TvType(tvtype type) : tvtype_{type} {

    }
    constexpr operator tvtype() const {
        return tvtype_;
    }
};

constexpr TvType BlackAndWhite(black_and_white);
constexpr TvType Color(color);

bool hasColor(TvType tv) {
    switch (tv) {
        case BlackAndWhite:
        return false;
        //case Color:
        //return true;
    };

    return false;
}

Try it yourself(在开关的两行中进行注释以使其编译)

此方法运行良好-只是有些冗长。

使用此方法,现在应该可以将一些额外的信息与enum值一起存储,例如字符串表示形式,也许还添加函数,等等。

也因为它实际上是下面的enum编译器可能会检查是否已在开关中检查所有情况 )。

[我对生成enum的通用/模板化方式或满足编译器检查的开关和可扩展性要求的类似方式很感兴趣。

但是为了对上面的内容进行模板化,似乎应该以(编译时)编程方式生成枚举,这会导致问题:

是否有可能通过fold表达式创建枚举,或者是否通过某种递归模板使它失败?

答案

是否有可能从fold表达式创建枚举,还是从某种递归模板中使枚举失败?

不使用可变参数模板。模板参数可以是类型或常量值。您不能用symbol粘贴到enum块中。

但是,您可以使用宏来实现。唯一的麻烦是,尽管C ++ 11具有称为“可变宏”的东西,但它非常有限。

#define MY_MACRO(...) enum { __VA_ARGS__ }

[您无法像模板一样对可变参数进行任何形式的转换,它实际上只是将__VA_ARGS___替换为...代表的所有标记。

但是,使用这个很好的答案中的技术:https://stackoverflow.com/a/11763277/1863938,您可以支持“最多N个”可变参数解决方案。使用它,您可以创建一个宏,该宏可以将样板类用作最多N个值的模板。以下是最多4个符号:

#define _JENUM_GET_MACRO(_1,_2,_3,_4,NAME,...) NAME

#define _JENUM_VALUE_DEF1(_1) _##_1##_v_
#define _JENUM_VALUE_DEF2(_1,_2) _JENUM_VALUE_DEF1(_1), _JENUM_VALUE_DEF1(_2)
#define _JENUM_VALUE_DEF3(_1,_2,_3) _JENUM_VALUE_DEF1(_1), _JENUM_VALUE_DEF1(_2), _JENUM_VALUE_DEF1(3)
#define _JENUM_VALUE_DEF4(_1,_2,_3,_4) _JENUM_VALUE_DEF1(_1), _JENUM_VALUE_DEF1(_2), _JENUM_VALUE_DEF1(3), _JENUM_VALUE_DEF1(_4)
#define _JENUM_VALUE_DEF(...) _JENUM_GET_MACRO(__VA_ARGS__, _JENUM_VALUE_DEF4, _JENUM_VALUE_DEF3, _JENUM_VALUE_DEF2, _JENUM_VALUE_DEF1)(__VA_ARGS__)
#define _JENUM_ENUM_DEF(name, ...) enum _##name##_e_ { _JENUM_VALUE_DEF(__VA_ARGS__) };

#define _JENUM_CLASS_DEF(name) struct name {
    _##name##_e_ _value;
    constexpr name(const name& that) : _value(that._value) { }
    constexpr name(_##name##_e_ _value) : _value(_value) { }
    constexpr operator _##name##_e_() const { return _value; }
};

#define _JENUM_CONST_DEF1(name, _1) constexpr name _1(_JENUM_VALUE_DEF1(_1));
#define _JENUM_CONST_DEF2(name, _1,_2) _JENUM_CONST_DEF1(name, _1) _JENUM_CONST_DEF1(name, _2)
#define _JENUM_CONST_DEF3(name, _1,_2,_3) _JENUM_CONST_DEF1(name, _1) _JENUM_CONST_DEF1(name, _2) _JENUM_CONST_DEF1(name, _3)
#define _JENUM_CONST_DEF4(name, _1,_2,_3,_4) _JENUM_CONST_DEF1(name, _1) _JENUM_CONST_DEF1(name, _2) _JENUM_CONST_DEF1(name, _3) _JENUM_CONST_DEF1(name, _4)
#define _JENUM_CONST_DEF(name, ...) _JENUM_GET_MACRO(__VA_ARGS__, _JENUM_CONST_DEF4, _JENUM_CONST_DEF3, _JENUM_CONST_DEF2, _JENUM_CONST_DEF1)(name, __VA_ARGS__)

#define JENUM(name, ...)
    _JENUM_ENUM_DEF(name, __VA_ARGS__)
    _JENUM_CLASS_DEF(name)
    _JENUM_CONST_DEF(name, __VA_ARGS__)

...只需按照模式添加更多重载

然后,使用:

JENUM(TvType, BlackAndWhite, Color);

现在,如果要添加方法而不是其他方法,只继承它比尝试在宏中处理任何事情都容易:

struct TvTypeEx : public TvType {
    using TvType::TvType;
    TvTypeEx(TvType that) : TvType(that) { }
    bool hasColor() const { return *this == Color; }
};

现在您可以做类似的事情:

TvTypeEx tv = Color;
return tv.hasColor() ? GetColorContent() : GetBlackAndWhiteContent();

演示:https://godbolt.org/z/rdqS65

以上是关于通过可变参数创建枚举?的主要内容,如果未能解决你的问题,请参考以下文章

可变参数宏与枚举

W6-junit泛型枚举增强for可变参数反射[JavaWeb]

带有红宝石集合/可枚举的酷技巧和富有表现力的片段[关闭]

(14)jdk1.5开始的一些新特性:静态导入,增强for循环,可变参数,自动装箱/拆箱,枚举类型

面向对象下

关于Java可变参数问题?