#pragma once #include #include #include #include #include #include #include #include "Support/TypeTraits.h" namespace clice::refl { 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 reflectable_enum = true; using underlying_type = underlying; constexpr Enum() : m_Value(invalid()) {} /// 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 Kind> 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 enum value. constexpr auto kind() const { return static_cast(m_Value); } /// Get the name of the enum. constexpr std::string_view name() const { using E = typename Derived::Kind; return refl::enum_name(static_cast(m_Value)); } template ... Kinds> constexpr bool is_one_of(Kinds... kinds) const { return ((m_Value == underlying_value(kinds)) || ...); } constexpr explicit operator bool() const { 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 refl::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 reflectable_enum = true; using underlying_type = underlying; 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 ... Kinds> 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 += refl::enum_name(static_cast(i)); } return masks; } constexpr static auto& all() { return enum_table::table; } constexpr explicit operator bool() const { return m_Value != 0; } constexpr friend bool operator==(Enum lhs, Enum rhs) = default; template Kind> constexpr Enum operator|(Kind kind) const { return Enum(m_Value | (1 << underlying_value(kind))); } template Kind> constexpr Enum operator&(Kind kind) const { return Enum(m_Value & (1 << underlying_value(kind))); } constexpr Enum operator&(Enum e) const { return Enum(m_Value & e.value()); } template Kind> constexpr Enum& operator|=(Kind kind) { m_Value |= (1 << underlying_value(kind)); return *this; } template Kind> constexpr Enum& operator&=(Kind kind) { m_Value &= (1 << underlying_value(kind)); return *this; } template ... Kinds> constexpr bool is_one_of(Kinds... kinds) const { return (((*this) & (kinds)) || ...); } 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 refl::enum_max(); } } private: underlying m_Value = 0; }; template requires (!integral) class Enum { public: /// Tag to indicate this is a special enum. constexpr inline static bool reflectable_enum = true; using underlying_type = underlying; constexpr Enum(underlying value) { static_assert( requires { Derived::All; }, "Derived enum must define all possible enum values."); for(auto& element: Derived::All) { if(element == value) { m_Value = element; } } assert(!m_Value.empty() && "Invalid enum value."); } constexpr Enum(const Enum&) = default; constexpr friend bool operator==(Enum lhs, Enum rhs) = default; constexpr underlying value() const { return m_Value; } private: underlying m_Value; }; template concept reflectable_enum = requires { T::reflectable_enum; requires T::reflectable_enum; }; } // namespace clice::refl