#pragma once #include #include #include #include #include /// EnumDetailsBase // Shared helpers struct EnumDetailsBase { template constexpr static auto strArr(auto input, auto start, auto end) { std::array out; input.copy(out.begin(), end - start, start); return out; } }; /// EnumTypeDetails // Extracts the fully qualified name of the enumeration template struct EnumTypeDetails : EnumDetailsBase { #ifndef ENUM_PROBE protected: #endif constexpr static std::string_view SEARCH_TYPE {"E = "}; constexpr static auto typeraw() { return std::string_view {__PRETTY_FUNCTION__}; }; constexpr static auto typeNameStart {typeraw().find(SEARCH_TYPE) + SEARCH_TYPE.length()}; constexpr static auto typeNameEnd {typeraw().find_first_of("];", typeNameStart)}; constexpr static auto typeNameLen {typeNameEnd - typeNameStart}; constexpr static auto typeNameArr {strArr(typeraw(), typeNameStart, typeNameEnd)}; public: constexpr static std::string_view typeName {typeNameArr.data(), typeNameArr.size()}; }; /// EnumValueDetails // Extracts the value name and constructs string_views of the parts template struct EnumValueDetails : public EnumTypeDetails { #ifndef ENUM_PROBE private: #endif using T = EnumTypeDetails; constexpr static auto raw() { return std::string_view {__PRETTY_FUNCTION__}; }; constexpr static auto nameStart {raw().find_last_of(": ") + 1}; constexpr static auto nameEnd {raw().find_first_of("];", nameStart)}; constexpr static auto nameLen {nameEnd - nameStart}; constexpr static auto nameArr {EnumValueDetails::template strArr(raw(), nameStart, nameEnd)}; public: constexpr static std::string_view valueName {nameArr.data(), nameArr.size()}; constexpr static auto valid {valueName.back() < '0' || valueName.back() > '9'}; #pragma GCC diagnostic push #ifdef __clang__ # pragma GCC diagnostic ignored "-Wenum-constexpr-conversion" #endif constexpr static auto raw_value {value}; #pragma GCC diagnostic pop }; /// EnumValueCollection // Customisation point for specifying the range of underlying values your enum can have template struct EnumValueCollection { using Vs = std::make_integer_sequence; }; /// EnumDetails // Interface for lookups/checks/etc at runtime template struct EnumDetails { #ifndef ENUM_PROBE private: #endif template constexpr static auto get_valids(std::integer_sequence) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #ifdef __clang__ # pragma GCC diagnostic ignored "-Wenum-constexpr-conversion" #endif return std::array {EnumValueDetails(n)>::valid...}; #pragma GCC diagnostic pop } template constexpr static auto get_values(std::integer_sequence) { #pragma GCC diagnostic push #ifdef __clang__ # pragma GCC diagnostic ignored "-Wenum-constexpr-conversion" #endif return std::array {EnumValueDetails(n)>::raw_value...}; #pragma GCC diagnostic pop } template constexpr static auto get_valueNames(std::integer_sequence) { return std::array {EnumValueDetails::valueName...}; } using EVC = EnumValueCollection; constexpr static auto valid_flags {get_valids(typename EVC::Vs {})}; constexpr static auto valid_count {std::count_if(valid_flags.begin(), valid_flags.end(), std::identity {})}; constexpr static auto lookup(const auto key, const auto & search, const auto & out) -> std::optional::value_type> { if (const auto itr = std::find(search.begin(), search.end(), key); itr != search.end()) { return out[static_cast(std::distance(search.begin(), itr))]; } return std::nullopt; } public: constexpr static auto values {[]() { constexpr auto values {get_values(typename EVC::Vs {})}; static_assert(std::is_sorted(values.begin(), values.end()), "Candidate values must be sorted"); std::array out; std::copy_if(values.begin(), values.end(), out.begin(), [valid = valid_flags.begin()](auto) mutable { return *valid++; }); return out; }()}; constexpr static auto names {get_valueNames(std::make_integer_sequence {})}; constexpr static bool is_valid(E value) noexcept { return std::binary_search(values.begin(), values.end(), value); } constexpr static std::optional parse(std::string_view name) noexcept { return lookup(name, names, values); } constexpr static std::optional to_string(E value) noexcept { return lookup(value, values, names); } };