diff options
| author | Pokechu22 <Pokechu022@gmail.com> | 2021-04-24 19:26:27 -0700 |
|---|---|---|
| committer | Pokechu22 <Pokechu022@gmail.com> | 2021-10-24 11:48:36 -0700 |
| commit | ba107819ec9bc826fd910bdfc6c65fcdecae3f9e (patch) | |
| tree | 12bdaaf174500ac4cba06635cbb5c05db0fcb745 /Source | |
| parent | a80fcf38ae51d5a1da76c878f89a5bd073de1c72 (diff) | |
Create EnumMap
Diffstat (limited to 'Source')
| -rw-r--r-- | Source/Core/Common/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | Source/Core/Common/ChunkFile.h | 7 | ||||
| -rw-r--r-- | Source/Core/Common/EnumFormatter.h | 21 | ||||
| -rw-r--r-- | Source/Core/Common/EnumMap.h | 83 | ||||
| -rw-r--r-- | Source/Core/Common/TypeUtils.h | 17 | ||||
| -rw-r--r-- | Source/Core/DolphinLib.props | 1 |
6 files changed, 122 insertions, 8 deletions
diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 1b806129fd..b1e25af45b 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -42,6 +42,7 @@ add_library(common ENetUtil.cpp ENetUtil.h EnumFormatter.h + EnumMap.h Event.h FileSearch.cpp FileSearch.h diff --git a/Source/Core/Common/ChunkFile.h b/Source/Core/Common/ChunkFile.h index 85507354af..8b84a4cf1c 100644 --- a/Source/Core/Common/ChunkFile.h +++ b/Source/Core/Common/ChunkFile.h @@ -27,6 +27,7 @@ #include "Common/Assert.h" #include "Common/CommonTypes.h" +#include "Common/EnumMap.h" #include "Common/Flag.h" #include "Common/Inline.h" #include "Common/Logging/Log.h" @@ -175,6 +176,12 @@ public: DoArray(x.data(), static_cast<u32>(x.size())); } + template <typename V, auto last_member, typename = decltype(last_member)> + void DoArray(Common::EnumMap<V, last_member>& x) + { + DoArray(x.data(), static_cast<u32>(x.size())); + } + template <typename T, typename std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0> void DoArray(T* x, u32 count) { diff --git a/Source/Core/Common/EnumFormatter.h b/Source/Core/Common/EnumFormatter.h index f590e761d4..1ab6bbeadd 100644 --- a/Source/Core/Common/EnumFormatter.h +++ b/Source/Core/Common/EnumFormatter.h @@ -3,7 +3,8 @@ #pragma once -#include <array> +#include "Common/EnumMap.h" + #include <fmt/format.h> #include <type_traits> @@ -41,11 +42,15 @@ * formatter() : EnumFormatter(names) {} * }; */ -template <auto last_member, typename T = decltype(last_member), - size_t size = static_cast<size_t>(last_member) + 1, - std::enable_if_t<std::is_enum_v<T>, bool> = true> +template <auto last_member, typename = decltype(last_member)> class EnumFormatter { + // The second template argument is needed to avoid compile errors from ambiguity with multiple + // enums with the same number of members in GCC prior to 8. See https://godbolt.org/z/xcKaW1seW + // and https://godbolt.org/z/hz7Yqq1P5 + using T = decltype(last_member); + static_assert(std::is_enum_v<T>); + public: constexpr auto parse(fmt::format_parse_context& ctx) { @@ -61,19 +66,19 @@ public: { const auto value_s = static_cast<std::underlying_type_t<T>>(e); // Possibly signed const auto value_u = static_cast<std::make_unsigned_t<T>>(value_s); // Always unsigned - const bool has_name = value_s >= 0 && value_u < size && m_names[value_u] != nullptr; + const bool has_name = m_names.InBounds(e) && m_names[e] != nullptr; if (!formatting_for_shader) { if (has_name) - return fmt::format_to(ctx.out(), "{} ({})", m_names[value_u], value_s); + return fmt::format_to(ctx.out(), "{} ({})", m_names[e], value_s); else return fmt::format_to(ctx.out(), "Invalid ({})", value_s); } else { if (has_name) - return fmt::format_to(ctx.out(), "{:#x}u /* {} */", value_u, m_names[value_u]); + return fmt::format_to(ctx.out(), "{:#x}u /* {} */", value_u, m_names[e]); else return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */", value_u); } @@ -81,7 +86,7 @@ public: protected: // This is needed because std::array deduces incorrectly if nullptr is included in the list - using array_type = std::array<const char*, size>; + using array_type = Common::EnumMap<const char*, last_member>; constexpr explicit EnumFormatter(const array_type names) : m_names(std::move(names)) {} diff --git a/Source/Core/Common/EnumMap.h b/Source/Core/Common/EnumMap.h new file mode 100644 index 0000000000..478ff42ec7 --- /dev/null +++ b/Source/Core/Common/EnumMap.h @@ -0,0 +1,83 @@ +// Copyright 2021 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <type_traits> + +#include "Common/TypeUtils.h" + +template <std::size_t position, std::size_t bits, typename T, typename StorageType> +struct BitField; + +namespace Common +{ +// A type that allows lookup of values associated with an enum as the key. +// Designed for enums whose numeric values start at 0 and increment continuously with few gaps. +template <typename V, auto last_member, typename = decltype(last_member)> +class EnumMap final +{ + // The third template argument is needed to avoid compile errors from ambiguity with multiple + // enums with the same number of members in GCC prior to 8. See https://godbolt.org/z/xcKaW1seW + // and https://godbolt.org/z/hz7Yqq1P5 + using T = decltype(last_member); + static_assert(std::is_enum_v<T>); + static constexpr size_t s_size = static_cast<size_t>(last_member) + 1; + + using array_type = std::array<V, s_size>; + using iterator = typename array_type::iterator; + using const_iterator = typename array_type::const_iterator; + +public: + constexpr EnumMap() = default; + constexpr EnumMap(const EnumMap& other) = default; + constexpr EnumMap& operator=(const EnumMap& other) = default; + constexpr EnumMap(EnumMap&& other) = default; + constexpr EnumMap& operator=(EnumMap&& other) = default; + + // Constructor that accepts exactly size Vs (enforcing that all must be specified). + template <typename... T, typename = std::enable_if_t<Common::IsNOf<V, s_size, T...>::value>> + constexpr EnumMap(T... values) : m_array{static_cast<V>(values)...} + { + } + + constexpr const V& operator[](T key) const { return m_array[static_cast<std::size_t>(key)]; } + constexpr V& operator[](T key) { return m_array[static_cast<std::size_t>(key)]; } + + // These only exist to perform the safety check; without them, BitField's implicit conversion + // would work (but since BitField is used for game-generated data, we need to be careful about + // bounds-checking) + template <std::size_t position, std::size_t bits, typename StorageType> + constexpr const V& operator[](BitField<position, bits, T, StorageType> key) const + { + static_assert(1 << bits == s_size, "Unsafe indexing into EnumMap (may go out of bounds)"); + return m_array[static_cast<std::size_t>(key.Value())]; + } + template <std::size_t position, std::size_t bits, typename StorageType> + constexpr V& operator[](BitField<position, bits, T, StorageType> key) + { + static_assert(1 << bits == s_size, "Unsafe indexing into EnumMap (may go out of bounds)"); + return m_array[static_cast<std::size_t>(key.value())]; + } + + constexpr bool InBounds(T key) const { return static_cast<std::size_t>(key) < s_size; } + + constexpr size_t size() const noexcept { return s_size; } + + constexpr V* data() { return m_array.data(); } + constexpr const V* data() const { return m_array.data(); } + + constexpr iterator begin() { return m_array.begin(); } + constexpr iterator end() { return m_array.end(); } + constexpr const_iterator begin() const { return m_array.begin(); } + constexpr const_iterator end() const { return m_array.end(); } + constexpr const_iterator cbegin() const { return m_array.cbegin(); } + constexpr const_iterator cend() const { return m_array.cend(); } + + constexpr void fill(const V& v) { m_array.fill(v); } + +private: + array_type m_array{}; +}; +} // namespace Common diff --git a/Source/Core/Common/TypeUtils.h b/Source/Core/Common/TypeUtils.h index c6875948c7..47077e11fb 100644 --- a/Source/Core/Common/TypeUtils.h +++ b/Source/Core/Common/TypeUtils.h @@ -3,6 +3,7 @@ #pragma once +#include <cstddef> #include <type_traits> namespace Common @@ -66,4 +67,20 @@ static_assert(std::is_same_v<ObjectType<&Bar::d>, Bar>); static_assert(std::is_same_v<ObjectType<&Bar::c>, Foo>); static_assert(!std::is_same_v<ObjectType<&Bar::c>, Bar>); } // namespace detail + +// Template for checking if Types is count occurrences of T. +template <typename T, size_t count, typename... Ts> +struct IsNOf : std::integral_constant<bool, std::conjunction_v<std::is_convertible<Ts, T>...> && + sizeof...(Ts) == count> +{ +}; + +static_assert(IsNOf<int, 0>::value); +static_assert(!IsNOf<int, 0, int>::value); +static_assert(IsNOf<int, 1, int>::value); +static_assert(!IsNOf<int, 1>::value); +static_assert(!IsNOf<int, 1, int, int>::value); +static_assert(IsNOf<int, 2, int, int>::value); +static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed +static_assert(!IsNOf<int, 2, int, char*>::value); } // namespace Common diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index b7e9bf3b32..fc429c0ab4 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -43,6 +43,7 @@ <ClInclude Include="Common\DynamicLibrary.h" /> <ClInclude Include="Common\ENetUtil.h" /> <ClInclude Include="Common\EnumFormatter.h" /> + <ClInclude Include="Common\EnumMap.h" /> <ClInclude Include="Common\Event.h" /> <ClInclude Include="Common\FileSearch.h" /> <ClInclude Include="Common\FileUtil.h" /> |
