|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionEver wanted to iterate through a series of enumerations? This was the situation I found myself. Presented with an enumeration sequence, perform a specific task. After a few minutes I generated the following code snippet. 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:
In addition to iteration, string conversion functionality is useful, i.e. enabling the conversion of strings to an enumeration value, and vice-versa. The EnumClassThe // 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 enum MyEnum { enum1, firstEnum= enum1, enum2, enum3,
enum4, enum5,totalEnums, errorEnum = 0xFF }
MyEnum__EnumClass MyEnumClass;
The enumeration // 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 enumerationsThe 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, // iteration
EnumClass<MyEnum>::const_iterator iter = MyEnumsEnumClass.begin();
EnumClass<MyEnum>::const_iterator end = MyEnumsEnumClass.end();
while(iter != end)
{
DoSomething(*iter);
iter++;
};
ImplementationThe following is a short description of the DECLARE_ENUMThis 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)); }
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 // 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_ENUMThis macro instantiates the helper class. #define IMPLEMENT_ENUM(EnumClassName) \
EnumClassName##__EnumClass EnumClassName;
EnumClassThe class provides the conversion and iteration functionality for any enumeration sequence. // 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 Additional InformationEnum EvaluationThe 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:
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 ENUMTo ensure the enum sequence (multiple comma's, hence multiple parameters) work 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 where ever it is used. The body of the macro can contain as many comma's as required to define the enumeration sequence and still be treated as a single macro parameter. DEFINE_AS_STRA 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 it's 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 can not 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. Caveatsfor loopThe 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, ennum3, 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 required 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 EvaluationThe 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: ConclusionI'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. History
|
||||||||||||||||||||||