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

后端 未结 13 1671
长情又很酷
长情又很酷 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 01:49

    Reflection TS: static reflection of enums (and other types)

    Reflection TS, particularly [reflect.ops.enum]/2 of the latest version of the Reflection TS draft offers the get_enumerators TransformationTrait operation:

    [reflect.ops.enum]/2

    template <Enum T> struct get_enumerators
    

    All specializations of get_enumerators<T> shall meet the TransformationTrait requirements (20.10.1). The nested type named type designates a meta-object type satisfying ObjectSequence, containing elements which satisfy Enumerator and reflect the enumerators of the enumeration type reflected by T.

    [reflect.ops.objseq] of the draft covers ObjectSequence operations, where particularly [reflect.ops.objseq]/1 covers the get_size trait for extracting the number of elements for a meta-object satisfying ObjectSequence:

    [reflect.ops.objseq]/1

    template <ObjectSequence T> struct get_size;
    

    All specializations of get_size<T> shall meet the UnaryTypeTrait requirements (20.10.1) with a base characteristic of integral_constant<size_t, N>, where N is the number of elements in the object sequence.

    Thus, in Reflection TS were to be accepted and implemented in its current form, the number of elements of an enum can be computed, at compile time, as follows:

    enum class Example { A, B, C, D, E };
    
    using ExampleEnumerators = get_enumerators<Example>::type;
    
    static_assert(get_size<ExampleEnumerators>::value == 5U, "");
    

    where we are likely to see alias templates get_enumerators_v and get_type_v to simplify the reflection further:

    enum class Example { A, B, C, D, E };
    
    using ExampleEnumerators = get_enumerators_t<Example>;
    
    static_assert(get_size_v<ExampleEnumerators> == 5U, "");
    

    Status on Reflection TS

    As stated by Herb Sutter's Trip report: Summer ISO C++ standards meeting (Rapperswil) from the June 9, 2018 ISO C++ committee summer meeting, Reflection TS has been declared as feature-complete

    Reflection TS is feature-complete: The Reflection TS was declared feature-complete and is being sent out for its main comment ballot over the summer. Note again that the TS’s current template metaprogramming-based syntax is just a placeholder; the feedback being requested is on the core “guts” of the design, and the committee already knows it intends to replace the surface syntax with a simpler programming model that uses ordinary compile-time code and not <>-style metaprogramming.

    and was initially planed for C++20, but it's somewhat unclear if Reflection TS will still have a chance to make it into the C++20 release.

    0 讨论(0)
  • 2020-12-13 01:50

    There is another way that doesn’t rely on line counts or templates. The only requirement is sticking the enum values in their own file and making the preprocessor/compiler do the count like so:

    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"
      ;
    

    This allows you to put comments with the enum values, re-assign values and does not inject an invalid 'count' enum value that needs to be ignored/accounted for in code.

    If you don't care about comments you don't need an extra file and can do as someone above mentioned, e.g.:

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

    and replace the #include "my_enum_inc.h" directives with MY_ENUM_LIST but you'll need to #undef ENUMVAL after each use.

    0 讨论(0)
  • 2020-12-13 01:50

    No , you have to write it in the code.

    0 讨论(0)
  • 2020-12-13 01:53

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

    0 讨论(0)
  • 2020-12-13 01:55

    Not directly, but you could use the following trick:

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

    Then the cardinality is available as static_cast<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.)

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

    Another kind of "stupid" solution to this is:

    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;
    }();
    

    By compiling this with -Werror=switch you make sure to get a compiler warning if you omit or duplicate any switch case. It's also constexpr so this is computed at compile time.

    But note that even for a en enum class the default initialized value is 0 even if the first value of the enum is not 0. So you have to either start on 0 or explicitly use the first value.

    0 讨论(0)
提交回复
热议问题