Introduction
I was looking for an enumerated type which is easy to serialize (to write to and read from a file, for example). I didn't want to care about the internal integer numbers associated with the enum, or the overlapping problems with these numbers. I just wanted to care about the names of the enumerated items. I also wanted an easy way to extend the enum definitions, to work with polymorphic classes. I wanted them to make a list of errors without using numbers, and I also wanted to make a list of string IDs without using numbers. That was the reason I didn't care at the beginning about iterations or assigning specific values to each item. At the end, I also decided to implement these possibilities, as maybe someone could find them useful.
Background
There are several interesting articles about enums that I have read before deciding to implement my own solution:
Using the Code
The key idea of my approach is to separate the list of enumerated items from the definition of the enumeration type itself. So, the first thing you have to do is to create a file with the list of items you want to enumerate. For example, declare "DayEnum.h":
ENUMITEM(Sunday)
ENUMITEM(Monday)
ENUMITEM(Tuesday)
ENUMITEM(Wednesday)
ENUMITEM(Thursday)
ENUMITEM(Friday)
ENUMITEM(Saturday)
The macro ENUMITEM() is implemented in several different ways to do the hard job. It is also possible to use ENUMITEM_VALUE(,) to assign specific values only to the items you want, but it is not recommended as this could create conflicts with enum extensions/inheritance. I recommend using only ENUMITEM() whenever possible.
The following example code will declare the enum type and the functions to convert the enum to a string, and to convert a string to an enum, to iterate, and to count items. All these will be defined (and encapsulated) in a namespace (as recommended in the first background article) or in a static class (from version 5.0 of this code, which allows a subclass definition), so we avoid all conflicts with other enum definitions.
#define IMPROVED_ENUM_NAME Day
#define IMPROVED_ENUM_FILE "DayEnum.h"
#include "DefineImprovedEnum.h"
Some authors would prefer to write ??=include "DefineImprovedEnum.h" to make clear the different use of the include file. "The ??= token is a trigraph for #", but trigraphs are not recognized by all compilers.
As a new feature (from version 4.0 of this code) all the code above can also be defined in the same place (without the need of a separated file):
#define IMPROVED_ENUM_NAME Day
#define IMPROVED_ENUM_LIST ENUMITEM(Sunday) \
ENUMITEM(Monday) \
ENUMITEM(Tuesday) \
ENUMITEM(Wednesday) \
ENUMITEM(Thursday) \
ENUMITEM(Friday) \
ENUMITEM(Saturday)
#include "DefineImprovedEnum.h"
Using the enumeration is as simple as this:
void Test()
{
Day::EnumType t = Day::Monday;
std::string text = Day::Enum2String(t);
t = Day::String2Enum("Friday");
t = Day::FirstEnumItem();
t = Day::NextEnumItem(t);
t = Day::LastEnumItem();
t = Day::PreviousEnumItem(t);
int n = Day::NumberOfValidEnumItem();
}
At the end of Test(), the value of t is Friday, and the value of text is "Monday".
How to Extend an Enum Definition (Inherit from Another Enum)
Another approach to solve the problem of Inheriting a C++ enum type with this code is to do the following:
ENUMITEM(Orange)
ENUMITEM(Mango)
ENUMITEM(Banana)
and:
ENUMITEM(Apple)
ENUMITEM(Pear)
Then, as we have separated the lists of items, we can create a new list with all the items:
#include "Fruit.h"
#include "NewFruit.h"
And, declare the new enum type:
#define IMPROVED_ENUM_NAME MyFruit
#define IMPROVED_ENUM_FILE "MyFruit.h"
#include "DefineImprovedEnum.h"
That approach works well, but we don't have an easy way to convert from the base enum type to the extended enum type. So, I decided to directly implement some inheritance functions to extend the functionality. The following example code will declare the base enum and the extended enum, with the extended functionality:
#define IMPROVED_ENUM_NAME Fruit
#define IMPROVED_ENUM_FILE "Fruit.h"
#include "DefineImprovedEnum.h"
#define IMPROVED_ENUM_NAME MyFruit
#define IMPROVED_ENUM_FILE "NewFruit.h"
#define IMPROVED_ENUM_INHERITED_NAME Fruit
#define IMPROVED_ENUM_INHERITED_FILE "Fruit.h"
#include "DefineImprovedEnum.h"
Each enum definition has its own namespace and there are no overlapping problems. The DefineImprovedEnum batch file defines the functions to convert items from one namespace to items of another namespace. Using the extended enumeration is as simple as this:
void eat(Fruit::EnumType fruit) {};
void consume(MyFruit::EnumType myfruit) {};
void ExtendedTest()
{
Fruit::EnumType fruitAux, fruit;
MyFruit::EnumType myfruitAux, myfruit, newfruit;
fruit = Fruit::Orange; myfruit = MyFruit::Orange; newfruit = MyFruit::Apple;
myfruitAux = MyFruit::Inherited2Enum(fruit); myfruitAux = MyFruit::Inherited2Enum(myfruit); myfruitAux = MyFruit::Inherited2Enum(newfruit);
fruitAux = MyFruit::Enum2Inherited(fruit); fruitAux = MyFruit::Enum2Inherited(myfruit); fruitAux = MyFruit::Enum2Inherited(newfruit);
eat(fruit); eat(MyFruit::Enum2Inherited(myfruitAux)); consume(myfruit); consume(MyFruit::Inherited2Enum(fruit));
myfruitAux = MyFruit::FirstExtendedEnumItem();
myfruitAux = MyFruit::NextInheritedEnumItem(newfruit);
myfruitAux = MyFruit::LastInheritedEnumItem();
myfruitAux = MyFruit::PreviousExtendedEnumItem(newfruit);
int n = MyFruit::NumberOfInheritedValidEnumItem();
int m = MyFruit::NumberOfExtendedValidEnumItem();
}
Implementation
The implementation is based on a batch file for preprocessing commands, called "DefineImprovedEnum.h":
#if defined(ENUMITEM)
#error ENUMITEM macro cannot be already defined
#elif defined(ENUMITEM_VALUE)
#error ENUMITEM_VALUE macro cannot be already defined
#endif
#include <string>
#if defined(IMPROVED_ENUM_SUBCLASS_PARENT)
#define STATIC_METHOD static
class IMPROVED_ENUM_NAME : public IMPROVED_ENUM_SUBCLASS_PARENT
{
public:
#elif defined(IMPROVED_ENUM_SUBCLASS)
#define STATIC_METHOD static
class IMPROVED_ENUM_NAME
{
public:
#else
#define STATIC_METHOD
namespace IMPROVED_ENUM_NAME
{
#endif
#define GET_MACRO_STRING_EXPANDED(Macro) #Macro
#define GET_MACRO_STRING(Macro) GET_MACRO_STRING_EXPANDED(Macro)
#define ENUM_SEPARATOR "::"
#define ENUM_TYPE_NAME GET_MACRO_STRING(IMPROVED_ENUM_NAME)
STATIC_METHOD inline const std::string EnumSeparator() { return ENUM_SEPARATOR; }
STATIC_METHOD inline const std::string EnumTypeName() { return ENUM_TYPE_NAME; }
#ifdef IMPROVED_ENUM_INHERITED_NAME
#define PARENT_ENUM_TYPE_NAME GET_MACRO_STRING(IMPROVED_ENUM_INHERITED_NAME)
#define FULL_ENUM_TYPE_NAME PARENT_ENUM_TYPE_NAME ENUM_SEPARATOR ENUM_TYPE_NAME
#else #define PARENT_ENUM_TYPE_NAME ""
#define FULL_ENUM_TYPE_NAME ENUM_TYPE_NAME
#endif STATIC_METHOD inline const std::string ParentEnumTypeName()
{ return PARENT_ENUM_TYPE_NAME; }
STATIC_METHOD inline const std::string FullEnumTypeName()
{ return FULL_ENUM_TYPE_NAME; }
typedef enum EnumTypeTag
{
#define ENUMITEM(EnumItem) EnumItem,
#define ENUMITEM_VALUE(EnumItem, Value) EnumItem = Value,
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
NotValidEnumItem } EnumType, Type;
STATIC_METHOD inline const std::string Enum2String(const EnumType& t)
{
switch (t)
{
#define ENUMITEM(EnumItem) case EnumItem : return #EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
}
return ""; }
STATIC_METHOD inline const std::string Enum2FullString(const EnumType& t)
{
switch (t)
{
#define ENUMITEM(EnumItem) \
case EnumItem : return FULL_ENUM_TYPE_NAME ENUM_SEPARATOR #EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
}
return ""; }
STATIC_METHOD inline const EnumType String2Enum(const std::string& s)
{
if (s == "") return NotValidEnumItem;
#define ENUMITEM(EnumItem) if (s == #EnumItem) return EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
return NotValidEnumItem;
}
STATIC_METHOD inline const EnumType FullString2Enum(const std::string& s)
{
if (s == "") return NotValidEnumItem;
#define ENUMITEM(EnumItem) \
if (s == FULL_ENUM_TYPE_NAME ENUM_SEPARATOR #EnumItem) return EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
return NotValidEnumItem;
}
STATIC_METHOD inline const EnumType NextEnumItem(const EnumType& t)
{
switch (t)
{
case NotValidEnumItem :
#define ENUMITEM(EnumItem) return EnumItem; case EnumItem :
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
return NotValidEnumItem; }
return NotValidEnumItem; }
STATIC_METHOD inline const EnumType PreviousEnumItem(const EnumType& t)
{
EnumType tprev = NotValidEnumItem;
#define ENUMITEM(EnumItem) \
if (t == EnumItem) return tprev; else tprev = EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
return tprev;
}
STATIC_METHOD inline const EnumType FirstEnumItem()
{ return NextEnumItem(NotValidEnumItem); }
STATIC_METHOD inline const EnumType LastEnumItem()
{ return PreviousEnumItem(NotValidEnumItem); }
STATIC_METHOD inline const int NumberOfValidEnumItem()
{
return 0
#define ENUMITEM(EnumItem) +1
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
;
}
#ifdef IMPROVED_ENUM_INHERITED_NAME
STATIC_METHOD inline const EnumType Inherited2Enum(const EnumType& t)
{ return t; }
STATIC_METHOD inline const EnumType Inherited2Enum(
const IMPROVED_ENUM_INHERITED_NAME::EnumType& t)
{
switch (t)
{
#define ENUMITEM(EnumItem) \
case IMPROVED_ENUM_INHERITED_NAME::EnumItem : return EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
}
return NotValidEnumItem;
}
STATIC_METHOD inline const IMPROVED_ENUM_INHERITED_NAME::EnumType Enum2Inherited(
const IMPROVED_ENUM_INHERITED_NAME::EnumType& t)
{ return t; }
STATIC_METHOD inline const IMPROVED_ENUM_INHERITED_NAME::EnumType Enum2Inherited(
const EnumType& t)
{
switch (t)
{
#define ENUMITEM(EnumItem) \
case EnumItem : return IMPROVED_ENUM_INHERITED_NAME::EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_INHERITED_FILE
#include IMPROVED_ENUM_INHERITED_FILE
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
}
return IMPROVED_ENUM_INHERITED_NAME::NotValidEnumItem;
}
STATIC_METHOD inline const EnumType NextExtendedEnumItem(
const EnumType& t)
{
switch (t)
{
case NotValidEnumItem :
#define ENUMITEM(EnumItem) return EnumItem; case EnumItem :
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
return NotValidEnumItem;
}
return NotValidEnumItem;
}
STATIC_METHOD inline const EnumType PreviousExtendedEnumItem(
const EnumType& t)
{
EnumType tprev = NotValidEnumItem;
#define ENUMITEM(EnumItem) \
if (t == EnumItem) return tprev; else tprev = EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
return tprev;
}
STATIC_METHOD inline const EnumType FirstExtendedEnumItem()
{ return NextExtendedEnumItem(NotValidEnumItem); }
STATIC_METHOD inline const EnumType LastExtendedEnumItem()
{ return PreviousExtendedEnumItem(NotValidEnumItem); }
STATIC_METHOD inline const int NumberOfExtendedValidEnumItem()
{
return 0
#define ENUMITEM(EnumItem) +1
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else IMPROVED_ENUM_LIST
#endif #undef ENUMITEM_VALUE
#undef ENUMITEM
;
}
STATIC_METHOD inline const EnumType NextInheritedEnumItem(
const EnumType& t)
{
switch (t)
{
case NotValidEnumItem :
#define ENUMITEM(EnumItem) return EnumItem; case EnumItem :
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#include IMPROVED_ENUM_INHERITED_FILE
#undef ENUMITEM_VALUE
#undef ENUMITEM
return NotValidEnumItem;
}
return NotValidEnumItem;
}
STATIC_METHOD inline const EnumType PreviousInheritedEnumItem(
const EnumType& t)
{
EnumType tprev = NotValidEnumItem;
#define ENUMITEM(EnumItem) \
if (t == EnumItem) return tprev; else tprev = EnumItem;
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#include IMPROVED_ENUM_INHERITED_FILE
#undef ENUMITEM_VALUE
#undef ENUMITEM
return tprev;
}
STATIC_METHOD inline const EnumType FirstInheritedEnumItem()
{ return NextInheritedEnumItem(NotValidEnumItem); }
STATIC_METHOD inline const EnumType LastInheritedEnumItem()
{ return PreviousInheritedEnumItem(NotValidEnumItem); }
STATIC_METHOD inline const int NumberOfInheritedValidEnumItem()
{
return 0
#define ENUMITEM(EnumItem) +1
#define ENUMITEM_VALUE(EnumItem, Value) ENUMITEM(EnumItem)
#include IMPROVED_ENUM_INHERITED_FILE
#undef ENUMITEM_VALUE
#undef ENUMITEM
;
}
#endif
#undef STATIC_METHOD
#undef ENUM_SEPARATOR
#undef ENUM_TYPE_NAME
#undef PARENT_ENUM_TYPE_NAME
#undef FULL_ENUM_TYPE_NAME
#undef GET_MACRO_STRING
#undef GET_MACRO_STRING_EXPANDED
}
#if defined(IMPROVED_ENUM_SUBCLASS) || defined(IMPROVED_ENUM_SUBCLASS_PARENT)
;
#endif
#undef IMPROVED_ENUM_NAME
#undef IMPROVED_ENUM_FILE
#undef IMPROVED_ENUM_LIST
#undef IMPROVED_ENUM_SUBCLASS
#undef IMPROVED_ENUM_SUBCLASS_PARENT
#undef IMPROVED_ENUM_INHERITED_NAME
#undef IMPROVED_ENUM_INHERITED_FILE
Points of Interest
I found no way to make the #include or #define directives inside a macro, so I think this cannot be done with a standard macro definition. I decided to make a file with all the preprocessing directives I needed, and then include it wherever I needed it in my code. The only problem I see with this approach is the way I have to pass the arguments/parameters to this file, because the code to define the enum type is not as clear as in the other solutions. Anyway, for my particular problem, it was the best solution I found. Any constructive comments and ideas are welcome.
Apart from that, I was exploring the concept of inheritance in enumerations and compared it with class inheritance. Derived classes add variables and methods to base classes, and derived/extended enum adds items to the base enum. I wanted to mix derived classes with respective derived enums (in a polymorphic pattern) as I thought it was a nice idea, but that could be nonsense (as class inheritance is for specialization, but enum inheritance is for extension). As you can have a method that takes a pointer of the base class but can be called with derived classes (using polymorphism), I wanted a method that takes "a base enum" but can be called with "derived enums". This can be done encapsulating each enum in a class, and that is the new approach with IMPROVED_ENUM_SUBCLASS and IMPROVED_ENUM_SUBCLASS_PARENT input parameters. Ideas are welcome...
History
- v1.0 - 2008/12/16
- First version with
Enum2String and String2Enum
- v2.0 - 2008/12/22
- Added
Enum2Inherited and Inherited2Enum
- v3.0 - 2008/12/23
- Added
Count and Iteration and published on The Code Project
- v4.0 - 2009/04/02
- Added
IMPROVED_ENUM_LIST for inline enum declaration, Enum2FullString and FullString2Enum to generate unambiguous strings when working with several enums, all functions declared inline to enable the use on header files without problems, and a "vcproj" example.
- v5.0 - 2009/04/13
- Added
IMPROVED_ENUM_SUBCLASS and IMPROVED_ENUM_SUBCLASS_PARENT to encapsulate the enum in a static class or a static derived class instead of a namespace. The calling syntax is exactly the same as the syntax in the namespace. You cannot declare a namespace inside a class, but with this option now you can declare ImprovedEnums inside a class. The examples have also been updated.