Is it possible to determine the number of elements of a c++ enum class?

不羁岁月 提交于 2019-12-03 03:23:36

问题


Is it possible to determine the cardinality of a c++ enum class:

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

I tried to use sizeof, however, it returns the size of an enum element.

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

Is there a standard way to get the cardinality (5 in my example) ?


回答1:


Not directly, but you could use the following trick:

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

Then the cardinality is available as (int)Example::Count.

Of course, this only works nicely if you let values of the enum be automatically assigned, starting from 0. If that's not the case, you can manually assign the correct cardinality to Count, which is really no different from having to maintain a separate constant anyway:

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

The one disadvantage is that the compiler will allow you to use Example::Count as an argument for an enum value -- so be careful if you use this! (I personally find this not to be a problem in practice, though.)




回答2:


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;

This is derived from UglyCoder's answer but improves it in three ways.

  • There are no extra elements in the type_safe enum (BEGIN and SIZE) (Cameron's answer also has this problem.)
    • The compiler will not complain about them being missing from a switch statement (a significant problem)
    • They cannot be passed inadvertently to functions expecting your enum. (not a common problem)
  • It does not require casting for use. (Cameron's answer has this problem too.)
  • The subtraction does not mess with the size of the enum class type.

It retains UglyCoder's advantage over Cameron's answer that enumerators can be assigned arbitrary values.

A problem (shared with UglyCoder but not with Cameron) is that it makes newlines and comments significant ... which is unexpected. So someone could add an entry with whitespace or a comment without adjusting TEST_SIZE's calculation.




回答3:


For C++17 you can use magic_enum::enum_count from lib https://github.com/Neargye/magic_enum:

magic_enum::enum_count<Example>() -> 4.




回答4:


enum class TEST
{
    BEGIN = __LINE__
    , ONE
    , TWO
    , NUMBER = __LINE__ - BEGIN - 1
};

auto const TEST_SIZE = TEST::NUMBER;

// or this might be better 
constexpr int COUNTER(int val, int )
{
  return val;
}

constexpr int E_START{__COUNTER__};
enum class E
{
    ONE = COUNTER(90, __COUNTER__)  , TWO = COUNTER(1990, __COUNTER__)
};
template<typename T>
constexpr T E_SIZE = __COUNTER__ - E_START - 1;



回答5:


One trick you can try is to add a enum value at the end of your list and use that as the size. In your example

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



回答6:


No , you have to write it in the code.




回答7:


There is one trick based on X()-macros: image, you have the following enum:

enum MyEnum {BOX, RECT};

Reformat it to:

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

Then the following code defines enum type:

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

And the following code calculates number of enum elements:

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
...



回答8:


You can also consider static_cast<int>(Example::E) + 1 which eliminates the extra element.




回答9:


If you make use of boost's preprocessor utilities, you can obtain the count using BOOST_PP_SEQ_SIZE(...).

For example, one could define the CREATE_ENUM macro as follows:

#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   

Then, calling the macro:

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

would generate the following code:

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

This is only scratching the surface with regards to the boost preprocessor tools. For example, your macro could also define to/from string conversion utilities and ostream operators for your strongly typed enum.

More on boost preprocessor tools here: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html


As an aside, I happen to strongly agree with @FantasticMrFox that the additional Count enumerated value employed in the accepted answer will create compiler warning headaches galore if using a switch statement. I find the unhandled case compiler warning quite useful for safer code maintenance, so I wouldn't want to undermine it.



来源:https://stackoverflow.com/questions/14989274/is-it-possible-to-determine-the-number-of-elements-of-a-c-enum-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!