Extending enums in C++?

前端 未结 10 1122
挽巷
挽巷 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 
    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 {
         public:
            explicit constexpr Type(uint8_t value): Enum(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 Set = {Value1, Value2, Value3, Value4, Value5, Value6};
        }
    

    now you can use the Enum like this:

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

提交回复
热议问题