From 370a1d523684532eb743ec78213fd323f4b011f3 Mon Sep 17 00:00:00 2001 From: neargye Date: Thu, 26 Sep 2019 18:53:11 +0500 Subject: [PATCH] improvement * SFINAE-friendly * overhead reduction * add enum_index * add enum_traits * less bin size --- include/magic_enum.hpp | 475 +++++++++++++++++++++++------------------ 1 file changed, 271 insertions(+), 204 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index b202722..c5505a9 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -33,7 +33,6 @@ #define NEARGYE_MAGIC_ENUM_HPP #include -#include #include #include #include @@ -62,23 +61,18 @@ namespace magic_enum { template struct enum_range final { static_assert(std::is_enum_v, "magic_enum::enum_range requires enum type."); - static constexpr int min = MAGIC_ENUM_RANGE_MIN; - static constexpr int max = MAGIC_ENUM_RANGE_MAX; + inline static constexpr int min = MAGIC_ENUM_RANGE_MIN; + inline static constexpr int max = MAGIC_ENUM_RANGE_MAX; static_assert(max > min, "magic_enum::enum_range requires max > min."); }; -static_assert(MAGIC_ENUM_RANGE_MIN <= 0, - "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); -static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), - "MAGIC_ENUM_RANGE_MIN must be greater than INT_MIN."); +static_assert(MAGIC_ENUM_RANGE_MIN <= 0, "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); +static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "MAGIC_ENUM_RANGE_MIN must be greater than INT16_MIN."); -static_assert(MAGIC_ENUM_RANGE_MAX > 0, - "MAGIC_ENUM_RANGE_MAX must be greater than 0."); -static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), - "MAGIC_ENUM_RANGE_MAX must be less than INT_MAX."); +static_assert(MAGIC_ENUM_RANGE_MAX > 0, "MAGIC_ENUM_RANGE_MAX must be greater than 0."); +static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE_MAX must be less than INT16_MAX."); -static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, - "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); namespace detail { @@ -90,9 +84,12 @@ struct supported final : std::false_type {}; #endif +template +inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; + template struct static_string final { - constexpr static_string(std::string_view str) noexcept : static_string(str, std::make_index_sequence{}) {} + constexpr static_string(std::string_view str) noexcept : static_string{str, std::make_index_sequence{}} {} constexpr const char* data() const noexcept { return chars.data(); } @@ -118,30 +115,7 @@ struct static_string<0> final { constexpr operator std::string_view() const noexcept { return {}; } }; -template -inline constexpr int min_v = static_cast(enum_range::min > (std::numeric_limits>::min)() - ? enum_range::min - : (std::numeric_limits>::min)()); - -template -inline constexpr int max_v = static_cast(enum_range::max < (std::numeric_limits>::max)() - ? enum_range::max - : (std::numeric_limits>::max)()); - -template -[[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."); - - return std::make_integer_sequence - min_v + 1>{}; -} - -template -inline constexpr auto range_v = range(); - -[[nodiscard]] constexpr std::string_view pretty_name(std::string_view name) noexcept { +constexpr std::string_view pretty_name(std::string_view name) noexcept { 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') || @@ -161,57 +135,132 @@ inline constexpr auto range_v = range(); return {}; // Invalid name. } +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); +#if defined(__clang__) + constexpr std::string_view name{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36}; +#elif defined(__GNUC__) && __GNUC__ >= 9 + constexpr std::string_view name{__PRETTY_FUNCTION__ + 49, sizeof(__PRETTY_FUNCTION__) - 51}; +#elif defined(_MSC_VER) + constexpr std::string_view name{__FUNCSIG__ + 40, sizeof(__FUNCSIG__) - 57}; +#endif + + if constexpr (supported::value) { + return static_string{name}; + } else { + static_assert(supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); + return std::string_view{}; // Unsupported compiler. + } +} + +template +inline constexpr auto type_name_v = n(); + template -[[nodiscard]] constexpr auto n() noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::n requires enum type."); +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); #if defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 9 constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); #elif defined(_MSC_VER) constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); -#else - return std::string_view{}; // Unsupported compiler. #endif -#if defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) - return static_string{name}; -#endif + if constexpr (supported::value) { + return static_string{name}; + } else { + static_assert(supported::value, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); + return std::string_view{}; // Unsupported compiler. + } } template inline constexpr auto name_v = n(); -template -[[nodiscard]] constexpr auto strings(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::strings requires enum type."); +template +inline constexpr int reflected_min_v = static_cast(enum_range::min > (std::numeric_limits>::min)() + ? enum_range::min + : (std::numeric_limits>::min)()); - return std::array{{name_v(I + min_v)>...}}; +template +inline constexpr int reflected_max_v = static_cast(enum_range::max < (std::numeric_limits>::max)() + ? enum_range::max + : (std::numeric_limits>::max)()); + +template +constexpr std::size_t reflected_size() { + static_assert(is_enum_v, "magic_enum::detail::range_size requires enum type."); + static_assert(reflected_min_v > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); + static_assert(reflected_max_v < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); + static_assert(reflected_max_v > reflected_min_v, "magic_enum::enum_range requires max > min."); + constexpr auto size = reflected_max_v - reflected_min_v + 1; + static_assert(size > 0, "magic_enum::enum_range requires valid size."); + + return static_cast(size); +} + +template +constexpr int range_min(std::integer_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::range_min requires enum type."); + + int r = 0; + (void)(((n(I + reflected_min_v)>().size() != 0) ? (r = I + reflected_min_v, false) : true) && ...); + return r; +} + +template +constexpr int range_max(std::integer_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::range_max requires enum type."); + + int r = 0; + (void)(((n(reflected_max_v - I)>().size() != 0) ? (r = reflected_max_v - I, false) : true) && ...); + return r; } template -[[nodiscard]] constexpr std::string_view name(E value) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::name requires enum type."); - using U = std::underlying_type_t; - constexpr auto names = strings(range_v); +inline constexpr int min_v = range_min(std::make_integer_sequence()>{}); - if (static_cast(value) > static_cast(max_v) || static_cast(value) < static_cast(min_v)) { - return {}; // Value out of range. - } +template +inline constexpr int max_v = range_max(std::make_integer_sequence()>{}); - if (auto i = static_cast(static_cast(value) - min_v); i < names.size()) { - return names[i]; - } +template +constexpr std::size_t range_size() noexcept { + static_assert(is_enum_v, "magic_enum::detail::range_size requires enum type."); + constexpr auto size = max_v - min_v + 1; + static_assert(size > 0, "magic_enum::enum_range requires valid size."); - return {}; // Value out of range. + return static_cast(size); } -template -[[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) + ...); +template +inline constexpr std::size_t size_v = range_size(); - std::array values{}; - for (std::size_t i = 0, v = 0; i < valid.size() && v < num_valid; ++i) { +template +inline constexpr auto range_v = std::make_integer_sequence>{}; + +template +constexpr std::size_t count(std::integer_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::count requires enum type."); + + return (((n(I + min_v)>().size() != 0) ? 1 : 0) + ...); +} + +template +inline constexpr std::size_t count_v = count(range_v); + +template +inline constexpr auto sequence_v = std::make_index_sequence>{}; + +template +inline constexpr bool sparsity_v = count_v != size_v; + +template +constexpr auto values(std::integer_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); + constexpr std::array> valid{{(n(I + min_v)>().size() != 0)...}}; + + std::array> values{}; + for (std::size_t i = 0, v = 0; v < count_v; ++i) { if (valid[i]) { values[v++] = static_cast(static_cast(i) + min_v); } @@ -221,39 +270,37 @@ template } template -inline constexpr auto values_v = values(range_v); +using index_t = std::conditional_t < (std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; template -inline constexpr std::size_t count_v = values_v.size(); +inline constexpr auto invalid_index_v = (std::numeric_limits>::max)(); -template -[[nodiscard]] constexpr auto names(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::names requires enum type."); +template +constexpr auto indexes(std::integer_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::indexes requires enum type."); + index_t i = 0; - return std::array{{name_v[I]>...}}; + return std::array, size_v>{{((n(I + min_v)>().size() != 0) ? i++ : invalid_index_v)...}}; } -template -inline constexpr auto names_v = names(std::make_index_sequence>{}); - template -[[nodiscard]] constexpr auto entries(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "magic_enum::detail::entries requires enum type."); +constexpr auto names(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::names requires enum type."); + constexpr auto vals = values(range_v); - return std::array, sizeof...(I)>{{{values_v[I], names_v[I]}...}}; + return std::array>{{name_v...}}; } -template -inline constexpr auto entries_v = entries(std::make_index_sequence>{}); +template +constexpr auto entries(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::entries requires enum type."); + constexpr auto vals = values(range_v); -template -using remove_cvref_t = std::remove_cv_t>; + return std::array, count_v>{{{vals[I], name_v}...}}; +} -template -using enable_if_enum_t = std::enable_if_t>, remove_cvref_t>; - -template -inline constexpr bool check_enum_v = std::is_same_v, D> && std::is_enum_v; +template +using enable_if_enum_t = std::enable_if_t, R>; template > struct is_scoped_enum : std::false_type {}; @@ -316,19 +363,79 @@ struct underlying_type : detail::underlying_type {}; template using underlying_type_t = typename underlying_type::type; +template +struct enum_traits {}; + +template +struct enum_traits>> { + using type = E; + using underlying_type = std::underlying_type_t; + + inline static constexpr std::string_view type_name = detail::type_name_v; + + inline static constexpr bool is_unscoped_enum = is_unscoped_enum_v; + inline static constexpr bool is_scoped_enum = is_scoped_enum_v; + inline static constexpr bool is_fixed_enum = is_fixed_enum_v; + + inline static constexpr std::size_t count = detail::count_v; + inline static constexpr std::array values = detail::values(detail::range_v); + inline static constexpr std::array names = detail::names(detail::sequence_v); + + [[nodiscard]] static constexpr bool reflected(E value) noexcept { + return static_cast(value) >= static_cast(detail::reflected_min_v) && static_cast(value) <= static_cast(detail::reflected_max_v); + } + + [[nodiscard]] static constexpr int index(E value) noexcept { + if (static_cast(value) >= static_cast(detail::min_v) && static_cast(value) <= static_cast(detail::max_v)) { + if constexpr (detail::sparsity_v) { + if (auto i = indexes[static_cast(value) - detail::min_v]; i != detail::invalid_index_v) { + return i; + } + } else { + return static_cast(value) - detail::min_v; + } + } + + return -1; // Value out of range. + } + + [[nodiscard]] static constexpr E value(std::size_t index) noexcept { + return values[index]; + } + + [[nodiscard]] static constexpr std::string_view name(E value) noexcept { + if (auto i = index(value); i != -1) { + return names[i]; + } + + return {}; // Value out of range. + } + + private: + static_assert(enum_range::min > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); + static_assert(enum_range::max < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); + static_assert(enum_range::max > enum_range::min, "magic_enum::enum_range requires max > min."); + using U = underlying_type; + inline static constexpr auto indexes = detail::indexes(detail::range_v); +}; + // Obtains enum value from enum string name. // Returns std::optional with enum value. -template > -[[nodiscard]] constexpr std::optional enum_cast(std::string_view value) noexcept { - static_assert(detail::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; - constexpr auto names = detail::names_v; +template +[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t>> { + using D = std::decay_t; - for (std::size_t i = 0; i < count; ++i) { - if (names[i] == value) { - return values[i]; + if constexpr (detail::size_v > detail::count_v * 2) { + for (std::size_t i = 0; i < enum_traits::count; ++i) { + if (value == enum_traits::names[i]) { + return enum_traits::values[i]; + } + } + } else { + for (auto i = detail::min_v; i <= detail::max_v; ++i) { + if (value == enum_traits::name(static_cast(i))) { + return static_cast(i); + } } } @@ -337,123 +444,101 @@ template > // Obtains enum value from integer value. // Returns std::optional with enum value. -template > -[[nodiscard]] constexpr std::optional enum_cast(std::underlying_type_t value) noexcept { - static_assert(detail::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."); +template +[[nodiscard]] constexpr auto enum_cast(std::underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { + using D = std::decay_t; - if (detail::name(static_cast(value)).empty()) { - return std::nullopt; // Invalid value or out of range. + if (enum_traits::index(static_cast(value)) != -1) { + return static_cast(value); } - return static_cast(value); + return std::nullopt; // Invalid value or out of range. } // Returns integer value from enum value. -template > -[[nodiscard]] constexpr std::underlying_type_t enum_integer(E value) noexcept { - static_assert(detail::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."); +template +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { + return static_cast>(value); +} - return static_cast>(value); +// Obtains index in enum value sequence from enum value. +// Returns std::optional with index. +template +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { + if (auto i = enum_traits>::index(value); i != -1) { + return i; + } + + return std::nullopt; // Value out of range. } // Returns enum value at specified index. // 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::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; - - return assert(index < values.size()), values[index]; +template +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { + return enum_traits>::value(index); } // Obtains value enum sequence. // Returns std::array with enum values, sorted by enum value. -template > -[[nodiscard]] constexpr auto enum_values() noexcept { - static_assert(detail::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; - - return values; +template +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t>::values)&> { + return enum_traits>::values; } // Returns number of enum values. -template > -[[nodiscard]] constexpr std::size_t enum_count() noexcept { - static_assert(detail::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; - - return count; +template +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t { + return enum_traits>::count; } // Returns string enum name from static storage enum variable. // 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::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_v; +template +[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_enum_t { + return detail::name_v, V>; } // Returns string enum name from enum value. -template > -[[nodiscard]] constexpr std::string_view enum_name(E value) noexcept { - static_assert(detail::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(value); +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { + return enum_traits>::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::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; - - return names; +template +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t>::names)&> { + return enum_traits>::names; } // Obtains pair (value enum, string enum name) sequence. // 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::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; +template +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t, std::string_view>, enum_count()>> { + using D = std::decay_t; - return entries; + return detail::entries(detail::sequence_v); } namespace ostream_operators { -template > -std::basic_ostream& operator<<(std::basic_ostream& os, E value) { - static_assert(detail::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(value); !name.empty()) { +template +auto operator<<(std::basic_ostream& os, E value) -> detail::enable_if_enum_t&> { + if (auto name = enum_traits>::name(value); !name.empty()) { for (auto c : name) { os.put(c); } } else { - os << static_cast>(value); + os << static_cast>(value); } return os; } -template > -std::basic_ostream& operator<<(std::basic_ostream& os, std::optional value) { - static_assert(detail::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."); - +template +auto operator<<(std::basic_ostream& os, std::optional value) -> detail::enable_if_enum_t&> { if (value.has_value()) { os << value.value(); } @@ -465,56 +550,38 @@ std::basic_ostream& operator<<(std::basic_ostream& o namespace bitwise_operators { -template > -constexpr E operator~(E rhs) noexcept { - static_assert(detail::check_enum_v, "magic_enum::bitwise_operators::operator~ requires enum type."); - using U = std::underlying_type_t; - - return static_cast(~static_cast(rhs)); +template +constexpr auto operator~(E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(~static_cast>(rhs)); } -template > -constexpr E operator|(E lhs, E rhs) noexcept { - static_assert(detail::check_enum_v, "magic_enum::bitwise_operators::operator| requires enum type."); - using U = std::underlying_type_t; - - return static_cast(static_cast(lhs) | static_cast(rhs)); +template +constexpr auto operator|(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); } -template > -constexpr E operator&(E lhs, E rhs) noexcept { - static_assert(detail::check_enum_v, "magic_enum::bitwise_operators::operator& requires enum type."); - using U = std::underlying_type_t; - - return static_cast(static_cast(lhs) & static_cast(rhs)); +template +constexpr auto operator&(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); } -template > -constexpr E operator^(E lhs, E rhs) noexcept { - static_assert(detail::check_enum_v, "magic_enum::bitwise_operators::operator^ requires enum type."); - using U = std::underlying_type_t; - - return static_cast(static_cast(lhs) ^ static_cast(rhs)); +template +constexpr auto operator^(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } -template > -constexpr E& operator|=(E& lhs, E rhs) noexcept { - static_assert(detail::check_enum_v, "magic_enum::bitwise_operators::operator|= requires enum type."); - +template +constexpr auto operator|=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { return lhs = lhs | rhs; } -template > -constexpr E& operator&=(E& lhs, E rhs) noexcept { - static_assert(detail::check_enum_v, "magic_enum::bitwise_operators::operator%= requires enum type."); - +template +constexpr auto operator&=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { return lhs = lhs & rhs; } -template > -constexpr E& operator^=(E& lhs, E rhs) noexcept { - static_assert(detail::check_enum_v, "magic_enum::bitwise_operators::operator^= requires enum type."); - +template +constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { return lhs = lhs ^ rhs; }