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

后端 未结 13 1673
长情又很酷
长情又很酷 2020-12-13 01:39

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

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

I tried to use si

相关标签:
13条回答
  • 2020-12-13 02:01

    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
    ...
    
    0 讨论(0)
  • 2020-12-13 02:02

    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 };
    
    0 讨论(0)
  • 2020-12-13 02:10
    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.

    0 讨论(0)
  • 2020-12-13 02:12

    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.

    0 讨论(0)
  • 2020-12-13 02:13

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

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

    Where is the drawback?

    This library uses a compiler-specific hack (based on __PRETTY_FUNCTION__ / __FUNCSIG__), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9.

    We go through the given interval range, and find all the enumerations with a name, this will be their count. Read more about limitations

    Many more about this hack in this post https://taylorconor.com/blog/enum-reflection.

    0 讨论(0)
  • 2020-12-13 02:13

    It can be solved by a trick with 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__}); \
        }();                                                          \
    };
    

    Usage:

    #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;
    }
    
    0 讨论(0)
提交回复
热议问题