It is common knowledge that built-in enums in C++ are not typesafe. I was wondering which classes implementing typesafe enums are used out there... I myself use the following "bicycle", but it is somewhat verbose and limited:
typesafeenum.h:
struct TypesafeEnum
{
// Construction:
public:
TypesafeEnum(): id (next_id++), name("") {}
TypesafeEnum(const std::string& n): id(next_id++), name(n) {}
// Operations:
public:
bool operator == (const TypesafeEnum& right) const;
bool operator != (const TypesafeEnum& right) const;
bool operator < (const TypesafeEnum& right) const;
std::string to_string() const { return name; }
// Implementation:
private:
static int next_id;
int id;
std::string name;
};
typesafeenum.cpp:
int TypesafeEnum::next_id = 1;
bool TypesafeEnum::operator== (const TypesafeEnum& right) const
{ return id == right.id; }
bool TypesafeEnum::operator!= (const TypesafeEnum& right) const
{ return !operator== (right); }
bool TypesafeEnum::operator< (const TypesafeEnum& right) const
{ return id < right.id; }
Usage:
class Dialog
{
...
struct Result: public TypesafeEnum
{
static const Result CANCEL("Cancel");
static const Result OK("Ok");
};
Result doModal();
...
};
const Dialog::Result Dialog::Result::OK;
const Dialog::Result Dialog::Result::CANCEL;
Addition: I think I should have been more specific about the requirements. I'll try to summarize them:
Priority 1: Setting an enum variable to an invalid value should be impossible (a compile-time error) with no exceptions.
Priority 2: Converting an enum value to/from an int should be possible with a single explicit function/method call.
Priority 3: As compact, elegant and convenient declaration and usage as possible
Priority 4: Converting enum values to and from strings.
Priority 5: (Nice to have) Possibility to iterate over enum values.
My take is that you're inventing a problem and then fitting a solution onto it. I see no need to do an elaborate framework for an enumeration of values. If you are dedicated to having your values only be members of a certain set, you could hack up a variant of a unique set datatype.
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: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 anenum
and an int) from being directly compared, because when you use values directly you force the implicit conversion toint
: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: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 withenum
s or not...I think the Java
enum
would be a good model to follow. Essentially, the Java form would look like this:What's interesting about the Java approach is that
OK
andCANCEL
are immutable, singleton instances ofResult
(with the methods that you see). You cannot create any further instances ofResult
. Since they're singletons, you can compare by pointer/reference---very handy. :-)ETA: In Java, instead of doing bitmasks by hand, instead you use an
EnumSet
to specify a bit set (it implements theSet
interface, and works like sets---but implemented using bitmasks). Much more readable than hand-written bitmask manipulation!I'm currently playing around with the Boost.Enum proposal from the Boost Vault (filename
enum_rev4.6.zip
). Although it was never officially submitted for inclusion into Boost, it's useable as-is. (Documentation is lacking but is made up for by clear source code and good tests.)Boost.Enum lets you declare an enum like this:
And have it automatically expand to this:
It satisfies all five of the priorities which you list.
I use C++0x typesafe enums. I use some helper template/macros that provide the to/from string functionality.
Use
boost::variant
!After trying a lot of the above ideas and finding them lacking I hit upon this simple approach:
You can probably come up with a macro to generate the boilerplate. (Let me know if you do.)
Unlike other approaches this one is actually type-safe and works with old C++. You can even make cool types like
boost::variant<int, A_t, B_t, boost::none>
, for example, to represent a value that could be A, B, an integer or nothing which is almost Haskell98 levels of type safety.Downsides to be aware of:
Update
Here, for your convenience is your typesafe-enum "library". Paste this header:
And use it like:
Notice you have to say
A_t
instead ofA
in theENUM
macro which destroys some of the magic. Oh well. Also, notice there's now atoStr
function and atoInt
function to meet OPs requirement of simple conversion to strings and ints. The requirement I can't figure out is a way to iterate over the items. Let me know if you know how to write such a thing.