summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorPokechu22 <Pokechu022@gmail.com>2021-04-24 19:26:27 -0700
committerPokechu22 <Pokechu022@gmail.com>2021-10-24 11:48:36 -0700
commitba107819ec9bc826fd910bdfc6c65fcdecae3f9e (patch)
tree12bdaaf174500ac4cba06635cbb5c05db0fcb745 /Source
parenta80fcf38ae51d5a1da76c878f89a5bd073de1c72 (diff)
Create EnumMap
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Common/CMakeLists.txt1
-rw-r--r--Source/Core/Common/ChunkFile.h7
-rw-r--r--Source/Core/Common/EnumFormatter.h21
-rw-r--r--Source/Core/Common/EnumMap.h83
-rw-r--r--Source/Core/Common/TypeUtils.h17
-rw-r--r--Source/Core/DolphinLib.props1
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" />