#pragma once #include #include #include #include template struct enum_traits; template concept EnumLike = std::is_enum_v; template constexpr usize enum_count_v = static_cast(enum_traits::last) - static_cast(enum_traits::first) + 1; template struct enum_array { using value_type = T; using enum_type = E; using underlying_index_type = usize; static constexpr E first = enum_traits::first; static constexpr E last = enum_traits::last; static constexpr usize size_value = enum_count_v; std::array _data {}; static constexpr usize size() noexcept { return size_value; } constexpr T *data() noexcept { return _data.data(); } constexpr T const *data() const noexcept { return _data.data(); } constexpr T *begin() noexcept { return _data.begin().operator->(); } constexpr T const *begin() const noexcept { return _data.begin().operator->(); } constexpr T *end() noexcept { return _data.end().operator->(); } constexpr T const *end() const noexcept { return _data.end().operator->(); } constexpr T &operator[](E e) noexcept { return _data[to_index(e)]; } constexpr T const &operator[](E e) const noexcept { return _data[to_index(e)]; } constexpr T &at(E e) { auto i = to_index(e); if (i >= size_value) throw std::out_of_range("enum_array::at"); return _data[i]; } constexpr T const &at(E e) const { auto i = to_index(e); if (i >= size_value) throw std::out_of_range("enum_array::at"); return _data[i]; } constexpr void fill(T const &v) { _data.fill(v); } private: static constexpr usize to_index(E e) noexcept { return static_cast(e) - static_cast(first); } }; template requires EnumLike && (std::is_same_v && ...) constexpr auto make_enum_array(T &&first_val, U &&...rest) { enum_array> arr; static_assert(sizeof...(rest) + 1 == enum_count_v, "initializer count must match enum range"); arr._data = { std::forward(first_val), std::forward(rest)... }; return arr; }