Extending enums in C++?

前端 未结 10 1074
挽巷
挽巷 2020-11-29 05:08

Is there a way in C++ to extend/\"inherit\" enums?

I.E:

enum Enum {A,B,C};
enum EnumEx : public Enum {D,E,F};

or at least define a

相关标签:
10条回答
  • 2020-11-29 05:35

    I had this problem in some projects that ran on small hardware devices I design. There is a common project that holds a number of services. Some of these services use enums as parameters to get additional type checking and safety. I needed to be able to extend these enums in the projects that use these services.

    As other people have mentioned c++ doesn't allow you to extend enums. You can however emulate enums using a namespace and a template that has all the benefits of enum class.

    enum class has the following benefits:

    1. Converts to a known integer type.
    2. Is a value type
    3. Is constexpr by default and takes up no valuable RAM on small processors
    4. Is scoped and accessible by enum::value
    5. Works in case statements
    6. Provides type safety when used as a parameter and needs to be explicitly cast

    Now if you define a class as an enum you can't create constexpr instances of the enum in the class declaration, because the class is not yet complete and it leads to a compile error. Also even if this worked you could not extend the value set of enums easily later in another file/sub project .

    Now namespaces have no such problem but they don't provide type safety.

    The answer is to first create a templated base class which allows enums of different base sizes so we don't waste what we don't use.

    template <typename TYPE>
    class EnumClass {
      private:
        TYPE value_;
      public:
        explicit constexpr EnumClass(TYPE value) :
            value_(value){
        }
        constexpr EnumClass() = default;
        ~EnumClass() = default;
        constexpr explicit EnumClass(const EnumClass &) = default;
        constexpr EnumClass &operator=(const EnumClass &) = default;
    
        constexpr operator TYPE() const {return    value_;}
        constexpr TYPE value() const {return value_;}
    
    };
    

    Then for each enum class we want to extend and emulate we create a namespace and a Type like this:

    namespace EnumName {
       class Type :public Enum<uint8_t> {
         public:
            explicit constexpr Type(uint8_t value): Enum<uint8_t>(value){}
            constexpr Enum() = default;
       }
       constexpr auto Value1 = Type(1); 
       constexpr auto Value2 = Type(2); 
       constexpr auto Value3 = Type(3); 
    }
    

    Then later in your code if you have included the original EnumName you can do this:

       namespace EnumName {
           constexpr auto Value4 = Type(4U); 
           constexpr auto Value5 = Type(5U); 
           constexpr auto Value6 = Type(6U); 
    
           constexpr std::array<Type, 6U> Set = {Value1, Value2, Value3, Value4, Value5, Value6};
        }
    

    now you can use the Enum like this:

    #include <iostream>
    
    void fn(EnumName::Type val){
        if( val != EnumName::Value1 ){
          std::cout << val;
        }
    }
    
    int main(){
      for( auto e :EnumName::Set){
        switch(e){
          case EnumName::Value1:  
            std::cout << "a";
            break;
          case EnumName::Value4:  
            std::cout << "b";
            break;
          default:
            fn(e);
        }
      }
    }
    

    So we have a case statement, enum comparisons, parameter type safety and its all extensible. Note the set is constexpr and wont end up using valuable RAM on a small micro (placement verified on Godbolt.org. :-). As a bonus we have the ability to iterate over a set of enum values.

    0 讨论(0)
  • 2020-11-29 05:44

    No, there is not.

    enum are really the poor thing in C++, and that's unfortunate of course.

    Even the class enum introduced in C++0x does not address this extensibility issue (though they do some things for type safety at least).

    The only advantage of enum is that they do not exist: they offer some type safety while not imposing any runtime overhead as they are substituted by the compiler directly.

    If you want such a beast, you'll have to work yourself:

    • create a class MyEnum, that contains an int (basically)
    • create named constructors for each of the interesting values

    you may now extend your class (adding named constructors) at will...

    That's a workaround though, I have never found a satistifying way of dealing with an enumeration...

    0 讨论(0)
  • 2020-11-29 05:46

    A simple, but useful workaround for this c++ gap could be as follows:

    #define ENUM_BASE_VALS A,B,C
    enum Enum {ENUM_BASE_VALS};
    enum EnumEx {ENUM_BASE_VALS, D,E,F};
    
    0 讨论(0)
  • 2020-11-29 05:47

    The following code works well.

    enum Enum {A,B,C};
    enum EnumEx {D=C+1,E,F};
    
    0 讨论(0)
提交回复
热议问题