// Copyright 2021 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #pragma once #include #include #include /* * Helper for using enums with fmt. * * Usage example: * * enum class Foo * { * A = 0, * B = 1, * C = 2, * }; * * template <> * struct fmt::formatter : EnumFormatter * { * formatter() : EnumFormatter({"A", "B", "C"}) {} * }; * * enum class Bar * { * D = 0, * E = 1, * F = 3, * }; * * template <> * struct fmt::formatter : EnumFormatter * { * // 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 (last_member) + 1, std::enable_if_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 auto format(const T& e, FormatContext& ctx) { const auto value = static_cast>(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>(value)); } } protected: // This is needed because std::array deduces incorrectly if nullptr is included in the list using array_type = std::array; constexpr explicit EnumFormatter(const array_type names) : m_names(std::move(names)) {} private: const array_type m_names; bool formatting_for_shader = false; };