通过可变参数创建枚举?

问题描述 投票:0回答:1

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

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表达式创建枚举,或者是否通过某种递归模板使它失败?

c++ enums c++17 template-meta-programming constexpr
1个回答
1
投票

是否有可能从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

© www.soinside.com 2019 - 2024. All rights reserved.