enum class as array index

Viewed 30845

I made an enum as:

enum class KeyPressSurfaces {
    KEY_PRESS_SURFACE_DEFAULT,
    KEY_PRESS_SURFACE_UP,
    KEY_PRESS_SURFACE_DOWN,
    KEY_PRESS_SURFACE_LEFT,
    KEY_PRESS_SURFACE_RIGHT,
    KEY_PRESS_SURFACE_TOTAL
};

and later on I attempt to define an array as I typed below, but I received the error, size of array 'KEY_PRESS_SURFACES' has non-integral type 'KeyPressSurfaces'

SDL_Surface*KEY_PRESS_SURFACES[KeyPressSurfaces::KEY_PRESS_SURFACE_TOTAL];

I understand the error fine, but I don't know where to move the KeyPressSurfaces to qualify the constant in the enum.

I also realize I could just use an enum and not an enum class, but I feel like this should work, and I want to learn how to do this.

Any response/advice?

9 Answers

Building on the other responses, another alternative is a simple templated class that wraps a c-style array. With the EnumArray example below, any enum class with a kMaxValue can be used as the index.

In my opinion, the improved readability is worth the introduction of a template.

template <class IndexType, class ValueType>
class EnumArray {
 public:  
  ValueType& operator[](IndexType i) { 
    return array_[static_cast<int>(i)];
  }

  const ValueType& operator[](IndexType i) const {
    return array_[static_cast<int>(i)];
  }

  int size() const { return size_; }

 private:
  ValueType array_[static_cast<int>(IndexType::kMaxValue) + 1];

  int size_ = static_cast<int>(IndexType::kMaxValue) + 1;
}; 

enum class KeyPressSurfaces {
    KEY_PRESS_SURFACE_DEFAULT,
    KEY_PRESS_SURFACE_UP,
    KEY_PRESS_SURFACE_DOWN,
    KEY_PRESS_SURFACE_LEFT,
    KEY_PRESS_SURFACE_RIGHT,
    KEY_PRESS_SURFACE_TOTAL,
    kMaxValue = KEY_PRESS_SURFACE_TOTAL
};

int main() {
    EnumArray<KeyPressSurfaces, int> array;
    array[KeyPressSurfaces::KEY_PRESS_SURFACE_DEFAULT] = 5;
    std::cout << array[KeyPressSurfaces::KEY_PRESS_SURFACE_DEFAULT] << std::endl;
    return 0;
}

You can work on the array:

/** \brief It's either this or removing the "class" from "enum class" */
template <class T, std::size_t N>
struct EnumClassArray : std::array<T, N>
{
    template <typename I>
    T& operator[](const I& i) { return std::array<T, N>::operator[](static_cast<std::underlying_type<I>::type>(i)); }
    template <typename I>
    const T& operator[](const I& i) const { return std::array<T, N>::operator[](static_cast<std::underlying_type<I>::type>(i)); }
};

You can use a namespace and anonymous enum. So you can get rid of these ugly prefixes and use enum items as index.

namespace KeyPressSurfaces
{
    enum
    {
        DEFAULT = 0,
        UP,
        DOWN,
        LEFT,
        RIGHT,
        TOTAL
    };
}

SDL_Surface* KEY_PRESS_SURFACES[KeyPressSurfaces::TOTAL];

In addition to the currently accepted answer, you could write a function to get a reference to the surface:

enum class KeyPressSurface
{
    DEFAULT,
    UP,
    DOWN,
    LEFT,
    RIGHT,
    TOTAL
};
// This is using static_cast like the accepted answer
std::array<SDL_Surface *, static_cast<int>(KeyPressSurface::TOTAL)> keyPressSurfaces;

// Function to get a reference to a surface
SDL_Surface *&getKeyPressSurface(KeyPressSurface surface)
{
    return keyPressSurfaces[static_cast<int>(surface)];
}

Now you can cleanly get a surface using the enum class:

// assignment
getKeyPressSurface(KeyPressSurface::UP) = SDL_LoadBMP("lamp.bmp");
// or get a value
SDL_Surface *currentSurface = getKeyPressSurface(KeyPressSurface::RIGHT);

Use struct members instead of enum.

struct KeyPressSurfaces {
    static constexpr int KEY_PRESS_SURFACE_DEFAULT = 0;
    static constexpr int KEY_PRESS_SURFACE_UP= 1;
    static constexpr int KEY_PRESS_SURFACE_DOWN = 2;
    static constexpr int KEY_PRESS_SURFACE_LEFT = 3;
    static constexpr int KEY_PRESS_SURFACE_RIGHT = 4;
    static constexpr int KEY_PRESS_SURFACE_TOTAL = 5;
};

Alternatively, have them in a namespace with the same logic, where you can benefit from using namespace.

namespace KeyPressSurfaces {
    constexpr int KEY_PRESS_SURFACE_DEFAULT = 0;
    constexpr int KEY_PRESS_SURFACE_UP= 1;
    constexpr int KEY_PRESS_SURFACE_DOWN = 2;
    constexpr int KEY_PRESS_SURFACE_LEFT = 3;
    constexpr int KEY_PRESS_SURFACE_RIGHT = 4;
    constexpr int KEY_PRESS_SURFACE_TOTAL = 5;
}
SDL_Surface* KEY_PRESS_SURFACES[KeyPressSurfaces::KEY_PRESS_SURFACE_TOTAL];
Related