diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 319e1c9..a8bf1c5 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -82,14 +82,50 @@ static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, namespace detail { +template +struct magic_enum_supported final +#if defined(__clang__) || defined(__GNUC__) && __GNUC__>= 9 || defined(_MSC_VER) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +struct static_string final { + constexpr static_string(std::string_view str) noexcept : static_string(str, std::make_index_sequence{}) {} + + constexpr const char* data() const noexcept { return chars.data(); } + + constexpr std::size_t size() const noexcept { return chars.size(); } + + constexpr operator std::string_view() const noexcept { return {chars.data(), chars.size()}; } + + private: + template + constexpr static_string(std::string_view str, std::index_sequence) noexcept : chars{{str[I]...}} {} + + const std::array chars; +}; + +template <> +struct static_string<0> final { + constexpr static_string(std::string_view) noexcept {} + + constexpr const char* data() const noexcept { return nullptr; } + + constexpr std::size_t size() const noexcept { return 0; } + + constexpr operator std::string_view() const noexcept { return {}; } +}; + template inline constexpr auto min_v = enum_range::min > (std::numeric_limits>::min)() ? enum_range::min : (std::numeric_limits>::min)(); template -[[nodiscard]] constexpr auto range_impl() { - static_assert(std::is_enum_v, "magic_enum::detail::range_impl requires enum type."); +[[nodiscard]] constexpr auto range() { + static_assert(std::is_enum_v, "magic_enum::detail::range requires enum type."); static_assert(enum_range::min > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT_MIN."); static_assert(enum_range::max < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT_MAX."); static_assert(enum_range::max > enum_range::min, "magic_enum::enum_range requires max > min."); @@ -100,10 +136,10 @@ template } template -inline constexpr auto range_v = range_impl(); +inline constexpr auto range_v = range(); [[nodiscard]] constexpr std::string_view pretty_name(std::string_view name) noexcept { - for (std::size_t i = name.length(); i > 0; --i) { + for (std::size_t i = name.size(); i > 0; --i) { if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || (name[i - 1] >= 'a' && name[i - 1] <= 'z') || (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || @@ -113,57 +149,57 @@ inline constexpr auto range_v = range_impl(); } } - if (name.length() > 0 && ((name.front() >= 'a' && name.front() <= 'z') || - (name.front() >= 'A' && name.front() <= 'Z') || - (name.front() == '_'))) { + if (name.size() > 0 && ((name.front() >= 'a' && name.front() <= 'z') || + (name.front() >= 'A' && name.front() <= 'Z') || + (name.front() == '_'))) { return name; } return {}; // Invalid name. } -template -struct always_false final : std::false_type {}; - template -[[nodiscard]] constexpr auto name_impl() noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::name_impl requires enum type."); +[[nodiscard]] constexpr auto n() noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::name requires enum type."); #if defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 9 - return pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); #elif defined(_MSC_VER) - return pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); #else - static_assert(always_false{}, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); return std::string_view{}; // Unsupported compiler. #endif + +#if defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) + return static_string{name}; +#endif } -template -[[nodiscard]] constexpr auto strings_impl(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::strings_impl requires enum type."); +template +inline constexpr auto name_v = n(); - return std::array{{name_impl(I + min_v)>()...}}; +template +[[nodiscard]] constexpr auto strings(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::strings requires enum type."); + + return std::array{{name_v(I + min_v)>...}}; } template -inline constexpr auto strings_v = strings_impl(range_v); +[[nodiscard]] constexpr std::string_view name(E value) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::name requires enum type."); + constexpr auto names = strings(range_v); -template -[[nodiscard]] constexpr std::string_view name_impl(E value) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::name_impl requires enum type."); - constexpr auto strings = strings_v; - - if (auto i = static_cast(static_cast(value) - min_v); i < strings.size()) { - return strings[i]; + if (auto i = static_cast(static_cast(value) - min_v); i < names.size()) { + return names[i]; } return {}; // Value out of range. } template -[[nodiscard]] constexpr auto values_impl(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::values_impl requires enum type."); - constexpr std::array valid{{!name_impl(I + min_v)>().empty()...}}; +[[nodiscard]] constexpr auto values(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::values requires enum type."); + constexpr std::array valid{{(name_v(I + min_v)>.size() != 0 )...}}; constexpr auto num_valid = ((valid[I] ? 1 : 0) + ...); std::array values{}; @@ -177,30 +213,30 @@ template } template -inline constexpr auto values_v = values_impl(range_v); +inline constexpr auto values_v = values(range_v); template inline constexpr auto count_v = values_v.size(); template -[[nodiscard]] constexpr auto names_impl(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::names_impl requires enum type."); +[[nodiscard]] constexpr auto names(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::names requires enum type."); - return std::array{{name_impl[I]>()...}}; + return std::array{{name_v[I]>...}}; } template -inline constexpr auto names_v = names_impl(std::make_index_sequence>{}); +inline constexpr auto names_v = names(std::make_index_sequence>{}); template -[[nodiscard]] constexpr auto entries_impl(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::entries_impl requires enum type."); +[[nodiscard]] constexpr auto entries(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::entries requires enum type."); return std::array, sizeof...(I)>{{{values_v[I], names_v[I]}...}}; } template -inline constexpr auto entries_v = entries_impl(std::make_index_sequence>{}); +inline constexpr auto entries_v = entries(std::make_index_sequence>{}); template using remove_cvref_t = std::remove_cv_t>; @@ -212,35 +248,38 @@ template inline constexpr bool check_enum_v = std::is_same_v, D> && std::is_enum_v; template > -struct is_scoped_enum_impl : std::false_type {}; +struct is_scoped_enum : std::false_type {}; template -struct is_scoped_enum_impl : std::bool_constant>> {}; +struct is_scoped_enum : std::bool_constant>> {}; template > -struct is_unscoped_enum_impl : std::false_type {}; +struct is_unscoped_enum : std::false_type {}; template -struct is_unscoped_enum_impl : std::bool_constant>> {}; +struct is_unscoped_enum : std::bool_constant>> {}; template -struct is_fixed_enum_impl : std::false_type {}; +struct is_fixed_enum : std::false_type {}; template -struct is_fixed_enum_impl : std::is_enum {}; +struct is_fixed_enum : std::is_enum {}; template > -struct underlying_type_impl {}; +struct underlying_type {}; template -struct underlying_type_impl : std::underlying_type {}; +struct underlying_type : std::underlying_type {}; } // namespace magic_enum::detail +// Checks is magic_enum supported compiler. +inline constexpr auto is_magic_enum_supported = detail::magic_enum_supported::value; + // Checks whether T is an Unscoped enumeration type. // Provides the member constant value which is equal to true, if T is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) type. Otherwise, value is equal to false. template -struct is_unscoped_enum : detail::is_unscoped_enum_impl {}; +struct is_unscoped_enum : detail::is_unscoped_enum {}; template inline constexpr bool is_unscoped_enum_v = is_unscoped_enum::value; @@ -248,7 +287,7 @@ inline constexpr bool is_unscoped_enum_v = is_unscoped_enum::value; // Checks whether T is an Scoped enumeration type. // Provides the member constant value which is equal to true, if T is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) type. Otherwise, value is equal to false. template -struct is_scoped_enum : detail::is_scoped_enum_impl {}; +struct is_scoped_enum : detail::is_scoped_enum {}; template inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; @@ -256,7 +295,7 @@ inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; // Checks whether T is an Fixed enumeration type. // Provides the member constant value which is equal to true, if T is an [Fixed enumeration](https://en.cppreference.com/w/cpp/language/enum) type. Otherwise, value is equal to false. template -struct is_fixed_enum : detail::is_fixed_enum_impl {}; +struct is_fixed_enum : detail::is_fixed_enum {}; template inline constexpr bool is_fixed_enum_v = is_fixed_enum::value; @@ -264,7 +303,7 @@ inline constexpr bool is_fixed_enum_v = is_fixed_enum::value; // If T is a complete enumeration type, provides a member typedef type that names the underlying type of T. // Otherwise, if T is not an enumeration type, there is no member type. Otherwise (T is an incomplete enumeration type), the program is ill-formed. template -struct underlying_type : detail::underlying_type_impl {}; +struct underlying_type : detail::underlying_type {}; template using underlying_type_t = typename underlying_type::type; @@ -273,6 +312,7 @@ using underlying_type_t = typename underlying_type::type; // Returns std::optional with enum value. template > [[nodiscard]] constexpr std::optional enum_cast(std::string_view value) noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_cast requires enum type."); constexpr auto values = detail::values_v; constexpr auto count = detail::count_v; @@ -291,9 +331,10 @@ template > // Returns std::optional with enum value. template > [[nodiscard]] constexpr std::optional enum_cast(std::underlying_type_t value) noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_cast requires enum type."); - if (detail::name_impl(static_cast(value)).empty()) { + if (detail::name(static_cast(value)).empty()) { return std::nullopt; // Invalid value or out of range. } @@ -303,6 +344,7 @@ template > // Returns integer value from enum value. template > [[nodiscard]] constexpr std::underlying_type_t enum_integer(E value) noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_integer requires enum type."); return static_cast>(value); @@ -312,6 +354,7 @@ template > // No bounds checking is performed: the behavior is undefined if index >= number of enum values. template> [[nodiscard]] constexpr D enum_value(std::size_t index) { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_value requires enum type."); constexpr auto values = detail::values_v; @@ -322,6 +365,7 @@ template> // Returns std::array with enum values, sorted by enum value. template > [[nodiscard]] constexpr auto enum_values() noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_values requires enum type."); constexpr auto values = detail::values_v; @@ -331,6 +375,7 @@ template > // Returns number of enum values. template > [[nodiscard]] constexpr std::size_t enum_count() noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_count requires enum type."); constexpr auto count = detail::count_v; @@ -341,23 +386,26 @@ template > // This version is much lighter on the compile times and is not restricted to the enum_range limitation. template > [[nodiscard]] constexpr std::string_view enum_name() noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_name requires enum type."); - return detail::name_impl(); + return detail::name_v; } // Returns string enum name from enum value. template > [[nodiscard]] constexpr std::string_view enum_name(E value) noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_name requires enum type."); - return detail::name_impl(value); + return detail::name(value); } // Obtains string enum name sequence. // Returns std::array with string enum names, sorted by enum value. template > [[nodiscard]] constexpr auto enum_names() noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_names requires enum type."); constexpr auto names = detail::names_v; @@ -368,6 +416,7 @@ template > // Returns std::array with std::pair (value enum, string enum name), sorted by enum value. template > [[nodiscard]] constexpr auto enum_entries() noexcept { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::enum_entries requires enum type."); constexpr auto entries = detail::entries_v; @@ -378,9 +427,10 @@ namespace ostream_operators { template > std::basic_ostream& operator<<(std::basic_ostream& os, E value) { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::ostream_operators::operator<< requires enum type."); - if (auto name = detail::name_impl(value); !name.empty()) { + if (auto name = detail::name(value); !name.empty()) { for (auto c : name) { os.put(c); } @@ -393,6 +443,7 @@ std::basic_ostream& operator<<(std::basic_ostream& o template > std::basic_ostream& operator<<(std::basic_ostream& os, std::optional value) { + static_assert(detail::magic_enum_supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(detail::check_enum_v, "magic_enum::ostream_operators::operator<< requires enum type."); if (value.has_value()) {