mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-22 13:49:53 -06:00
Create EnumFormatter
This commit is contained in:
@ -42,6 +42,7 @@ add_library(common
|
|||||||
DynamicLibrary.h
|
DynamicLibrary.h
|
||||||
ENetUtil.cpp
|
ENetUtil.cpp
|
||||||
ENetUtil.h
|
ENetUtil.h
|
||||||
|
EnumFormatter.h
|
||||||
Event.h
|
Event.h
|
||||||
FileSearch.cpp
|
FileSearch.cpp
|
||||||
FileSearch.h
|
FileSearch.h
|
||||||
|
91
Source/Core/Common/EnumFormatter.h
Normal file
91
Source/Core/Common/EnumFormatter.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2021 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper for using enums with fmt.
|
||||||
|
*
|
||||||
|
* Usage example:
|
||||||
|
*
|
||||||
|
* enum class Foo
|
||||||
|
* {
|
||||||
|
* A = 0,
|
||||||
|
* B = 1,
|
||||||
|
* C = 2,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* template <>
|
||||||
|
* struct fmt::formatter<Foo> : EnumFormatter<Foo::C>
|
||||||
|
* {
|
||||||
|
* formatter() : EnumFormatter({"A", "B", "C"}) {}
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* enum class Bar
|
||||||
|
* {
|
||||||
|
* D = 0,
|
||||||
|
* E = 1,
|
||||||
|
* F = 3,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* template <>
|
||||||
|
* struct fmt::formatter<Bar> : EnumFormatter<Bar::F>
|
||||||
|
* {
|
||||||
|
* // using std::array here fails due to nullptr not being const char*, at least in MSVC
|
||||||
|
* // (but only when a field is used; directly in the constructor is OK)
|
||||||
|
* static constexpr array_type names = {"D", "E", nullptr, "F"};
|
||||||
|
* 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>
|
||||||
|
class EnumFormatter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr auto parse(fmt::format_parse_context& ctx)
|
||||||
|
{
|
||||||
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
|
// 'u' for user display, 's' for shader generation
|
||||||
|
if (it != end && (*it == 'u' || *it == 's'))
|
||||||
|
formatting_for_shader = (*it++ == 's');
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T& e, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
const auto value = static_cast<std::underlying_type_t<T>>(e);
|
||||||
|
|
||||||
|
if (!formatting_for_shader)
|
||||||
|
{
|
||||||
|
if (value >= 0 && value < size && m_names[value] != nullptr)
|
||||||
|
return fmt::format_to(ctx.out(), "{} ({})", m_names[value], value);
|
||||||
|
else
|
||||||
|
return fmt::format_to(ctx.out(), "Invalid ({})", value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (value >= 0 && value < size && m_names[value] != nullptr)
|
||||||
|
return fmt::format_to(ctx.out(), "{:#x}u /* {} */", value, m_names[value]);
|
||||||
|
else
|
||||||
|
return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */",
|
||||||
|
static_cast<std::make_unsigned_t<T>>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// This is needed because std::array deduces incorrectly if nullptr is included in the list
|
||||||
|
using array_type = std::array<const char*, size>;
|
||||||
|
|
||||||
|
constexpr explicit EnumFormatter(const array_type names) : m_names(std::move(names)) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const array_type m_names;
|
||||||
|
bool formatting_for_shader = false;
|
||||||
|
};
|
@ -45,6 +45,7 @@
|
|||||||
<ClInclude Include="Common\DebugInterface.h" />
|
<ClInclude Include="Common\DebugInterface.h" />
|
||||||
<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\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" />
|
||||||
|
@ -5,6 +5,7 @@ add_dolphin_test(BlockingLoopTest BlockingLoopTest.cpp)
|
|||||||
add_dolphin_test(BusyLoopTest BusyLoopTest.cpp)
|
add_dolphin_test(BusyLoopTest BusyLoopTest.cpp)
|
||||||
add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)
|
add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)
|
||||||
add_dolphin_test(CryptoEcTest Crypto/EcTest.cpp)
|
add_dolphin_test(CryptoEcTest Crypto/EcTest.cpp)
|
||||||
|
add_dolphin_test(EnumFormatterTest EnumFormatterTest.cpp)
|
||||||
add_dolphin_test(EventTest EventTest.cpp)
|
add_dolphin_test(EventTest EventTest.cpp)
|
||||||
add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp)
|
add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp)
|
||||||
add_dolphin_test(FlagTest FlagTest.cpp)
|
add_dolphin_test(FlagTest FlagTest.cpp)
|
||||||
|
67
Source/UnitTests/Common/EnumFormatterTest.cpp
Normal file
67
Source/UnitTests/Common/EnumFormatterTest.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2021 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EnumFormatter.h"
|
||||||
|
|
||||||
|
enum class Enum1 : u32
|
||||||
|
{
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
C = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Enum1> : EnumFormatter<Enum1::C>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"A", "B", "C"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Enum2 : s32
|
||||||
|
{
|
||||||
|
D = 0,
|
||||||
|
E = 1,
|
||||||
|
F = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Enum2> : EnumFormatter<Enum2::F>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {"D", "E", nullptr, "F"};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(EnumUtil, Enum1)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum1::A), "A (0)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum1::B), "B (1)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum1::C), "C (2)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum1>(3)), "Invalid (3)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum1>(4)), "Invalid (4)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum1::A), "0x0u /* A */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum1::B), "0x1u /* B */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum1::C), "0x2u /* C */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(3)), "0x3u /* Invalid */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(4)), "0x4u /* Invalid */");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EnumUtil, Enum2)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum2::D), "D (0)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum2::E), "E (1)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum2>(2)), "Invalid (2)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum2::F), "F (3)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum2>(4)), "Invalid (4)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum2>(-1)), "Invalid (-1)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum2::D), "0x0u /* D */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum2::E), "0x1u /* E */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(2)), "0x2u /* Invalid */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum2::F), "0x3u /* F */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(4)), "0x4u /* Invalid */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(-1)), "0xffffffffu /* Invalid */");
|
||||||
|
}
|
@ -49,6 +49,7 @@
|
|||||||
<ClCompile Include="Common\BusyLoopTest.cpp" />
|
<ClCompile Include="Common\BusyLoopTest.cpp" />
|
||||||
<ClCompile Include="Common\CommonFuncsTest.cpp" />
|
<ClCompile Include="Common\CommonFuncsTest.cpp" />
|
||||||
<ClCompile Include="Common\Crypto\EcTest.cpp" />
|
<ClCompile Include="Common\Crypto\EcTest.cpp" />
|
||||||
|
<ClCompile Include="Common\EnumFormatterTest.cpp" />
|
||||||
<ClCompile Include="Common\EventTest.cpp" />
|
<ClCompile Include="Common\EventTest.cpp" />
|
||||||
<ClCompile Include="Common\FixedSizeQueueTest.cpp" />
|
<ClCompile Include="Common\FixedSizeQueueTest.cpp" />
|
||||||
<ClCompile Include="Common\FlagTest.cpp" />
|
<ClCompile Include="Common\FlagTest.cpp" />
|
||||||
|
Reference in New Issue
Block a user