10

I'm making a enum in c++ to make a finite state machine using binary flags. It looks like:

enum VStates { NEUTRAL = 0x00000000, // 000000 // Physical Status DRY = 0x00000001, // 000001 WET = 0x00000002, // 000010 HOT = 0x00000004, // 000100 COLD = 0x00000008, // 001000 BURNED = 0x00000016, // etc.. FROZEN = 0x00000032, EROS = 0x00000064, // THANATOS = 0x00000128, // SLEEP = 0x00000256, STUNNED = 0x00000512, PARALYZED = 0x00001024, POISONED = 0x00002048, // BLIND = 0x00004096, SOFT = 0x00008192, // Flexible TOUGH = 0x00016384, // Resistent MAGNETIZED = 0x00032768, POSSEDERUNT = 0x00131072, // // Mental Status ANGRY = 0x00262144, DRUGGED = 0x00524288, // Drugs Meaning HORNY = 0x01048576, // Sexual Meaning // Material Status METAL = 0x02097152, WOOD = 0x04194304, GLASS = 0x08388608, AIR = 0x16777216, EARTH = 0x33554432, DUST = 0x67108864, LIGHT = 0x134217728, SHADOW = 0x268435456, WATER = 0x536870912, // Total Status PROTECTED = 0x1073741824, INVULNERABLE = 0x2147483648 }; 

Some status are incompatibles, so I use Bitwise operators to manage them. Now, my compiler say:

warning: integer constant is too large for 'long' type 

Is this the correct way to declare this enum? I like avoid warning so, How can I resolve this problem?

3
  • 1
    Not an answer, but this would be much simpler to write and to read if the values were written as hex constants. Commented Nov 14, 2012 at 19:20
  • 9
    @PeteBecker: They actually are written as hex constants -- but it looks like he's using the digits he should for decimal constants (e.g., he has 0x00000512, which should really be 512 or 0x200). Commented Nov 14, 2012 at 19:21
  • 16
    You do realize that 0x00000016 is not 16? These would probably fit in your enum if they were initialized with decimal, not hex constants. Commented Nov 14, 2012 at 19:23

5 Answers 5

16

In C++11, you can specify the underlying type of the enum.

#include <cstdint> enum VStates : uint64_t { // Values } 

On a side note, I recommend against calculating out all of those powers of two. You made an error in your calculations by writing a hex constant but giving it the digits of a base-10 numeral. However, rather than recalculating all of those, I recommend something like:

#include <cstdint> enum VStates : uint64_t { NEUTRAL = 0ULL, DRY = 1ULL << 0, WET = 1ULL << 1, HOT = 1ULL << 2, COLD = 1ULL << 3, // etc. } 

Then you are sure not to make a mistake. The ULL suffix ensures that the literal is accepted as at least a 64-bit wide integer.

Sign up to request clarification or add additional context in comments.

Comments

11

(Note: to make my answer complete I'll add in something I didn't take time to notice but others have pointed out: you're using the 0x prefix, which means your numbers would be interpreted as hexadecimal. They'd not actually be powers of two, and your bitflag tests would not have worked!)

If your enumerations are running out of control like this, don't use an enumerated type. Use something like a std::bitset. Then your enum can just be a simple numbered list of names for the positions of the bits in the set...and you won't be exhausting your enumeration space exponentially!

For example:

enum VState { NEUTRAL, DRY, WET, COLD, BURNED, FROZEN, /* ... */ VState_Max }; bitset<VState_Max> state; state[COLD] = true; if (state[COLD]) { cout << "I am cold\n"; } 

Now your enum is just small, maintainable numbers and you don't have to worry about being on a 64-bit platform or whatnot.

I note that you gave a value of "0" for NEUTRAL in your original example. If your intent was to have it be possible to use this in combination with other things...such as being able to be state = NEUTRAL | INVULNERABLE | SHADOW and individually test for NEUTRAL, that wouldn't have worked before. It will now...you'd just keep it in the enumeration for indexing the bitset.

But if it intended as a name for "nothing set", then you would remove it from the enum and instead test for no bits set with:

if (state.none()) { // we are in the "NEUTRAL" state of nothing set... } 

...and if you wanted to set all the bits to false, you'd go with:

state.reset(); 

7 Comments

This is a much better answer than mine, as you correct the true underlying issue. Use a std::bitset for collections of flags, not enums. Even better than using an enum to index would be to wrap it in a class that has the std::bitset as a private member with named member access functions. class VStates { public: bool is_dry() const { return bits[1]; } ... };
@DavidStone Thanks. And if packing information is of interest to this audience, then my Nstate library may be of interest...
I may end up using that. I'm currently working on an application where ~2/3 of my time is spent copying data around. A 50% decrease in size of my relevant data structures corresponds to slightly less than a 30% decrease in run time, and it seems like at least part of my application may be amenable to that solution.
@DavidStone I've considered packaging it separately from the NoCycle project, as its build dependencies are simpler and the applications are more common. It's pretty easy to chop the NState.hpp and NState.cpp files out to try it, but if anyone is using it seriously let me know and I'll standardize that. It needs to be revisited for C++11 stuff like move construction / etc.
Interestingly, occasion actually came up for me to use this a day or two after this post. I ended up going with a combination of my comment and your answer. I created a class that contained an enum { value0, value1, value2, ..., END }. The class had a std::bitset<Enum::END> member, and I accessed the values with a member function is_set(Enum e) const. This allowed me to be certain that no matter how I changed the amount of legal values, I would always index the correct position. That seems to be the best of both worlds.
|
4

The enumeration has 31 non-zero values, so they can all fit in a 32-bit unsigned value. The problem is that the values here aren't bit-values. Either write them as decimal values (remove the 0x from the front) or write them as hexadecimal values (0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, etc.) I personally don't like it, but some people write this kind of constant with shifts: 1<<0, 1<<1, 1<<2, 1<<3, etc.

2 Comments

I'm curious what you do not like about shifts? They are less error prone and a quick glance at the enum and you immediately know you can use bitwise operations on it.
@Joe - it's purely esthetic. They're not less error prone than hex values, and I think they're ugly.
3

If you are using C++11, you can declare a strongly-typed enum with a defined type as unsigned long long (_int64 in Windows, though you should probably use the portable uint64_t), if that extends your range far enough.

Thanks to Joachim for a link to examples of C++11 enum usage: Strongly Typed Enums

2 Comments

See e.g. this Wikipedia link on strongly typed enums, and how to define them. with different integer types.
@JoachimPileborg thanks, mind if I use that link to improve the answer?
0

Use smaller numbers. An enum can only be as big as a long. The size of a long depends on the compiler, but a typical size is either 32 or 64 bits. I see some 10 digit hex numbers there, those are too big.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.