mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Create EnumMap
This commit is contained in:
parent
a80fcf38ae
commit
ba107819ec
@ -42,6 +42,7 @@ add_library(common
|
|||||||
ENetUtil.cpp
|
ENetUtil.cpp
|
||||||
ENetUtil.h
|
ENetUtil.h
|
||||||
EnumFormatter.h
|
EnumFormatter.h
|
||||||
|
EnumMap.h
|
||||||
Event.h
|
Event.h
|
||||||
FileSearch.cpp
|
FileSearch.cpp
|
||||||
FileSearch.h
|
FileSearch.h
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EnumMap.h"
|
||||||
#include "Common/Flag.h"
|
#include "Common/Flag.h"
|
||||||
#include "Common/Inline.h"
|
#include "Common/Inline.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
@ -175,6 +176,12 @@ public:
|
|||||||
DoArray(x.data(), static_cast<u32>(x.size()));
|
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>
|
template <typename T, typename std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0>
|
||||||
void DoArray(T* x, u32 count)
|
void DoArray(T* x, u32 count)
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include "Common/EnumMap.h"
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@ -41,11 +42,15 @@
|
|||||||
* formatter() : EnumFormatter(names) {}
|
* formatter() : EnumFormatter(names) {}
|
||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
template <auto last_member, typename T = decltype(last_member),
|
template <auto last_member, typename = decltype(last_member)>
|
||||||
size_t size = static_cast<size_t>(last_member) + 1,
|
|
||||||
std::enable_if_t<std::is_enum_v<T>, bool> = true>
|
|
||||||
class EnumFormatter
|
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:
|
public:
|
||||||
constexpr auto parse(fmt::format_parse_context& ctx)
|
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_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 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 (!formatting_for_shader)
|
||||||
{
|
{
|
||||||
if (has_name)
|
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
|
else
|
||||||
return fmt::format_to(ctx.out(), "Invalid ({})", value_s);
|
return fmt::format_to(ctx.out(), "Invalid ({})", value_s);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (has_name)
|
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
|
else
|
||||||
return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */", value_u);
|
return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */", value_u);
|
||||||
}
|
}
|
||||||
@ -81,7 +86,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// This is needed because std::array deduces incorrectly if nullptr is included in the list
|
// 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)) {}
|
constexpr explicit EnumFormatter(const array_type names) : m_names(std::move(names)) {}
|
||||||
|
|
||||||
|
83
Source/Core/Common/EnumMap.h
Normal file
83
Source/Core/Common/EnumMap.h
Normal file
@ -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
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace Common
|
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>, Foo>);
|
||||||
static_assert(!std::is_same_v<ObjectType<&Bar::c>, Bar>);
|
static_assert(!std::is_same_v<ObjectType<&Bar::c>, Bar>);
|
||||||
} // namespace detail
|
} // 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
|
} // namespace Common
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
<ClInclude Include="Common\DynamicLibrary.h" />
|
<ClInclude Include="Common\DynamicLibrary.h" />
|
||||||
<ClInclude Include="Common\ENetUtil.h" />
|
<ClInclude Include="Common\ENetUtil.h" />
|
||||||
<ClInclude Include="Common\EnumFormatter.h" />
|
<ClInclude Include="Common\EnumFormatter.h" />
|
||||||
|
<ClInclude Include="Common\EnumMap.h" />
|
||||||
<ClInclude Include="Common\Event.h" />
|
<ClInclude Include="Common\Event.h" />
|
||||||
<ClInclude Include="Common\FileSearch.h" />
|
<ClInclude Include="Common\FileSearch.h" />
|
||||||
<ClInclude Include="Common\FileUtil.h" />
|
<ClInclude Include="Common\FileUtil.h" />
|
||||||
|
Loading…
Reference in New Issue
Block a user