The enum keyword seems like it would neat and convenient tool for me, except that it doesn't behave the way I'd expect or want...
From this reference (chosen arbitrarily).
There is an implicit conversion from any enum type to int.
...
On the other hand, there is not an implicit conversion from int to an enum type
To me, this seems backwards. Suppose I had something like this:
enum Month {jan=1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec};
It seems to me that it would be fairly natural to want to do something like this:
Month this_month = 4;
It's not a big deal that that isn't allowed. What is a big deal is this:
int an_unrelated_number = mar; //lolwut?
I just don't understand why this is allowed! I'd like to use enum, but I don't what my enums to clutter up the namespace in this way.
So my question is this: is there some trick I can use to define an enum, like the Month thing above, without implicitly defining a bunch of constant ints that I won't ever need outside of the Month enum.
enum can be converted to int, int cannot be converted to enum at least because two different enum values can hold the same integer value.
I just don't understand why this is allowed! I'd like to use enum, but I don't what my enums to clutter up the namespace in this way.
You can always define enums within a class/struct/namespace:
And then you can happily use it like this:
int day1 = DAY::MON; Day::VALUE day2 = DAY::WED;
You can also make completely typesafe enums, by defining a class that holds the values and a template class that contains the enumerated value:
//enum template template <class T> class Enum : public T { public: //bla bla constructors, destructor, setters, getters, etc. private: typename T::VALUE m_value; }; //only if DAY is not a namespace typedef Enum<DAY> Day;
And then you have a completely typesafe enum. For example, this is not possible:
int d = Day::MON;
while this is:
Day d = Day::MON;
Personally, I prefer the class solution over the namespace solution because I always need to encapsulate retrospection information in my enums, so as that the GUI is automatically created from the enums ;-).
The reason for not allowing an implicit int -> enum conversion seems ... reasonable; and axilmar's examples look like they do just what I was asking for. So thanks for all that.
Also, I did a little bit of testing with regard to the enum namespace thing...
enum Day {mon = 0, tue, wed, thr, fri}; //... { int test = tue; // this is allowed, and now test == 1 Day today = tue; } { int test = tue; int tue = 5; int test2 = tue; // now test == 1 and test2 == 5; Everything is fine so far Day today = tue; // but this is not allowed }
So the result of all that is that although the enum stuff does hang around even when it isn't wanted, it does a decent job of staying out of the way.
For what I'm doing, I think I'll try axilmar's class thing. (edit - ** see below)
--
type568, I don't think what you said about multiple enum values being the same int value is correct – because of this:
enum Day {mon = 0, tue = 0, wed, thr, fri}; Day today = mon; if (today == tue) cout << "tuesday!";
This code results in "tuesday!" being printed. The point is that although multiple labels can apply to one number, it is still just one number. So this doesn't create a problem for converting ints to enums.
--
[edit]
I'm having some trouble getting that class solution to work. I guess I didn't really understand what I was meant to do. Here's what I've got so far:
I'm having some trouble getting that class solution to work.
You should not provide this operator:
operator typename T::VALUE() { return m_value; }
Because C++ sees it and converts your class to int.
Ok, but even with that line commented out, x = Day::mon; is compiled without warnings or errors. Actually I don't understand what would prevent it from being allowed. This 'Day' class, made using the template, has all the properties that struct DAY has and they are all public. So if DAY::mon works, then Day::mon will also work.
I'm trying to achieve what you described here:
And then you have a completely typesafe enum. For example, this is not possible:
int d = Day::MON;
while this is:
Day d = Day::MON;
If you've done this in your own projects, would you mind posting a complete example? Because although I thought I understood how it worked before, I don't see any way forward now.
From this [enel.ucalgary.ca] reference (chosen arbitrarily).
There is an implicit conversion from any enum type to int.
...
On the other hand, there is not an implicit conversion from int to an enum type
To me, this seems backwards. Suppose I had something like this:
An enum is an integer (you can implicitly cast enum->int), but an integer isn't an enum (you can't implicitly cast int->enum). If you think about it, it makes sense. Lets say you had code like this:
enum Foo { VAL1 = 1, VAL2 = 3 }; void Func(int ival) { Foo eval = ival; ... }
Now, what should happen if you call Func(0); or Func(2);? If you try to forcefully cast it, you essentially break the type, since it has a value that's not part of its enumeration values.
You could argue it should know if given a determinable value, but that would cause an inconsistency as it could accept some integer variables and not others.
Right, that seems fair. But I think an equally fair alternative would be to just treat enum as an int in which some particular values have names. So any int value is allowed by the enum, but not all values have special names. That's how it works in C, and I think it's fine*.
However, I completely understand the reasons behind not allowing implicit conversion from int to enum, and I think the reasons are sensible and fair. I just expected that it would be the same as in C, for consistency, but it isn't.
The main thing I don't like is the other side of things - the fact that the labels can be used outside of the enum (C has the same problem). I want the enum labels to apply only to the enum. I don't see any reason to spew the enum definitions across the entire namespace, and it prevents me from doing stuff like this:
enum Type1 {empty = 0, rock, sand, water, grass}; enum Type2 {empty = 0, road, water, tree, dirt}; // error: conflicting declaration 'water' // but all I want is for 'water' to refer to 3 when I'm using Type1, and 2 when using Type2. There should be no conflict.
If I wanted to define a bunch of constant ints, I'd define a bunch of constant ints... like this const int empty = 0, rock = 1, sand = 2, water = 3, grass = 4; // not what I want
But I think an equally fair alternative would be to just treat enum as an int in which some particular values have names. So any int value is allowed by the enum, but not all values have special names. That's how it works in C, and I think it's fine*.
I like to think of enums as strong integer types, such that an enum variable will only contain a valid enumeration value for it. With GCC, for example, if you do this:
enum Foo { VAL1, VAL2, VAL3 }; void Func(Foo val) { switch(val) { case VAL1: ... case VAL2: ... } }
It will throw a warning that VAL3 is not handled. And given that an enum can't contain an invalid enumeration value (unless you fail to initialize it, or forcefully cast a value), it's very useful for keeping a variable restrained to specific values. C++'s stronger type safety just helps enforce that.
EDIT:
enum Type1 {empty = 0, rock, sand, water, grass}; enum Type2 {empty = 0, road, water, tree, dirt}; // error: conflicting declaration 'water' // but all I want is for 'water' to refer to 3 when I'm using Type1, and 2 when using Type2. There should be no conflict.
You can use a namespace:
namespace nType1 { enum Type1...; }; using namespace nType1; namespace nType2 { enum Type2...; }; using namespace nType2;
Though you'll have to use nType1::water or nType2::water, if there's a duplicate enum value name. You can also use operator overrides to handle casts from a Type1 enum value to a Type2 one, and vice-versa.
And given that an enum can't contain an invalid enumeration value (unless you fail to initialize it, or forcefully cast a value), it's very useful for keeping a variable restrained to specific values.
In C and C++, enum is just loosely controlled syntactic sugar over an int. The type doesn't mean anything in particular, except that certain values can be accessed by a meaningful name (albeit, the namespace issue pretty much forces the name to be uber long or potentially conflicting). enum types are not very type safe at all. You certainly can't rely on them. They basically provide the programmer with a semantically sensible alternative to magic numbers. Essentially, identical to a set of const ints and an int; except that C++ requires a cast. Requiring a cast is a good thing, but it still isn't something that you can rely on nobody doing. For that it would need to be impossible to cast to an enum type, which would have its own downfalls. Validation is your friend.
I've that feeling, that the more useless a feature is the more it is discussed.
I've that feeling, that the more useless a feature is the more it is discussed.
Of course! Nobody argues about whether stuff that's obviously necessary should exist.
In this case, I'm hoping that our discussions will reveal a way of making enum less useless (more useful). Maybe people discuss useless stuff in general because that's the stuff that needs improving.
That's the stuff that needs removing.
Requiring a cast is a good thing, but it still isn't something that you can rely on nobody doing. For that it would need to be impossible to cast to an enum type, which would have its own downfalls.
You can say that about anything, really. If you can't rely on the code being safe with enum values (ie, not casting an unverified int value), then by the same token you couldn't rely on the code being safe with object pointers types (ie, not casting from a potentially incompatible pointer type to another; particularly from void*). Both require you to go out of your way to break it, in C++.
While it is true that pre-existing code may be more likely to break C++ enums than pointers, because people misunderstand their purpose and think they're just named integers or const int variables, it's a different case when you are in control of the code base. If you control the code, it's not difficult to make sure you don't cast unverified values (or preferrably not cast at all) from integers. When handled properly, enums can make the code more elegant and less error-prone.
If you've done this in your own projects, would you mind posting a complete example? Because although I thought I understood how it worked before, I don't see any way forward now.
Here is the code:
And here is the compiler error:
1>e:\temp\test_enum\main.cpp(39) : error C2440: '=' : cannot convert from 'Day' to 'int'
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
You can say that about anything, really. If you can't rely on the code being safe with enum values (ie, not casting an unverified int value), then by the same token you couldn't rely on the code being safe with object pointers types (ie, not casting from a potentially incompatible pointer type to another; particularly from void*).
You can't.
That's why modern languages, like Java and C#, make it more difficult or impossible to do such things. You can never really rely on them being correct. Unfortunately, most code is written by many people and it can be very difficult to spot such evils, even if you do review patches, etc.
The best thing to do is to validate when possible. You can't validate a pointer (aside from ensuring it isn't 0), but you can damn sure validate enum types. The easiest way is probably to handle them with a switch statement with a default case that handles the error or design the program so that an invalid enum value has no side effects (when appropriate). All I'm saying is that you can't rely on them and that if you want your code to be closer to bullet proof then you should assume that the value can be invalid and handle that case.
T::VALUE()
Is that a function call?
WTF is going on there?
ah. I understand now. What I had was on the right track but I thought that it would prevent i = Day::MON;, which it doesn't. But it does prevent i = day;.. So it isn't a complete solution to my complaint, but it is an improvement over the usual enum behavior. Thanks for that.
Is that a function call? WTF is going on there?
That's meant to be the default initializer of T::VALUE. So it should result in the default value of the enum. Actually, I the line needs to be Enum(typename T::VALUE value = typename T::VALUE()) : m_value(value) {}, I think. I don't really know why it needs the "typename" keyword, but the compiler was complaining without it.
but I thought that it would prevent i = Day::MON
Unfortunately, it's not possible.
In my never-to-be-released language, my enums where strongly typed sets of values and therefore converting from enum to integer or integer to enum wouldn't be possible ;-).
So it should result in the default value of the enum.
Actually, it just sets the value to 0, which may or may not be the default value. The correct approach would be to set the value to the enum's first member, but since enums are ints, it is just set to 0.
<quote>
the line needs to be Enum(typename T::VALUE value = typename T::VALUE()) : m_value(value) {}
</code>
I thought so too, but the compiler complains if I put 'typename' in front of 'T::VALUE()'.
C++0x seems to solve all these problems. If you declare an enum with enum class then it's strongly typed (no conversion from or to integers) and the scope of the values is inside the enum name.
That's good news about C++0x. I look forward to it. 
In fact, maybe it's time I updated my gcc (which is currently v3.4.5). By the sounds of this I'd be able to start using this feature right away. -- but then I'd have to recompile allegro (wouldn't I?)
That's why modern languages, like Java and C#, make it more difficult or impossible to do such things.
In C#, you can cast any enum back to its underlying integer type without a compiler error; the only thing that's required is that the cast be explicit. The enum behaves exactly as in C++.
Compile and run for your pleasure:
Seriously, the idea is not to make things 120% foolproof. The idea is to provide programmers with tools that prevent them from accidentally doing stupid things. If you want to assign a value to an enumeration variable that's not in the enumeration, you can, but the language makes you jump through a little inconvenient hoop, which should be enough to remind you that you're not supposed to do this unless you absolutely have to (e.g., when reading enum values from a data source that has no notion of enumerations, like a binary file, or when interfacing with a closed API that provides raw integers only).
In C#, you can cast any enum back to its underlying integer type without a compiler error; the only thing that's required is that the cast be explicit. The enum behaves exactly as in C++.
I think I was actually referring to the casting of incompatible pointers part.
I'm pretty sure a determined programmer will find a creative way around that, too.