#pragma once #include #include #include #include #include #include #include "TypeTraits.h" #include "JSON.h" #include "Format.h" namespace clice::support { template requires std::is_enum_v constexpr auto underlying_value(T value) { return static_cast>(value); } template requires std::is_enum_v consteval auto enum_name() { std::string_view name = std::source_location::current().function_name(); #if __GNUC__ || __clang__ std::size_t start = name.find('=') + 2; std::size_t end = name.size() - 1; #elif _MSC_VER std::size_t start = name.find('<') + 1; std::size_t end = name.rfind(">("); #else static_assert(false, "Not supported compiler"); #endif name = name.substr(start, end - start); start = name.rfind("::"); return start == std::string_view::npos ? name : name.substr(start + 2); } template consteval auto enum_max() { constexpr auto value = std::bit_cast(static_cast>(N)); if constexpr(enum_name().find(")") == std::string_view::npos) return enum_max(); else return N; } template struct enum_table { constexpr static std::array table = [](std::index_sequence) { return std::array{enum_name(Is)>()...}; }(std::make_index_sequence{}); }; template ()> constexpr std::string_view enum_name(E value) { return enum_table::table[static_cast>(value) - begin]; } /// A helper class to define enum. template class Enum { public: /// Tag to indicate this is a special enum. constexpr inline static bool is_special_enum_v = true; /// A integral must explicitly convert to the enum. explicit constexpr Enum(underlying value) : m_Value(value) {} /// Allow the enum to be constructed from the enum value. template requires std::is_same_v constexpr Enum(Kind kind) : m_Value(kind) { static_assert(sizeof(underlying) >= sizeof(typename Derived::Kind), "Underlying type is too small to hold all enum values."); } constexpr Enum(const Enum&) = default; constexpr Enum& operator= (const Enum&) = default; /// Get the underlying value of the enum. constexpr underlying value() const { return m_Value; } /// Get the name of the enum. constexpr std::string_view name() const { using E = typename Derived::Kind; return support::enum_name(static_cast(m_Value)); } constexpr explicit operator bool () { return m_Value != invalid(); } constexpr friend bool operator== (Enum lhs, Enum rhs) = default; constexpr static auto& all() { return enum_table::table; } private: consteval static underlying begin() { if constexpr(requires { Derived::FirstEnum; }) { return Derived::FirstEnum; } else { return 0; } } consteval static underlying end() { if constexpr(requires { Derived::LastEnum; }) { return Derived::LastEnum; } else { return support::enum_max(); } } consteval static underlying invalid() { if constexpr(requires { Derived::InvalidEnum; }) { return Derived::InvalidEnum; } else { static_assert(dependent_false, "Invalid enum value is not defined."); } } private: underlying m_Value; }; template class Enum { public: /// Tag to indicate this is a special enum. constexpr inline static bool is_special_enum_v = true; /// FIXME: Enum() = default; /// A integral must explicitly convert to the enum. explicit constexpr Enum(underlying value) : m_Value(value) {} /// Allow the enum to be constructed from the enum value. template requires (std::is_same_v && ...) constexpr Enum(Kinds... kind) : m_Value(((1 << underlying_value(kind)) | ...)) { static_assert(sizeof(underlying) * 8 >= end(), "Underlying type is too small to hold all enum values."); } constexpr Enum(const Enum&) = default; constexpr Enum& operator= (const Enum&) = default; /// Get the underlying value of the enum. constexpr underlying value() const { return m_Value; } /// Get the name of the enum. constexpr std::string name() const { std::string masks; bool isFirst = true; for(std::size_t i = 0; i < sizeof(underlying) * 8; i++) { bool hasBit = m_Value & (1 << i); if(!hasBit) { continue; } if(isFirst) { isFirst = false; } else { masks += " | "; } using E = typename Derived::Kind; masks += support::enum_name(static_cast(i)); } return masks; } constexpr static auto& all() { return enum_table::table; } constexpr explicit operator bool () { return m_Value != 0; } constexpr friend bool operator== (Enum lhs, Enum rhs) = default; template requires std::is_same_v constexpr Enum operator| (Kind kind) const { return Enum(m_Value | (1 << underlying_value(kind))); } template requires std::is_same_v constexpr Enum operator& (Kind kind) const { return Enum(m_Value & (1 << underlying_value(kind))); } template requires std::is_same_v constexpr Enum& operator|= (Kind kind) { m_Value |= (1 << underlying_value(kind)); return *this; } template requires std::is_same_v constexpr Enum& operator&= (Kind kind) { m_Value &= (1 << underlying_value(kind)); return *this; } private: consteval static std::size_t begin() { if constexpr(requires { Derived::FirstEnum; }) { return Derived::FirstEnum; } else { return 0; } } consteval static std::size_t end() { if constexpr(requires { Derived::LastEnum; }) { return Derived::LastEnum; } else { return support::enum_max(); } } private: underlying m_Value; }; template concept special_enum = requires { T::is_special_enum_v; requires T::is_special_enum_v; }; } // namespace clice::support namespace clice::json { template requires support::special_enum struct Serde { static json::Value serialize(const E& e) { return json::Value(e.value()); } static E deserialize(const json::Value& value) { assert(value.kind() == json::Value::Number && "Expect a number"); return E(value.getAsNumber().value()); } }; } // namespace clice::json template requires clice::support::special_enum struct std::formatter : std::formatter { using Base = std::formatter; template constexpr auto parse(ParseContext& ctx) { return Base::parse(ctx); } template auto format(const E& e, FormatContext& ctx) const { return Base::format(e.name(), ctx); } };