Files
clice/include/Support/Enum.h
2025-11-30 15:21:27 +08:00

302 lines
8.5 KiB
C++

#pragma once
#include <array>
#include <bit>
#include <cassert>
#include <cstdint>
#include <source_location>
#include <string>
#include <string_view>
#include "Support/TypeTraits.h"
namespace clice::refl {
template <typename T>
requires std::is_enum_v<T>
constexpr auto underlying_value(T value) {
return static_cast<std::underlying_type_t<T>>(value);
}
template <auto value>
requires std::is_enum_v<decltype(value)>
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 <typename E, std::size_t N = 0>
consteval auto enum_max() {
constexpr auto value = std::bit_cast<E>(static_cast<std::underlying_type_t<E>>(N));
if constexpr(enum_name<value>().find(")") == std::string_view::npos)
return enum_max<E, N + 1>();
else
return N;
}
template <typename E, std::size_t count>
struct enum_table {
constexpr static std::array<std::string_view, count> table =
[]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::array{enum_name<static_cast<E>(Is)>()...};
}(std::make_index_sequence<count>{});
};
template <typename E, std::size_t begin = 0, std::size_t end = enum_max<E, begin>()>
constexpr std::string_view enum_name(E value) {
return enum_table<E, end - begin>::table[static_cast<std::underlying_type_t<E>>(value) - begin];
}
/// A helper class to define enum.
template <typename Derived, bool is_bitmask = false, typename underlying = uint8_t>
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 <std::same_as<typename Derived::Kind> 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<typename Derived::Kind>(m_Value);
}
/// Get the name of the enum.
constexpr std::string_view name() const {
using E = typename Derived::Kind;
return refl::enum_name<E, begin(), end()>(static_cast<E>(m_Value));
}
template <std::same_as<typename Derived::Kind>... 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<typename Derived::Kind, end() - begin()>::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<typename Derived::Kind, begin()>();
}
}
consteval static underlying invalid() {
if constexpr(requires { Derived::InvalidEnum; }) {
return Derived::InvalidEnum;
} else {
static_assert(dependent_false<Derived>, "Invalid enum value is not defined.");
}
}
private:
underlying m_Value;
};
template <typename Derived, typename underlying>
class Enum<Derived, true, underlying> {
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 <std::same_as<typename Derived::Kind>... 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<E, begin(), end()>(static_cast<E>(i));
}
return masks;
}
constexpr static auto& all() {
return enum_table<typename Derived::Kind, end() - begin()>::table;
}
constexpr explicit operator bool() const {
return m_Value != 0;
}
constexpr friend bool operator==(Enum lhs, Enum rhs) = default;
template <std::same_as<typename Derived::Kind> Kind>
constexpr Enum operator|(Kind kind) const {
return Enum(m_Value | (1 << underlying_value(kind)));
}
template <std::same_as<typename Derived::Kind> 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 <std::same_as<typename Derived::Kind> Kind>
constexpr Enum& operator|=(Kind kind) {
m_Value |= (1 << underlying_value(kind));
return *this;
}
template <std::same_as<typename Derived::Kind> Kind>
constexpr Enum& operator&=(Kind kind) {
m_Value &= (1 << underlying_value(kind));
return *this;
}
template <std::same_as<typename Derived::Kind>... 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<typename Derived::Kind, begin()>();
}
}
private:
underlying m_Value = 0;
};
template <typename Derived, typename underlying>
requires (!integral<underlying>)
class Enum<Derived, false, underlying> {
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 <typename T>
concept reflectable_enum = requires {
T::reflectable_enum;
requires T::reflectable_enum;
};
} // namespace clice::refl