13,094,983 members (85,632 online)
alternative version

#### Stats

31.7K views
16 bookmarked
Posted 7 Oct 2008

# Enum Iteration and String Conversion

, 7 Oct 2008
 Rate this:
Enum iteration and string conversion

## Introduction

Ever wanted to iterate through a series of enumerations? This was the situation I found myself in. Presented with an enumeration sequence, perform a specific task. After a few minutes, I generated the following code snippet. It is simple and effective.

```enum MyEnum { enum1, firstEnum=enum1, enum2, enum3, enum4, enum5,
totalEnums, errorEnum = 0xFF };

const MyEnum& operator++(MyEnum& eValue)    { return (eValue = (MyEnum)(eValue+1)); }

for(MyEnum eValue = firstEnum ; eValue < totalEnums; ++eValue)
DoSomething(eValue);```

However, this implementation suffers from a number of fundamental problems:

• It will only work for simple sequential enumerations.
• It requires additional knowledge about the enumeration sequence, i.e. the start and end points.

In addition to iteration, `string `conversion functionality is useful, i.e. enabling the conversion of `string`s to an enumeration value, and vice-versa.
The following short article aims to provide a simple mechanism for providing this functionality.

## The EnumClass

The `EnumClass<> `provides the functionality identified in the introduction. It provides iteration and `string `conversion functionality for C++ style enumerations. The class does require two helper macros to declare and implement the enumeration and the helper class itself, but with the following three simple lines of code, the `enum `and helper class are defined and implemented.

```// Place in the header file for public access to enum MyEnum
// and the helper class MyEnumClass
#define MYENUM { enum1, firstEnum=enum1, enum2, enum3, enum4, enum5,
totalEnums, errorEnum = 0xFF }
DECLARE_ENUM(MyEnumClass, MyEnum, MYENUM);

// Place in source file for implementation of MyEnumClass helper class
IMPLEMENT_ENUM(MyEnumClass);```

The code above generates the following (the external declaration for `MyEnumClass `has been removed for clarity):

```enum MyEnum { enum1, firstEnum= enum1, enum2, enum3,
enum4, enum5,totalEnums, errorEnum = 0xFF }
MyEnum__EnumClass MyEnumClass;```

The enumeration `MyEnum `can be used as follows:

```// conversion functionality
// convert enum to string - outputs "totalEnums"
std::cout << MyEnumClass(totalEnums) << std::endl;
// convert string to enum - outputs 5
std::cout << MyEnumClass("totalEnums") << std::endl;

// simple looping
for(MyEnums eValue = firstEnum; eValue <= totalEnums; ++eValue)
DoSomething(eValue);```

## More Complex Enumerations

The `for `loop implementation will correctly sequence through enumerations that are sequential, it will also handle non consecutive enumerations, i.e.

```enum MyEnum { firstEnum = 0, enum1 = firstEnum, enum2=1, enum3=2, enum3=4,
enum4=8, enum5 = 16, totalEnums = enum5, errorEnum = 0xFF }```

However, the simple `for `loop is not able to guarantee handling non sequential enumerations (See Caveats later), e.g.

```enum MyEnum { firstEnum = 0, enum1 = firstEnum, enum2=16, enum3=1, enum3=2,
enum4=4, enum5 = 8, totalEnums = enum5, errorEnum = 0xFF }```

To overcome this problem, `EnumClass<> `provides iterators that can correctly sequence these more complex enumeration sequences. The following code snippet shows how to use the `EnumClass<>::const_iterator` to sequence through more complex enumeration sequences.

```// iteration
EnumClass<MyEnum>::const_iterator iter = MyEnumsEnumClass.begin();
EnumClass<MyEnum>::const_iterator end = MyEnumsEnumClass.end();
while(iter != end)
{
DoSomething(*iter);
iter++;
};```

## Implementation

The following is a short description of the `EnumClass `and the helper macros `DECLARE_ENUM` & `IMPLEMENT_ENUM`.

## DECLARE_ENUM

This macro declares the enumeration, the increment operators and the `extern `reference for helper class.

```#define DECLARE_ENUM(EnumClassName, EnumName, ENUM) \
enum EnumName ENUM; \
class EnumClassName##__EnumClass: public EnumClass<MyEnum> \
{ \
public: \
EnumClassName##__EnumClass(): EnumClass<MyEnum>(DEFINE_AS_STR(ENUM)) {}; \
}; \
extern EnumClassName##__EnumClass EnumClassName; \
inline const EnumName& operator++(EnumName& eValue)
{ return (eValue = EnumClassName.GetNext(eValue)); } \
inline const EnumName& operator++(EnumName& eValue, int)
{ return (eValue = EnumClassName.GetNext(eValue)); }```
• First parameter is the name of the helper class object - `EnumClassName`.
`extern EnumName##__EnumClass EnumClassName;`
• Second Parameter is the name of the enumeration sequence - `EnumName `
`enum EnumName ENUM`
• Third parameter is the enumeration set - `ENUM `
`enum EnumName ENUM`

This will generate the following code (using the example above):

```extern MyEnum__EnumClass MyEnumClass;
enum MyEnum { enum1, firstEnum= enum1, enum2, enum3, enum4, enum5,
totalEnums, errorEnum = 0xFF }```

The class `MyEnum__EnumClass `is also defined as part of the macro. This class is a wrapper for the `EnumClass<>`, and is simply used as a mechanism for supplying the stringized enumeration to the `EnumClass<>`. It does not need to be used to access any of the `enum `functionality. See `DEFINE_AS_STR `section for further information regarding stringizing the enumeration.

The `enum `conversion functionality can be accessed via the class object name, i.e. for the example above:

```// convert enum to string - outputs "totalEnums"
std::cout << MyEnumClass(totalEnums) << std::endl;
// convert string to enum - outputs 5
std::cout << MyEnumClass("totalEnums") << std::endl;	```

The `enum `iterator can be access via the base class name and the class object, i.e.

```EnumClass<MyEnum>::const_iterator iter = MyEnumClass.begin();
EnumClass<MyEnum>::const_iterator end = MyEnumClass.end();```

### IMPLEMENT_ENUM

This macro instantiates the helper class.

```#define IMPLEMENT_ENUM(EnumClassName) \
EnumClassName##__EnumClass EnumClassName;```
• First parameter is the name of the helper class object - `EnumClassName`.
`EnumName##__EnumClass EnumClassName;`

### EnumClass

The class provides the conversion and iteration functionality for any enumeration sequence.
It is a template class providing name space resolution, based on the unique enumeration identity, i.e. a different and unique `EnumClass<> `is generated for each enumeration, thereby preventing name space clashes.
The interface to the `EnumClass<> `is simple.

```// Class construction
EnumClass(const std::string& eString)
:undefined("undefined")                    { ParseEnumString(eString); }

// Conversion
const std::string operator()(T eValue) const        { return GetEnumString(eValue); }
const T operator()(const std::string& sStr) const   { return GetEnumValue(sStr); }

// iteration
const_iterator begin() const              { return const_iterator(enumList.begin()); }
const_iterator end() const                { return const_iterator(enumList.end()); }
const_reverse_iterator rbegin() const
{ return const_reverse_iterator(enumList.rbegin()); }
const_reverse_iterator rend() const
{ return const_reverse_iterator(enumList.rend()); }

// first / last accessors
const T first() const                               { return enumList.front(); }
const T last() const                                { return enumList.last(); }
size_t size() const                                 { return enumList.size(); }```

The `EnumClass<> `holds a vector of `EnumInfo `objects. These contain the mapping between enumeration values and `string `equivalents. The list of `EnumInfo `objects is generated during the class construction. The `ParseEnumString() `method splits the enumeration `string `into individual name, value pairs. The name is stored as a name for the `enum`, whilst the value is evaluated to generate an integer value for the enumeration. These name value pairs are then added to the list of `EnumInfo `objects. If an `EnumInfo `object already exists with the same value, the name is added to the existing `EnumInfo `object, and the new `EnumInfo `object discarded.

### Enum Evaluation

The value assigned to an `EnumInfo `object is sequential, starting at zero, if no value is defined. If a value is defined, then it is evaluated and assigned to the `EnumInfo `object. The evaluation logic allows for simple algebraic expressions (including the use of braces). The following operators can be handled:
• - subtract
• * multiply
• / divide
• ^ exclusive OR
• & arithmetic AND
• | arithmetic OR
• % modulus

The evaluation should handle 99% of all expressions, however evaluation is strictly left to right, i.e. no precedence is performed, see caveats for more details.

## Enum Values

### #define ENUM

To ensure the `enum `sequence (multiple commas, hence multiple parameters) works using standard macros, a fixed number of macro parameters is required. This is achieved by defining the `enum `sequence as a macro. This `enum `macro can then be treated as a single macro parameter wherever it is used. The body of the macro can contain as many commas as required to define the enumeration sequence and still be treated as a single macro parameter.

### DEFINE_AS_STR

A problem was encountered when trying to stringize the `ENUM #define`. Compilers do not appear to perform the `#define `substitution expected, the `#define `name is stringized, not its content.

```#define QAZ { 1,2,3,4,5 }
#define ZAQ(P1) char* myString = #P1;```

generates the following:

`char* myString = "QAZ";`

The `DEFINE_AS_STR `allows the compiler to perform the correct substitution, i.e.

```#define QAZ { 1,2,3,4,5 }
#define ZAQ(P1) char* myString = DEFINE_AS_STR(P1);```

generates the following:

`char* myString = "{ 1,2,3,4,5 }";`

I cannot lay claim to discovering this gem, the idea was lifted from the `boost `library. So I am not sure how it works, but it does ensure the correct substitutions are made.

## Caveats

### for loop

The pre and post increment operators work correctly, however it should be noted that unexpected results may occur if they are used with non-sequential enumeration. Consider the following:

```enum MyEnums { firstEnum = 0, enum1 = firstEnum, enum2=1, enum3=16, enum3=2,
enum4=4, enum5 = 8, totalEnums = enum5, errorEnum = 0xFF }
for(MyEnums eValue = firstEnum; eValue < totalEnums; ++eValue)
DoSomething(eValue)```

The increment operators will sequence through the `enum`s as follows: `enum1`, `enum2`, `enum3`, however, when `eValue `becomes `enum3 `(`eValue = 2`) this is less than `totalEnums`, i.e. 16 > 8, and the `for `loop will exit. There are two solutions, firstly do not use the `<` operator, use `! =` operator, or use the `const_iterator `provided by `EnumClass`. Note: use of the `!=` operator does require that the end value is unique. Care should be taken to ensure the `for `loop will work as expected when iterating through non sequential `enum`s.

### Expression Evaluation

The `EnumClass `uses a very simple evaluation mechanism, which evaluates in a left to right sequence. It does not perform any precedence on the value expressions, therefore:
A+B*C will not evaluate correctly, use braces to ensure correct evaluation, i.e. (A+B)*C

## Conclusion

I've tried to use standard C++ within this implementation, but having only compiled this using a Microsoft compiler, it would be interesting to hear if it compiles successfully with other compilers/platforms. I suspect there will be an issue over the stringizer macro, but minds greater than mine should be able to resolve that issue.

The two caveats identified above are work for the future releases; I am not sure there is a simple answer to the `for `loop issue, however if there is sufficient demand the expression evaluation should be resolvable.

Hopefully the class will be of some use. Enjoy!

## History

• 06 Oct 2008: Original article

## Share

 Software Developer (Senior) Jask Consultants United Kingdom
No Biography provided

## You may also be interested in...

 First Prev Next
 DEFINE_AS_STR mstrom6-Apr-10 2:49 mstrom 6-Apr-10 2:49
 Re: DEFINE_AS_STR tomayo4727-Apr-11 9:45 tomayo47 27-Apr-11 9:45
 A different approach Hugo González Castro23-Dec-08 17:48 Hugo González Castro 23-Dec-08 17:48
 Last Visit: 31-Dec-99 18:00     Last Update: 22-Aug-17 11:18 Refresh 1