Using an enum as an array index

I have this enum:

enum ButtonState {
    BUTTON_NORMAL = 0,
    BUTTON_PRESSED = 1,
    BUTTON_CLICKED = 2
};

const u8 NUM_BUTTON_STATES = 3;

In my Button class I have member variables ButtonState state; and ButtonColors colors[NUM_BUTTON_STATES];. When drawing the button, I use colors[state] to get the colours for whatever state the button is in.

My questions:

  1. Is this good programming style? Is there a better way to do it? (I usually only use enums with switch statements... using an enum as an array index doesn't feel right.)
  2. Do I have to specify the values of the enum? It seems to start from 0 by default and increment by 1 but is it guaranteed to work that way in all compilers?

Solution 1:

Is this good programming style?

I think so. I do the same thing quite frequently.

Is there a better way to do it?

class Button
{
public:
    // Used for array indexes!  Don't change the numbers!
  enum State {
    NORMAL = 0,
    PRESSED,
    CLICKED,
    NUMBER_OF_BUTTON_STATES
  };
};

Drawback is that NUMBER_OF_BUTTON_STATES is now a valid Button::State value. Not a big issue if you are passing these values around as ints. But trouble if you are actually expecting a Button::State.

Using an enum as an array index doesn't feel right.

It's fine. Just DOCUMENT it, so the next guy knows what's going on! (That's what comments are for.)

Do I have to specify the values of the enum?

With no '=' assignment, enum's are supposed to start at zero and increment upwards.

If a enum entry has an '=' assigned value, subsequent non '=' enum entries continue counting from there.

Source: The Annotated C++ Reference Manual, pg 113

That said, I like to specify the initial value just to make the code that much clearer.

Solution 2:

Yeah it will work well. That said, in any case, you really should put another entry in your enumeration defining the value of the amount of items:

enum ButtonState {
    BUTTON_NORMAL,
    BUTTON_PRESSED,
    BUTTON_CLICKED,
    STATE_COUNT
};

Then you can define the array like

Color colors[STATE_COUNT];

otherwise, it's a mess to keep the amount of states synchronous with the size of the array. Enumerations will always start with zero if not otherwise initialized, and then each additional entry will be assigned a value one above the previous one, if not otherwise initialized. Of course it also wouldn't hurt if you put a zero explicitly if you want. If you don't mind additional code, i would wrap the access to the raw array using a function like

Color & operator[](ButtonState state) {
    return array[state];
}

Or an equivalent getColor function forwarding the request. That would forbid directly indexing the array with some integer, which would almost certainly at some point fail because one gets the indexes wrong.