Extending Enums in C++

Extend enum with additional values

Your only concern is for the integral constants to be unique. Simply assign the first element of your second enum to the last element of your first enum plus one.

enum abc { A, B, C, };     
enum def { D = C + 1, E, F };

Extending enums in C++?

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

C, extending enum with union of enums

As long as you don't need the enum to be a type, you can do this easily enough.

enum State{
STATE_PAUSED = 0,
STATE_RUN,
STATE_COUNTING,
STATE_PERIODIC,
STATE_MAX // must be last
};

enum GPS_State{
GPS_MCU_UART_DEFAULT = STATE_MAX,
GPS_FOO,
GPS_BAR
};

enum Other_State{
OS_CORGI = STATE_MAX,
OS_FOO,
OS_BAR
};

This does mean that a variable that could store any of these states would need to be an int. I'm not sure that there is a way to do it while enforcing type safety.

Extending enum types

There might be some sharp edges, but I think this should work for you:

#include<iostream>

// Helper struct

template<typename T, int N, typename... LIST>
struct whereInType {
static const int index = -1;
};

template<typename T, int N, typename HEAD, typename... TAIL>
struct whereInType<T,N,HEAD,TAIL...> {
static const int index = whereInType<T, N+1, TAIL...>::index;
};

template<typename T, int N, typename... TAIL>
struct whereInType<T,N,T,TAIL...> {
static const int index = N;
};

// The actual union type

template<typename... ENUMS>
class enum_union {
public:
template<typename ENUM>
constexpr enum_union(ENUM val) :
which_enum(whereInType<ENUM,0,ENUMS...>::index),
value(val) {}
constexpr operator long long(){
return static_cast<long long>(which_enum)<<32 | value;
}
template<typename ENUM, int IGNORE=0>
constexpr bool operator==(const ENUM& e) {
return *this == enum_union<ENUMS...>(e);
}
template<int IGNORE=0>
constexpr bool operator==(const enum_union<ENUMS...>& oth) {
return which_enum==oth.which_enum && value==oth.value;
}
template<typename T>
constexpr bool operator!=(const T& oth) {
return !(*this == oth);
}
private:
int which_enum;
int value;
};

// An example usage

enum normal_errors {
E_OUTOFMEMORY,
E_IOERROR
};

enum weird_errors {
E_OUTOFAARDVARKS,
E_DIVIDEBYCUCUMBER
};

typedef enum_union<normal_errors, weird_errors> any_error;

// Some tests

void print(any_error e) {
switch(e) {
case any_error(E_OUTOFMEMORY):
std::cout << "Out of Memory\n";
break;
case any_error(E_IOERROR):
std::cout << "I/O Error\n";
break;
case any_error(E_OUTOFAARDVARKS):
std::cout << "WE NEED AARDVARKS!!! NOW!!!!!\n";
break;
case any_error(E_DIVIDEBYCUCUMBER):
std::cout << "please reinstall universe\n";
break;
}
}

main(){
print(E_OUTOFMEMORY);
print(E_IOERROR);
print(E_OUTOFAARDVARKS);
print(E_DIVIDEBYCUCUMBER);
if (any_error(E_OUTOFMEMORY) == E_OUTOFAARDVARKS) {
std::cout<<"bad\n";
}else{
std::cout<<"good\n";
}
if (any_error(E_OUTOFMEMORY) != E_OUTOFAARDVARKS) {
std::cout<<"good\n";
}else{
std::cout<<"bad\n";
}
if (any_error(E_OUTOFMEMORY) == E_OUTOFMEMORY) {
std::cout<<"good\n";
}else{
std::cout<<"bad\n";
}
if (any_error(E_OUTOFMEMORY) != E_OUTOFMEMORY) {
std::cout<<"bad\n";
}else{
std::cout<<"good\n";
}
}

Extending an enum via inheritance

The reason you can't extend Enums is because it would lead to problems with polymorphism.

Say you have an enum MyEnum with values A, B, and C , and extend it with value D as MyExtEnum.

Suppose a method expects a myEnum value somewhere, for instance as a parameter. It should be legal to supply a MyExtEnum value, because it's a subtype, but now what are you going to do when it turns out the value is D?

To eliminate this problem, extending enums is illegal

About extending enum in C++

You can replace the enum with an int or string or float or some such.

However, that is probably not a GREAT idea either. Assuming we COULD extend the enum [in an imaginary version of C++ that has this capability] , since getMyStatus is a virtual function, it is designed to be called from generic code that doesn't know the details of the derived classes. So if you write something like:

for(iter : baseClassContainter)
{
status = iter->getMyStatus();
switch(status)
{
case DEAD:
... do some stuff here.
break;

case ALIVE:
..... do some stuff here ...

}
}

What should this code do with "HALFDEAD"? It doesn't even know such a value exists, since [in our imaginary language that supports this] it only exists in the derived class...

Extending enums in c#

I wish enums were more powerful in .Net. And I love .Net! You can use attributes to accomplish the same thing. Write the code below once and use it everywhere. This will be a long answer but I think it's a pretty good solution so have patience!

Usage

SomeEnum e = SomeEnum.ValueTwo;
string description = e.GetDescription();

The Enum

Use attributes to describe the enum and it's values.

[DescriptiveEnumEnforcement(DescriptiveEnumEnforcement.EnforcementTypeEnum.ThrowException)]
public enum SomeEnum
{
[Description("Value One")]
ValueOne,

[Description("Value Two")]
ValueTwo,

[Description("Value 3")]
ValueThree
}

DescriptionAttribute

/// <summary>Indicates that an enum value has a description.</summary>
[AttributeUsage(AttributeTargets.Field)]
public class DescriptionAttribute : System.Attribute
{
/// <summary>The description for the enum value.</summary>
public string Description { get; set; }

/// <summary>Constructs a new DescriptionAttribute.</summary>
public DescriptionAttribute() { }

/// <summary>Constructs a new DescriptionAttribute.</summary>
/// <param name="description">The initial value of the Description property.</param>
public DescriptionAttribute(string description)
{
this.Description = description;
}

/// <summary>Returns the Description property.</summary>
/// <returns>The Description property.</returns>
public override string ToString()
{
return this.Description;
}
}

DescriptiveEnumEnforcementAttribute

An attribute to ensure your enum's are properly configured.

/// <summary>Indicates whether or not an enum must have a NameAttribute and a DescriptionAttribute.</summary>
[AttributeUsage(AttributeTargets.Enum)]
public class DescriptiveEnumEnforcementAttribute : System.Attribute
{
/// <summary>Defines the different types of enforcement for DescriptiveEnums.</summary>
public enum EnforcementTypeEnum
{
/// <summary>Indicates that the enum must have a NameAttribute and a DescriptionAttribute.</summary>
ThrowException,

/// <summary>Indicates that the enum does not have a NameAttribute and a DescriptionAttribute, the value will be used instead.</summary>
DefaultToValue
}

/// <summary>The enforcement type for this DescriptiveEnumEnforcementAttribute.</summary>
public EnforcementTypeEnum EnforcementType { get; set; }

/// <summary>Constructs a new DescriptiveEnumEnforcementAttribute.</summary>
public DescriptiveEnumEnforcementAttribute()
{
this.EnforcementType = EnforcementTypeEnum.DefaultToValue;
}

/// <summary>Constructs a new DescriptiveEnumEnforcementAttribute.</summary>
/// <param name="enforcementType">The initial value of the EnforcementType property.</param>
public DescriptiveEnumEnforcementAttribute(EnforcementTypeEnum enforcementType)
{
this.EnforcementType = enforcementType;
}
}

Getting the Description

/// <summary>Provides functionality to enhance enumerations.</summary>
public static partial class EnumUtil
{
/// <summary>Returns the description of the specified enum.</summary>
/// <param name="value">The value of the enum for which to return the description.</param>
/// <returns>A description of the enum, or the enum name if no description exists.</returns>
public static string GetDescription(this Enum value)
{
return GetEnumDescription(value);
}

/// <summary>Returns the description of the specified enum.</summary>
/// <param name="value">The value of the enum for which to return the description.</param>
/// <returns>A description of the enum, or the enum name if no description exists.</returns>
public static string GetDescription<T>(object value)
{
return GetEnumDescription(value);
}

/// <summary>Returns the description of the specified enum.</summary>
/// <param name="value">The value of the enum for which to return the description.</param>
/// <returns>A description of the enum, or the enum name if no description exists.</returns>
public static string GetEnumDescription(object value)
{
if (value == null)
return null;

Type type = value.GetType();

//Make sure the object is an enum.
if (!type.IsEnum)
throw new ApplicationException("Value parameter must be an enum.");

FieldInfo fieldInfo = type.GetField(value.ToString());
object[] descriptionAttributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

//If no DescriptionAttribute exists for this enum value, check the DescriptiveEnumEnforcementAttribute and decide how to proceed.
if (descriptionAttributes == null || descriptionAttributes.Length == 0)
{
object[] enforcementAttributes = fieldInfo.GetCustomAttributes(typeof(DescriptiveEnumEnforcementAttribute), false);

//If a DescriptiveEnumEnforcementAttribute exists, either throw an exception or return the name of the enum instead.
if (enforcementAttributes != null && enforcementAttributes.Length == 1)
{
DescriptiveEnumEnforcementAttribute enforcementAttribute = (DescriptiveEnumEnforcementAttribute)enforcementAttributes[0];

if (enforcementAttribute.EnforcementType == DescriptiveEnumEnforcementAttribute.EnforcementTypeEnum.ThrowException)
throw new ApplicationException("No Description attributes exist in enforced enum of type '" + type.Name + "', value '" + value.ToString() + "'.");

return GetEnumName(value);
}
else //Just return the name of the enum.
return GetEnumName(value);
}
else if (descriptionAttributes.Length > 1)
throw new ApplicationException("Too many Description attributes exist in enum of type '" + type.Name + "', value '" + value.ToString() + "'.");

//Return the value of the DescriptionAttribute.
return descriptionAttributes[0].ToString();
}
}


Related Topics



Leave a reply



Submit