I didn't see "pretty" in your requirements, so I submit this solution implemented using the Boost Preprocessor library.
As an up-front disclaimer, I haven't used Boost.Preprocessor a whole lot and I've only tested this with the test cases presented here, so there could be bugs, and there may be an easier, cleaner way to do this. I certainly welcome comments, corrections, suggestions, insults, etc.
Here we go:
#include <boost/preprocessor.hpp>
#define EXPAND_ENUM_VALUE(r, data, i, elem) \
BOOST_PP_SEQ_ELEM(0, elem) \
BOOST_PP_IIF( \
BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(elem), 2), \
= BOOST_PP_SEQ_ELEM(1, elem), \
BOOST_PP_EMPTY()) \
BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(data, BOOST_PP_ADD(i, 1)))
#define ADD_CASE_FOR_ENUM_VALUE(r, data, elem) \
case BOOST_PP_SEQ_ELEM(0, elem) : break;
#define DEFINE_UNIQUE_ENUM(name, values) \
enum name \
{ \
BOOST_PP_SEQ_FOR_EACH_I(EXPAND_ENUM_VALUE, \
BOOST_PP_SEQ_SIZE(values), values) \
}; \
\
namespace detail \
{ \
void UniqueEnumSanityCheck##name() \
{ \
switch (name()) \
{ \
BOOST_PP_SEQ_FOR_EACH(ADD_CASE_FOR_ENUM_VALUE, name, values) \
} \
} \
}
We can then use it like so:
DEFINE_UNIQUE_ENUM(DayOfWeek, ((Monday) (1))
((Tuesday) (2))
((Wednesday) )
((Thursday) (4)))
The enumerator value is optional; this code generates an enumeration equivalent to:
enum DayOfWeek
{
Monday = 1,
Tuesday = 2,
Wednesday,
Thursday = 4
};
It also generates a sanity-check function that contains a switch statement as described in Ben Voigt's answer. If we change the enumeration declaration such that we have non-unique enumerator values, e.g.,
DEFINE_UNIQUE_ENUM(DayOfWeek, ((Monday) (1))
((Tuesday) (2))
((Wednesday) )
((Thursday) (1)))
it will not compile (Visual C++ reports the expected error C2196: case value '1' already used).
Thanks also to Matthieu M., whose answer to another question got me interested in the Boost Preprocessor library.