I'm personally using an adapted version of the typesafe enum idiom. It doesn't provide all the five "requirements" that you've stated in your edit, but I strongly disagree with some of them anyway. For example, I don't see how Prio#4 (conversion of values to strings) has anything to do with type safety. Most of the time string representation of individual values should be separate from the definition of the type anyway (think i18n for a simple reason why). Prio#5 (iteratio, which is optional) is one of the nicest things I'd like to see naturally happening in enums, so I felt sad that it appears as "optional" in your request, but it seems it is better addressed via a separate iteration system such as begin
/end
functions or an enum_iterator, which makes them work seamlessly with STL and C++11 foreach.
OTOH this simple idiom nicely provides Prio#3 Prio#1 thanks to the fact that it mostly only wraps enum
s with more type information. Not to mention it is a very simple solution that for the most part doesn't require any external dependency headers, so it's pretty easy to carry around. It also has the advantage of making enumerations scoped a-la-C++11:
// This doesn't compile, and if it did it wouldn't work anyway
enum colors { salmon, .... };
enum fishes { salmon, .... };
// This, however, works seamlessly.
struct colors_def { enum type { salmon, .... }; };
struct fishes_def { enum type { salmon, .... }; };
typedef typesafe_enum<colors_def> colors;
typedef typesafe_enum<fishes_def> fishes;
The only "hole" that solution provides is that it doesn't address the fact that it doesn't prevent enum
s of different types (or an enum
and an int) from being directly compared, because when you use values directly you force the implicit conversion to int
:
if (colors::salmon == fishes::salmon) { .../* Ooops! */... }
But so far I've found such problems can be solved by simply offering a better comparison to the compiler - for example, explicitly providing an operator that compares any two different enum
types, then forcing it to fail:
// I'm using backports of C++11 utilities like static_assert and enable_if
template <typename Enum1, typename Enum2>
typename enable_if< (is_enum<Enum1>::value && is_enum<Enum2>::value) && (false == is_same<Enum1,Enum2>::value) , bool >
::type operator== (Enum1, Enum2) {
static_assert (false, "Comparing enumerations of different types!");
}
Though it doesn't seem to break code so far, and it does to explicitly deal with the specific problem without doing something else, I'm not sure it such thing is a thing one "should" do (I suspect it will interfere with enum
s already taking part in conversion operators declared elsewhere; I'd gladly receive commentary about this).
Combining this with the above typesafe idiom gives something that is relatively close to C++11 enum class
in humanibility (readability and maintainability) without having to do anything too obscure. And I have to admit it was fun to do, I had never thought to actually ask the compiler if I was dealing with enum
s or not...