diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index edf73c1..7508fc7 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -301,7 +301,11 @@ constexpr auto entries(std::index_sequence) noexcept { } template -using enable_if_enum_t = std::enable_if_t, R>; +using enable_if_enum_t = std::enable_if_t>, R>; + +} // namespace magic_enum::detail + +namespace traits { template > struct is_scoped_enum : std::false_type {}; @@ -315,42 +319,13 @@ struct is_unscoped_enum : std::false_type {}; template struct is_unscoped_enum : std::bool_constant>> {}; -template > +template >> struct underlying_type {}; template -struct underlying_type : std::underlying_type {}; +struct underlying_type : std::underlying_type> {}; -} // namespace magic_enum::detail - -// Checks is magic_enum supported compiler. -inline constexpr bool is_magic_enum_supported = detail::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 {}; - -template -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 {}; - -template -inline constexpr bool is_scoped_enum_v = is_scoped_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 {}; - -template -using underlying_type_t = typename underlying_type::type; - -template +template struct enum_traits {}; template @@ -360,8 +335,8 @@ struct enum_traits>> { 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_unscoped_enum = traits::is_unscoped_enum::value; + inline static constexpr bool is_scoped_enum = traits::is_scoped_enum::value; inline static constexpr std::size_t count = detail::count_v; inline static constexpr std::array values = detail::values(detail::range_v); @@ -375,9 +350,9 @@ struct enum_traits>> { [[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::size_v != detail::count_v) { - if (auto i = indexes[static_cast(value) - detail::min_v]; i != detail::invalid_index_v) { - return i; - } + 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; } @@ -411,6 +386,39 @@ struct enum_traits>> { inline static constexpr auto indexes = detail::indexes(detail::range_v); }; +} // namespace magic_enum::traits + +// Checks is magic_enum supported compiler. +inline constexpr bool is_magic_enum_supported = detail::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 : traits::is_unscoped_enum {}; + +template +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 : traits::is_scoped_enum {}; + +template +inline constexpr bool is_scoped_enum_v = is_scoped_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 : traits::underlying_type {}; + +template +using underlying_type_t = typename underlying_type::type; + +// Enum traits defines a compile-time template-based interface to query the properties of enum. +template +using enum_traits = traits::enum_traits>; + // Obtains enum value from enum string name. // Returns std::optional with enum value. template @@ -437,7 +445,7 @@ template // Obtains enum value from integer value. // Returns std::optional with enum value. template -[[nodiscard]] constexpr auto enum_cast(std::underlying_type_t> value) noexcept -> detail::enable_if_enum_t, std::optional>> { +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { using D = std::decay_t; if (enum_traits::index(static_cast(value)) != -1) { @@ -449,15 +457,15 @@ template // Returns integer value from enum value. template -[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { - return static_cast>(value); +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { + 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) { + if (auto i = enum_traits::index(value); i != -1) { return i; } @@ -468,20 +476,20 @@ template // No bounds checking is performed: the behavior is undefined if index >= number of enum values. template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { - return enum_traits>::value(index); + 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 -> detail::enable_if_enum_t>::values)&> { - return enum_traits>::values; +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t::values)&> { + return enum_traits::values; } // Returns number of enum values. template [[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t { - return enum_traits>::count; + return enum_traits::count; } // Returns string enum name from static storage enum variable. @@ -495,33 +503,33 @@ template // 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); + 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 -> detail::enable_if_enum_t>::names)&> { - return enum_traits>::names; +[[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 -> detail::enable_if_enum_t>::entries)&> { - return enum_traits>::entries; +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t::entries)&> { + return enum_traits::entries; } namespace ostream_operators { template auto operator<<(std::basic_ostream& os, E value) -> detail::enable_if_enum_t&> { - if (auto name = enum_traits>::name(value); !name.empty()) { + if (auto name = enum_name(value); !name.empty()) { for (auto c : name) { os.put(c); } } else { - os << static_cast>(value); + os << enum_integer(value); } return os; @@ -542,22 +550,22 @@ namespace bitwise_operators { template constexpr auto operator~(E rhs) noexcept -> detail::enable_if_enum_t { - return static_cast(~static_cast>(rhs)); + return static_cast(~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)); + 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)); + 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)); + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } template diff --git a/test/test.cpp b/test/test.cpp index 49ce5dc..71eddfe 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -55,7 +55,7 @@ TEST_CASE("enum_cast") { SECTION("string") { constexpr auto cr = enum_cast("RED"); REQUIRE(cr.value() == Color::RED); - REQUIRE(enum_cast("GREEN").value() == Color::GREEN); + REQUIRE(enum_cast("GREEN").value() == Color::GREEN); REQUIRE(enum_cast("BLUE").value() == Color::BLUE); REQUIRE_FALSE(enum_cast("None").has_value()); @@ -67,7 +67,7 @@ TEST_CASE("enum_cast") { REQUIRE_FALSE(enum_cast("None").has_value()); constexpr auto dr = enum_cast("Right"); - REQUIRE(enum_cast("Up").value() == Directions::Up); + REQUIRE(enum_cast("Up").value() == Directions::Up); REQUIRE(enum_cast("Down").value() == Directions::Down); REQUIRE(dr.value() == Directions::Right); REQUIRE(enum_cast("Left").value() == Directions::Left); @@ -85,7 +85,6 @@ TEST_CASE("enum_cast") { Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; constexpr auto cr = enum_cast(-12); REQUIRE(cr.value() == Color::RED); - REQUIRE(enum_cast(7).value() == Color::GREEN); REQUIRE(enum_cast(7).value() == Color::GREEN); REQUIRE(enum_cast((int)cm[2]).value() == Color::BLUE); REQUIRE_FALSE(enum_cast(0).has_value()); @@ -98,7 +97,7 @@ TEST_CASE("enum_cast") { REQUIRE_FALSE(enum_cast(0).has_value()); constexpr auto dr = enum_cast(120); - REQUIRE(enum_cast(85).value() == Directions::Up); + REQUIRE(enum_cast(85).value() == Directions::Up); REQUIRE(enum_cast(-42).value() == Directions::Down); REQUIRE(dr.value() == Directions::Right); REQUIRE(enum_cast(-120).value() == Directions::Left); @@ -116,8 +115,9 @@ TEST_CASE("enum_cast") { TEST_CASE("enum_integer") { Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; constexpr auto cr = enum_integer(Color::RED); + Color cg = Color::GREEN; REQUIRE(cr == -12); - REQUIRE(enum_integer(Color::GREEN) == 7); + REQUIRE(enum_integer(cg) == 7); REQUIRE(enum_integer(cm[2]) == 15); REQUIRE(enum_integer(static_cast(0)) == 0); @@ -129,7 +129,8 @@ TEST_CASE("enum_integer") { REQUIRE(enum_integer(static_cast(0)) == 0); constexpr auto dr = enum_integer(Directions::Right); - REQUIRE(enum_integer(Directions::Left) == -120); + Directions dl = Directions::Left; + REQUIRE(enum_integer(dl) == -120); REQUIRE(enum_integer(Directions::Down) == -42); REQUIRE(enum_integer(Directions::Up) == 85); REQUIRE(dr == 120); @@ -146,8 +147,9 @@ TEST_CASE("enum_integer") { TEST_CASE("enum_index") { Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; constexpr auto cr = enum_index(Color::RED); + Color cg = Color::GREEN; REQUIRE(cr.value() == 0); - REQUIRE(enum_index(Color::GREEN).value() == 1); + REQUIRE(enum_index(cg).value() == 1); REQUIRE(enum_index(cm[2]).value() == 2); REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); @@ -159,7 +161,8 @@ TEST_CASE("enum_index") { REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); constexpr auto dr = enum_index(Directions::Right); - REQUIRE(enum_index(Directions::Left).value() == 0); + Directions dl = Directions::Left; + REQUIRE(enum_index(dl).value() == 0); REQUIRE(enum_index(Directions::Down).value() == 1); REQUIRE(enum_index(Directions::Up).value() == 2); REQUIRE(dr.value() == 3); @@ -176,7 +179,7 @@ TEST_CASE("enum_index") { TEST_CASE("enum_value") { constexpr auto cr = enum_value(0); REQUIRE(cr == Color::RED); - REQUIRE(enum_value(1) == Color::GREEN); + REQUIRE(enum_value(1) == Color::GREEN); REQUIRE(enum_value(2) == Color::BLUE); constexpr auto no = enum_value(0); @@ -185,7 +188,7 @@ TEST_CASE("enum_value") { REQUIRE(enum_value(2) == Numbers::three); constexpr auto dr = enum_value(3); - REQUIRE(enum_value(0) == Directions::Left); + REQUIRE(enum_value(0) == Directions::Left); REQUIRE(enum_value(1) == Directions::Down); REQUIRE(enum_value(2) == Directions::Up); REQUIRE(dr == Directions::Right); @@ -197,13 +200,13 @@ TEST_CASE("enum_value") { } TEST_CASE("enum_values") { - constexpr auto s1 = enum_values(); + constexpr auto s1 = enum_values(); REQUIRE(s1 == std::array{{Color::RED, Color::GREEN, Color::BLUE}}); auto s2 = enum_values(); REQUIRE(s2 == std::array{{Numbers::one, Numbers::two, Numbers::three}}); - constexpr auto s3 = enum_values(); + constexpr auto s3 = enum_values(); REQUIRE(s3 == std::array{{Directions::Left, Directions::Down, Directions::Up, Directions::Right}}); auto s4 = enum_values(); @@ -211,13 +214,13 @@ TEST_CASE("enum_values") { } TEST_CASE("enum_count") { - constexpr auto s1 = enum_count(); + constexpr auto s1 = enum_count(); REQUIRE(s1 == 3); auto s2 = enum_count(); REQUIRE(s2 == 3); - constexpr auto s3 = enum_count(); + constexpr auto s3 = enum_count(); REQUIRE(s3 == 4); auto s4 = enum_count(); @@ -229,8 +232,9 @@ TEST_CASE("enum_name") { constexpr Color cr = Color::RED; constexpr auto cr_name = enum_name(cr); Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + Color cb = Color::BLUE; REQUIRE(cr_name == "RED"); - REQUIRE(enum_name(Color::BLUE) == "BLUE"); + REQUIRE(enum_name(cb) == "BLUE"); REQUIRE(enum_name(cm[1]) == "GREEN"); REQUIRE(enum_name(static_cast(0)).empty()); @@ -244,7 +248,8 @@ TEST_CASE("enum_name") { constexpr Directions dr = Directions::Right; constexpr auto dr_name = enum_name(dr); - REQUIRE(enum_name(Directions::Up) == "Up"); + Directions du = Directions::Up; + REQUIRE(enum_name(du) == "Up"); REQUIRE(enum_name(Directions::Down) == "Down"); REQUIRE(dr_name == "Right"); REQUIRE(enum_name(Directions::Left) == "Left"); @@ -295,13 +300,13 @@ TEST_CASE("enum_name") { } TEST_CASE("enum_names") { - constexpr auto s1 = enum_names(); + constexpr auto s1 = enum_names(); REQUIRE(s1 == std::array{{"RED", "GREEN", "BLUE"}}); auto s2 = enum_names(); REQUIRE(s2 == std::array{{"one", "two", "three"}}); - constexpr auto s3 = enum_names(); + constexpr auto s3 = enum_names(); REQUIRE(s3 == std::array{{"Left", "Down", "Up", "Right"}}); auto s4 = enum_names(); @@ -309,13 +314,13 @@ TEST_CASE("enum_names") { } TEST_CASE("enum_entries") { - constexpr auto s1 = enum_entries(); + constexpr auto s1 = enum_entries(); REQUIRE(s1 == std::array, 3>{{{Color::RED, "RED"}, {Color::GREEN, "GREEN"}, {Color::BLUE, "BLUE"}}}); auto s2 = enum_entries(); REQUIRE(s2 == std::array, 3>{{{Numbers::one, "one"}, {Numbers::two, "two"}, {Numbers::three, "three"}}}); - constexpr auto s3 = enum_entries(); + constexpr auto s3 = enum_entries(); REQUIRE(s3 == std::array, 4>{{{Directions::Left, "Left"}, {Directions::Down, "Down"}, {Directions::Up, "Up"}, {Directions::Right, "Right"}}}); auto s4 = enum_entries(); @@ -458,9 +463,9 @@ TEST_CASE("type_traits") { TEST_CASE("enum_traits") { SECTION("type_name") { - REQUIRE(enum_traits::type_name == "Color"); + REQUIRE(enum_traits::type_name == "Color"); REQUIRE(enum_traits::type_name == "Numbers"); - REQUIRE(enum_traits::type_name == "Directions"); + REQUIRE(enum_traits::type_name == "Directions"); REQUIRE(enum_traits::type_name == "number"); } }