Header <boost/smart_enum.hpp>Abstract
This library provides a generic wrapper template for
enumdata types, allowing for checked maniplation ofenuminstances. In particular, the template classes can verify arithmetic and assignment expressions at compile-time and run-time with neglectable overhead. All functionality is provided in the header file; there is no need to link any binary object files to your application in order to use this library.Please note that — despite the look and feel of the library and its documentation —
smart_enumis not officially part of the Boost library effort, even though I (obviously) plan to submit it eventually.
smart_enumsequential_smart_enumwrapped_smart_enumbounded_smart_enumsmart_enum directlyboost::lambdasmart_enumProgram writers frequently have to store state information in their classes,
which determines the next step in the program flow. Another frequent requirement is to denote
certain distinct values, which are grouped by some relation, using descriptive names that
suggest the value's meaning. The intuitive representation for these applications is
enum, which has been designed exactly for this purpose.
Unfortunately, enum has a few shortcomings:
enum is always instantiated uninitialized. There's no way to add a
default constructor, because enum is a fundamental datatype.enum in order to add the
required member functions to the derived class.enum in an arithmetic expression, such as
++state state = BASE_STATE + 2 state = state + 5 state = 16you would have to define a whole bunch operators to get things checked at run-time. And if you don't want to do that, you'll have to use some sort of cast to get the expressions to compile at all because the compiler will implicitely convert the
enum to int
in order to do the maths. But apparently, it cannot convert the result of the integer
expression back to enum.Solving this dilemma is not a hard problem, but it is an annoying problem. You should rather concentrate on your application than to worry about missing operators.
smart_enum aims to make your life easier by providing a generic framework,
which turns an enum into a simple class that can be incremented, decremented, used
in complex arithmetic expressions, and be assigned with arbitrary
values. Yet, the class
enforces that the result of these expressions will always a valid value of the underlying
enum type.
This objective is achieved by splitting the functionality into two components: An so
called Incrementor
and a generic smart_enum template, which uses a given
Incrementor to manipulate its value. This relationship is illustrated in the following
diagram:
To understand how this construct works, let us take a look at the Incrementor first. An Incrementor is any function object, that provides the member function:
enumT operator() (int val, int n)
This function accepts an arbitrary value val, which is supposed to be a valid
value of enumT, and the integer value n, by which val should
be incremented. If this expression yields a valid enumT value, the Incrementor
will return it. Otherwise, the Incrementor may throw an exception, fail with an assertion, or
simply return a default value instead.
One of the standard Incrementors provided by the library is
SequentialIncrementor, which asserts that the resulting value is in the range
[minVal, maxVal]. It is defined as follows:
template<typename enumT, enumT minVal, enumT maxVal>
struct SequentialIncrementor
: public std::binary_function<int, int, enumT>
{
enumT operator() (int val, int n) const
{
if (val + n >= minVal && val + n <= maxVal)
return enumT(val + n);
else
throw std::out_of_range("invalid enum");
}
};
Using this Incrementor combined with the smart_enum template, you can
turn an enum into a range-checked smart_enum as follows:
enum myEnum { state1, state2, state3, state4 };
typedef smart_enum<
myEnum,
SequentialIncrementor<myEnum, state1, state4>
> my_enum_t;
my_enum_t e(state1);
Of course nobody wants to write code like this, so to make your life a bit easier,
specialized versions of smart_enum are included in the library, which provide a
nicer interface. The example code shown above, could also be written as:
typedef sequential_smart_enum<myEnum, state1, state4> my_enum_t;
This is achieved by providing a short helper-class, which is hard-coded to use
SequentialIncrementor. The architecture of this class is illustrated in the
following diagram:
The library provides three standard Incrementors plus appropriate helper classes to
simlify the interface. These Incrementors are discussed in the following sections. Those, who
wish to write Incrementors of their own, should read section Using
template
instead.smart_enum directly
sequential_smart_enumThe template sequential_smart_enum ties a generic smart_enum
to a SequentialIncrementor instance, which is instantiated with the appropriate
boundaries. The class must be instantiated with the following template parameters:
sequential_smart_enum<typename enumT, enumT minVal, enumT maxVal>
enumT is — obviously — the type of the enum you'd like to wrap
into a smart_enum class, and minVal and maxVal specify the
range of valid values for this type of enum. Here is a concrete example:
enum foo { state1, state2, state3 };
sequential_smart_enum<foo, state1, state3> mystate(state1);
The resulting mystate instance behaves like the underlying enum
does, but you can use it as part of an arithmetic expression, increment, decrement, or assign
it. Every time you do, sequential_smart_enum will make sure its value is in the
range of [minVal, maxVal]. If an operation on the instance would
result in an invalid value, a std::out_of_range exception will be thrown:
my_foo_state = 0; // state1 ++my_foo_state; // state2 my_foo_state++; // state3 my_foo_state -= 2; // state1 my_foo_state = -5; // throws std::out_of_range (my_foo_state = 0)--; // throws std::out_of_range
The only significant difference between sequential_smart_enum and a plain
enum is that you cannot instantiate an uninitialized
sequential_smart_enum; the template does not provide a default constructor.
Please note that sequential_smart_enum cannot validate enums
that have gaps in their values, such as:
enum not_sequential { first = 1, second = 3 };
If you need to represent such an enum, you'll have to write an appropriate
Incrementor of your own. More on this topic can be found in section Using template
.smart_enum directly
wrapped_smart_enumThe template wrapped_smart_enum is to be used exactly like a sequential_smart_enum and has the same properties —
with one exception: The class is circular. If the value leaves the range
[minVal, maxVal] at one end, it will be set to the other end. The
class must be instantiated with the following template parameters:
wrapped_smart_enum<typename enumT, enumT minVal, enumT maxVal>
enumT is — obviously — the type of the enum you'd like to wrap
into a smart_enum class, and minVal and maxVal specify the
range of valid values for this type of enum. Here is a concrete example:
enum foo { state1, state2, state3 };
wrapped_smart_enum<foo, state1, state3> mystate(state1);
++my_foo_state; // state2
++my_foo_state; // state3
++my_foo_state; // state1 (!)
Because of its semantics, wrapped_smart_enum won't fail nor throw any
exceptions. Like sequential_smart_enum, though, it cannot validate
enums that have gaps in their values!
bounded_smart_enumThe template bounded_smart_enum is to be used exactly like a sequential_smart_enum and has the same properties —
with one exception: The class won't let its value leave the range
[minVal, maxVal] by substituting minVal for any value for
any value that is smaller than minVal and maxVal for any value that is
larger than maxVal. It must be instantiated with the following template
parameters:
bounded_smart_enum<typename enumT, enumT minVal, enumT maxVal>
enumT is — obviously — the type of the enum you'd like to wrap
into a smart_enum class, and minVal and maxVal specify the
range of valid values for this type of enum. Here is a concrete example:
enum foo { state1, state2, state3 };
bounded_smart_enum<foo, state1, state3> mystate(state1);
my_foo_state = -50; // state1 (minVal)
my_foo_state = 50; // state3 (maxVal)
++my_foo_state; // still state3
my_foo_state += 100; // and still state3
This property is achived by using the modulus operator; hence it is possible to assign arbitrary value to the instance and still get a (hopefully) meaningful result:
my_foo_state = 50; // 50 % 3 = 2 --> state3
Consequently, bounded_smart_enum won't fail nor throw any exceptions. Like
sequential_smart_enum, though, it cannot validate enums
that have gaps in their values.
smart_enum directly[to be written once the class is really stable]
boost::lambdaIf you are really adventureous, don't fear discovering esotheric compiler bugs, and
always wanted to use template meta-programming, you can combine the boost::lambda library with
smart_enum. Just set the Incrementor to be used when instantiating the
smart_enum class, then you can use a lambda function to write the code
on-the-fly:
This example almost exactly duplicates the functionality of a wrapped_smart_enum, but unlike its simpler (and more generic)
counterpart, this version can cope with an enum that is not sequential. Take a
look at the test_lambda_enum.cpp
regression test included in this library, if you want to see a full example program for this
technique.
The shapshot tarball smart-enum-HEAD.tar.gz is available for download. Further information is available in the git repository.
Copyright © 2002-2007 by Peter Simons. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.