是否可以确定c ++枚举类的元素数量?

时间:2013-02-20 20:27:11

标签: c++ c++11 cardinality enum-class

是否可以确定c ++ enum class的基数:

enum class Example { A, B, C, D, E };

我尝试使用sizeof,但是,它返回枚举元素的大小。

sizeof(Example); // Returns 4 (on my architecture)

是否有获得基数的标准方法(在我的示例中为5)?

14 个答案:

答案 0 :(得分:52)

不是直接的,但您可以使用以下技巧:

enum class Example { A, B, C, D, E, Count };

然后基数可以(int)Example::Count

当然,如果你从0开始自动分配枚举的值,这只能很好地工作。如果不是这样的话,你可以手动将正确的基数分配给Count,这与必须保持不同一个单独的常数:

enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };

一个缺点是编译器允许你使用Example::Count作为枚举值的参数 - 所以如果你使用它就要小心! (不过,我个人认为这在实践中并不是一个问题。)

答案 1 :(得分:15)

constexpr auto TEST_START_LINE = __LINE__;
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one 
    ONE = 7
  , TWO = 6
  , THREE = 9
};
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;

这源于UglyCoder's answer但以三种方式改进。

  • type_safe枚举中没有额外的元素(BEGINSIZE)(Cameron's answer也存在此问题。)
    • 编译器不会抱怨它们在switch语句中丢失(一个重大问题)
    • 他们不能无意中传递给期望你的枚举的功能。 (不是常见问题)
  • 不需要使用铸造。 (Cameron's answer也有这个问题。)
  • 减法不会影响枚举类类型的大小。

UglyCoder's优于Cameron's answer优势,可以为枚举数分配任意值。

问题(与UglyCoder共享但不与Cameron共享)是因为它使换行符和注释显着...这是意料之外的。因此,有人可以添加带有空格或评论的条目,而无需调整TEST_SIZE的计算。

答案 2 :(得分:7)

(src) => Splitter.SplitTextByEachDelimiter({";d:",";"})(src){1}

答案 3 :(得分:3)

您可以尝试的一个技巧是在列表末尾添加枚举值并将其用作大小。在你的例子中

enum class Example { A, B, C, D, E, ExampleCount };

答案 4 :(得分:2)

不,你必须在代码中写下它。

答案 5 :(得分:2)

有一个基于X()的技巧 - 宏:image,你有以下枚举:

enum MyEnum {BOX, RECT};

将其重新格式化为:

#define MyEnumDef \
    X(BOX), \
    X(RECT)

然后以下代码定义枚举类型:

enum MyEnum
{
#define X(val) val
    MyEnumDef
#undef X
};

以下代码计算枚举元素的数量:

template <typename ... T> void null(T...) {}

template <typename ... T>
constexpr size_t countLength(T ... args)
{
    null(args...); //kill warnings
    return sizeof...(args);
}

constexpr size_t enumLength()
{
#define XValue(val) #val
    return countLength(MyEnumDef);
#undef XValue
}

...
std::array<int, enumLength()> some_arr; //enumLength() is compile-time
std::cout << enumLength() << std::endl; //result is: 2
...

答案 6 :(得分:2)

可以通过std :: initializer_list的技巧来解决:

#define TypedEnum(Name, Type, ...)                                \
struct Name {                                                     \
    enum : Type{                                                  \
        __VA_ARGS__                                               \
    };                                                            \
    static inline const size_t count = []{                        \
        static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \
    }();                                                          \
};

用法:

#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_)
Enum(FakeEnum, A = 1, B = 0, C)

int main()
{
    std::cout << FakeEnum::A     << std::endl
              << FakeEnun::count << std::endl;
}

答案 7 :(得分:2)

还有另一种不依赖行数或模板的方式。唯一的要求是将枚举值粘贴在自己的文件中,并使预处理器/编译器进行计数,如下所示:

my_enum_inc.h

ENUMVAL(BANANA)
ENUMVAL(ORANGE=10)
ENUMVAL(KIWI)
...
#undef ENUMVAL

my_enum.h

typedef enum {
  #define ENUMVAL(TYPE) TYPE,
  #include "my_enum_inc.h"
} Fruits;

#define ENUMVAL(TYPE) +1
const size_t num_fruits =
  #include "my_enum_inc.h"
  ;

这允许您使用枚举值放置注释,重新分配值,并且不会注入需要在代码中忽略/解释的无效“计数”枚举值。

如果您不关心评论,则不需要多余的文件,并且可以像上面提到的人那样做,例如:

#define MY_ENUM_LIST \
    ENUMVAL(BANANA) \
    ENUMVAL(ORANGE = 7) \
    ENUMVAL(KIWI)

,并将#include "my_enum_inc.h"指令替换为MY_ENUM_LIST,但每次使用后都需要#undef ENUMVAL

答案 8 :(得分:1)

你也可以考虑消除额外元素的static_cast<int>(Example::E) + 1

答案 9 :(得分:1)

对此的另一种“愚蠢”解决方案是:

with
  prep1 (tablename, operation, orgname, fragment) as (
    select tablename, operation, orgname,
           json_object( key 'canonicalName' value orgname,
                        key 'synonyms'
              value nullif(json_arrayagg(synonyms order by synonyms), '[]')
                                  FORMAT JSON  ABSENT ON NULL
                      )
     from   t
     group  by tablename, orgname, operation
   )
, prep2 (tablename, operation, org_str) as (
    select tablename, operation, json_arrayagg(fragment order by orgname)
    from   prep1
    group  by tablename, operation
  )
select tablename, json_objectagg(key operation value org_str) as json_str
from   prep2
group  by tablename
;



TABLENAME JSON_STR                                                                                                                                                                                                                                                                                                                                                                                                                
--------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ITEM      {"add":[{"canonicalName":"Apple Computers","synonyms":["Apple","Apple Inc"]},{"canonicalName":"Google India","synonyms":["Google"]},{"canonicalName":"IBM","synonyms":["IBM Corporation"]}],"update":[{"canonicalName":"Infosys","synonyms":["Infosys Tech"]},{"canonicalName":"Wipro Tech","synonyms":["Wipro Technology"]}],"delete":[{"canonicalName":"IBM","synonyms":["IBM Corporation"]},{"canonicalName":"TCS"}]}
ORG       {"add":[{"canonicalName":"Apple Computers","synonyms":["Apple","Apple Inc"]},{"canonicalName":"Google India","synonyms":["Google"]},{"canonicalName":"IBM","synonyms":["IBM Corporation"]}],"update":[{"canonicalName":"Infosys","synonyms":["Infosys Tech"]},{"canonicalName":"Wipro Tech","synonyms":["Wipro Technology"]}],"delete":[{"canonicalName":"IBM","synonyms":["IBM Corporation"]},{"canonicalName":"TCS"}]}

通过使用enum class Example { A, B, C, D, E }; constexpr int ExampleCount = [] { Example e{}; int count = 0; switch (e) { case Example::A: count++; case Example::B: count++; case Example::C: count++; case Example::D: count++; case Example::E: count++; } return count; }(); 进行编译,如果您省略或重复了任何切换用例,请确保收到编译器警告。它也是constexpr,因此它是在编译时计算的。

但是请注意,即使对于en -Werror=switch,即使枚举的第一个值不为0,默认初始化值也为0。因此,您必须从0开始或显式使用第一个值。

答案 10 :(得分:0)

如果您使用boost的预处理器实用程序,则可以使用BOOST_PP_SEQ_SIZE(...)获得计数。

例如,可以如下定义CREATE_ENUM宏:

#include <boost/preprocessor.hpp>

#define ENUM_PRIMITIVE_TYPE std::int32_t

#define CREATE_ENUM(EnumType, enumValSeq)                                  \
enum class EnumType : ENUM_PRIMITIVE_TYPE                                  \
{                                                                          \
   BOOST_PP_SEQ_ENUM(enumValSeq)                                           \
};                                                                         \
static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count =                     \
                 BOOST_PP_SEQ_SIZE(enumValSeq);                            \
// END MACRO   

然后,调用宏:

CREATE_ENUM(Example, (A)(B)(C)(D)(E));

将生成以下代码:

enum class Example : std::int32_t 
{
   A, B, C, D, E 
};
static constexpr std::int32_t ExampleCount = 5;

这只是针对Boost预处理器工具的表面。例如,您的宏还可以为强类型枚举定义往返字符串转换实用程序和ostream运算符。

更多有关升压预处理器工具的信息: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html


顺便说一句,我碰巧非常同意@FantasticMrFox,即如果使用Count语句,那么在接受的答案中使用的其他switch枚举值将产生编译器警告,令人头痛。我发现unhandled case编译器警告对于更安全的代码维护非常有用,因此我不想破坏它。

答案 11 :(得分:0)

对于C ++ 17,您可以使用lib https://github.com/Neargye/magic_enum中的magic_enum :: enum_count magic_enum :: enum_count()-> 4。

答案 12 :(得分:0)

Reflection TS:枚举(和其他类型)的静态反射

Reflection TS,尤其是latest version of the Reflection TS draft的[reflect.ops.enum] / 2提供了get_enumerators TransformationTrait操作:

[reflect.ops.enum] / 2

template <Enum T> struct get_enumerators

get_enumerators<T>的所有专业都必须满足 TransformationTrait要求(20.10.1)。嵌套类型名为 type指定满足以下条件的元对象类型 ObjectSequence,包含满足Enumerator和 反映由T反映的枚举类型的枚举数。

草案的

[reflect.ops.objseq]涵盖了ObjectSequence个操作,其中特别是[reflect.ops.objseq] / 1涵盖了get_size特质,用于提取元元素的元素数量满足ObjectSequence的对象:

[reflect.ops.objseq] / 1

template <ObjectSequence T> struct get_size;

get_size<T>的所有专业都必须满足 基本特征为UnaryTypeTrait的要求(20.10.1) integral_constant<size_t, N>,其中N是其中的元素数 对象序列。

因此,在Reflection TS被接受并以其当前形式实现时,可以在编译时计算枚举的元素数量,如下所示:

enum class Example { A, B, C, D, E };

using ExampleEnumerators = get_enumerators<Example>::type;

static_assert(get_size<ExampleEnumerators>::value == 5U, "");

我们很可能会看到别名模板get_enumerators_vget_type_v,以进一步简化反映:

enum class Example { A, B, C, D, E };

using ExampleEnumerators = get_enumerators_t<Example>;

static_assert(get_size_v<ExampleEnumerators> == 5U, "");

反射状态TS

正如赫伯·萨特(Herb Sutter)在2018年6月9日召开的ISO C ++委员会夏季会议上的Trip report: Summer ISO C++ standards meeting (Rapperswil)所述,Reflection TS已被宣布为功能完善

Reflection TS已完成功能:Reflection TS已被宣布为功能已完成,并将在今年夏季进行主要评论投票。再次注意,TS当前基于元编程的模板语法只是一个占位符;所要求的反馈意见是在设计的“核心”上进行的,委员会已经知道它打算用一种简单的编程模型替换表面语法,该模型使用普通的编译时代码而不是<>风格的元编程。

并且是initially planed for C++20,但目前尚不清楚Reflection TS是否仍然有机会进入C ++ 20版本。

答案 13 :(得分:0)

这是在 2020 年对我有用的解决方案,使用 Visual Studio 2019

#define Enum(Name, ...)                                                        \
    struct Name {                                                              \
        enum : int {                                                           \
            __VA_ARGS__                                                        \
        };                                                                     \
        private: struct en_size { int __VA_ARGS__; };                          \
        public: static constexpr  size_t count = sizeof(en_size)/sizeof(int);  \
    }   
      

用法:

struct S {

    Enum(TestEnum, a=11, b=22, c=33);

    void Print() {
        std::cout << TestEnum::a << '\n';
        std::cout << TestEnum::b << '\n';
        std::cout << TestEnum::count << '\n';
    }

};


int main()
{        

    S d;
    d.Print();

    return 0
}

输出:

11
22
3