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 enum
s 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 // IMPROVED_ENUM_SUBCLASS || IMPROVED_ENUM_SUBCLASS_PARENT
#define STATIC_METHOD
namespace IMPROVED_ENUM_NAME
{
#endif // IMPROVED_ENUM_SUBCLASS || IMPROVED_ENUM_SUBCLASS_PARENT
#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 //IMPROVED_ENUM_INHERITED_NAME
#define PARENT_ENUM_TYPE_NAME ""
#define FULL_ENUM_TYPE_NAME ENUM_TYPE_NAME
#endif//IMPROVED_ENUM_INHERITED_NAME
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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#ifdef IMPROVED_ENUM_FILE
#include IMPROVED_ENUM_FILE
#else // IMPROVED_ENUM_LIST
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#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// IMPROVED_ENUM_INHERITED_FILE
#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
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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
IMPROVED_ENUM_LIST
#endif// IMPROVED_ENUM_FILE
#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 // IMPROVED_ENUM_INHERITED_NAME
#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 enum
s (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 enum
s". 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 string
s when working with several enum
s, 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.