Extending enums in C++?

I've solved in this way:

typedef enum
{
    #include "NetProtocols.def"
} eNetProtocols, eNP;

Of course, if you add a new net protocol in the NetProtocols.def file, you have to recompile, but at least it's expandable.

"NetProtocols.def" will contain only the field names:

HTTP,
HTTPS,
FTP

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.


If you were able to create a subclass of an enum it'd have to work the other way around.

The set of instances in a sub-class is a subset of the instances in the super-class. Think about the standard "Shape" example. The Shape class represents the set of all Shapes. The Circle class, its subclass, represents the subset of Shapes that are Circles.

So to be consistent, a subclass of an enum would have to contain a subset of the elements in the enum it inherits from.

(And no, C++ doesn't support this.)


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

Tags:

C++

Enums