diff --git a/README.md b/README.md index 1b2e309..6c1ca94 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Header-only C++17 library provides static reflection for enums, work with any en * `enum_names` obtains string enum name sequence. * `enum_entries` obtains pair (value enum, string enum name) sequence. * `enum_index` obtains index in enum value sequence from enum value. -* `contains_value` checks whether enum contains such value +* `enum_contains` checks whether enum contains enumerator with such value. * `is_unscoped_enum` checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration). * `is_scoped_enum` checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations). * `underlying_type` improved UB-free "SFINAE-friendly" [std::underlying_type](https://en.cppreference.com/w/cpp/types/underlying_type). diff --git a/doc/reference.md b/doc/reference.md index 89d4b31..84150b0 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -9,6 +9,7 @@ * [`enum_names` obtains string enum name sequence.](#enum_names) * [`enum_entries` obtains pair (value enum, string enum name) sequence.](#enum_entries) * [`enum_index` obtains index in enum value sequence from enum value.](#enum_index) +* [`enum_contains` checks whether enum contains enumerator with such value.](#enum_contains) * [`is_unscoped_enum` checks whether type is an Unscoped enumeration.](#is_unscoped_enum) * [`is_scoped_enum` checks whether type is an Scoped enumeration.](#is_scoped_enum) * [`underlying_type` improved UB-free "SFINAE-friendly" std::underlying_type.](#underlying_type) @@ -43,7 +44,7 @@ constexpr optional enum_cast(underlying_type_t value) noexcept; * String to enum value. ```cpp - std::string color_name{"GREEN"}; + string color_name{"GREEN"}; auto color = magic_enum::enum_cast(color_name); if (color.has_value()) { // color.value() -> Color::GREEN @@ -108,7 +109,7 @@ constexpr size_t enum_count() noexcept; * Examples ```cpp - constexpr std::size_t color_count = magic_enum::enum_count(); + constexpr auto color_count = magic_enum::enum_count(); // color_count -> 3 ``` @@ -198,6 +199,48 @@ constexpr array, N> enum_entries() noexcept; // color_entries[0].second -> "RED" ``` +## `enum_index` + +```cpp +template +constexpr optional enum_index() noexcept; +``` + +* Obtains index in enum value sequence from enum value. + +* Returns `std::optional` with index. + +* Examples + + ```cpp + constexpr auto color_index = magic_enum::enum_index(Color::BLUE); + // color_index -> color_index.value() -> 1 + // color_index -> color_index.has_value() -> true + ``` + +## `enum_contains` + +```cpp +template +constexpr bool enum_contains(E value) noexcept; +constexpr bool enum_contains(underlying_type_t value) noexcept; +constexpr bool enum_contains(string_view value) noexcept; +``` + +* Checks whether enum contains enumerator with such value. + +* Returns true is enum contains value, otherwise false. + +* Examples + + ```cpp + magic_enum::enum_contains(Color::GREEN); // -> true + magic_enum::enum_contains(2); // -> true + magic_enum::enum_contains(123); // -> false + magic_enum::enum_contains("GREEN"); // -> true + magic_enum::enum_contains("fda"); // -> false + ``` + ## `is_unscoped_enum` ```cpp diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index c67d058..de05c83 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -356,25 +356,11 @@ struct enum_traits { inline static constexpr std::array, count> entries = detail::entries(std::make_index_sequence>{}); [[nodiscard]] static constexpr bool reflected(E value) noexcept { - return static_cast(value) >= static_cast(reflected_min_v) && static_cast(value) <= static_cast(reflected_max_v); - } - - [[nodiscard]] static constexpr int index(underlying_type value) noexcept { - if (value >= min_v && value <= max_v) { - if constexpr (is_sparse) { - if (const auto i = indexes[value - min_v]; i != invalid_index_v) { - return i; - } - } else { - return value - min_v; - } - } - - return -1; // Value out of range. + return reflected(static_cast(value)); } [[nodiscard]] static constexpr int index(E value) noexcept { - return index(static_cast(value)); + return index(static_cast(value)); } [[nodiscard]] static constexpr E value(std::size_t index) noexcept { @@ -399,6 +385,24 @@ struct enum_traits { static_assert(count > 0, "magic_enum::enum_range requires enum implementation and valid max and min."); using U = underlying_type; inline static constexpr auto indexes = detail::indexes(std::make_integer_sequence>{}); + + static constexpr bool reflected(U value) noexcept { + return value >= static_cast(reflected_min_v) && value <= static_cast(reflected_max_v); + } + + static constexpr int index(U value) noexcept { + if (value >= static_cast(min_v) && value <= static_cast(max_v)) { + if constexpr (is_sparse) { + if (const auto i = indexes[value - min_v]; i != invalid_index_v) { + return i; + } + } else { + return value - min_v; + } + } + + return -1; // Value out of range. + } }; } // namespace magic_enum::detail @@ -490,12 +494,24 @@ template return std::nullopt; // Value out of range. } -// Checks whether enum contains enumerator with such value +// Checks whether enum contains enumerator with such value. template -[[nodiscard]] constexpr auto contains_value(underlying_type_t value) noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { return enum_traits::index(value) != -1; } +// Checks whether enum contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_t { + return enum_cast(value).has_value(); +} + +// Checks whether enum contains enumerator with such string enum name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t { + return enum_cast(value).has_value(); +} + // Returns enum value at specified index. // No bounds checking is performed: the behavior is undefined if index >= number of enum values. template diff --git a/test/test.cpp b/test/test.cpp index 3aa963b..d2e9c54 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -68,7 +68,7 @@ TEST_CASE("enum_cast") { constexpr auto dr = enum_cast("Right"); REQUIRE(enum_cast("Up").value() == Directions::Up); - REQUIRE(enum_cast("Down").value() == Directions::Down); + REQUIRE(enum_cast("Down").value() == Directions::Down); REQUIRE(dr.value() == Directions::Right); REQUIRE(enum_cast("Left").value() == Directions::Left); REQUIRE_FALSE(enum_cast("None").has_value()); @@ -98,7 +98,7 @@ TEST_CASE("enum_cast") { constexpr auto dr = enum_cast(120); REQUIRE(enum_cast(85).value() == Directions::Up); - REQUIRE(enum_cast(-42).value() == Directions::Down); + REQUIRE(enum_cast(-42).value() == Directions::Down); REQUIRE(dr.value() == Directions::Right); REQUIRE(enum_cast(-120).value() == Directions::Left); REQUIRE_FALSE(enum_cast(0).has_value()); @@ -131,7 +131,7 @@ TEST_CASE("enum_integer") { constexpr auto dr = enum_integer(Directions::Right); Directions dl = Directions::Left; REQUIRE(enum_integer(dl) == -120); - REQUIRE(enum_integer(Directions::Down) == -42); + REQUIRE(enum_integer(Directions::Down) == -42); REQUIRE(enum_integer(Directions::Up) == 85); REQUIRE(dr == 120); REQUIRE(enum_integer(static_cast(0)) == 0); @@ -163,7 +163,7 @@ TEST_CASE("enum_index") { constexpr auto dr = enum_index(Directions::Right); Directions dl = Directions::Left; REQUIRE(enum_index(dl).value() == 0); - REQUIRE(enum_index(Directions::Down).value() == 1); + REQUIRE(enum_index(Directions::Down).value() == 1); REQUIRE(enum_index(Directions::Up).value() == 2); REQUIRE(dr.value() == 3); REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); @@ -176,36 +176,99 @@ TEST_CASE("enum_index") { REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); } -TEST_CASE("contains_value") { - REQUIRE(contains_value(-12)); - REQUIRE(contains_value(7)); - REQUIRE(contains_value(15)); - REQUIRE_FALSE(contains_value(42)); - REQUIRE_FALSE(contains_value(-120)); - REQUIRE_FALSE(contains_value(0)); +TEST_CASE("enum_contains") { + SECTION("value") { + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + constexpr auto cr = enum_contains(Color::RED); + Color cg = Color::GREEN; + REQUIRE(cr); + REQUIRE(enum_contains(cg)); + REQUIRE(enum_contains(cm[2])); + REQUIRE_FALSE(enum_contains(static_cast(0))); - constexpr auto no = enum_integer(Numbers::one); - REQUIRE(contains_value(no)); - REQUIRE(contains_value(enum_integer(Numbers::two))); - REQUIRE(contains_value(enum_integer(Numbers::three))); - REQUIRE_FALSE(contains_value(enum_integer(Numbers::many))); + constexpr auto no = enum_contains(Numbers::one); + REQUIRE(no); + REQUIRE(enum_contains(Numbers::two)); + REQUIRE(enum_contains(Numbers::three)); + REQUIRE_FALSE(enum_contains(Numbers::many)); + REQUIRE_FALSE(enum_contains(static_cast(0))); - constexpr auto dr = enum_integer(Directions::Right); - REQUIRE(contains_value(dr)); - REQUIRE(contains_value(Directions::Down)); - REQUIRE(contains_value(Directions::Up)); - REQUIRE_FALSE(contains_value(static_cast(0))); + constexpr auto dr = enum_contains(Directions::Right); + Directions dl = Directions::Left; + REQUIRE(enum_contains(dl)); + REQUIRE(enum_contains(Directions::Down)); + REQUIRE(enum_contains(Directions::Up)); + REQUIRE(dr); + REQUIRE_FALSE(enum_contains(static_cast(0))); - constexpr auto nt = contains_value(number::three); - REQUIRE(contains_value(number::one)); - REQUIRE(contains_value(100)); - REQUIRE(contains_value(200)); - REQUIRE(contains_value(300)); - REQUIRE(contains_value(number::two)); - REQUIRE(nt); - REQUIRE_FALSE(contains_value(number::four)); - REQUIRE_FALSE(contains_value(111)); - REQUIRE_FALSE(contains_value(0)); + constexpr auto nt = enum_contains(number::three); + REQUIRE(enum_contains(number::one)); + REQUIRE(enum_contains(number::two)); + REQUIRE(nt); + REQUIRE_FALSE(enum_contains(number::four)); + REQUIRE_FALSE(enum_contains(static_cast(0))); + } + + SECTION("integer") { + REQUIRE(enum_contains(-12)); + REQUIRE(enum_contains(7)); + REQUIRE(enum_contains(15)); + REQUIRE_FALSE(enum_contains(42)); + REQUIRE_FALSE(enum_contains(-120)); + REQUIRE_FALSE(enum_contains(0)); + + constexpr auto no = enum_integer(Numbers::one); + REQUIRE(enum_contains(no)); + REQUIRE(enum_contains(enum_integer(Numbers::two))); + REQUIRE(enum_contains(enum_integer(Numbers::three))); + REQUIRE_FALSE(enum_contains(enum_integer(Numbers::many))); + + constexpr auto dr = enum_integer(Directions::Right); + REQUIRE(enum_contains(dr)); + REQUIRE(enum_contains(Directions::Down)); + REQUIRE(enum_contains(Directions::Up)); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto nt = enum_contains(number::three); + REQUIRE(enum_contains(number::one)); + REQUIRE(enum_contains(100)); + REQUIRE(enum_contains(200)); + REQUIRE(enum_contains(300)); + REQUIRE(enum_contains(number::two)); + REQUIRE(nt); + REQUIRE_FALSE(enum_contains(number::four)); + REQUIRE_FALSE(enum_contains(111)); + REQUIRE_FALSE(enum_contains(0)); + } + + SECTION("string") { + constexpr auto cr = "RED"; + REQUIRE(enum_contains(cr)); + REQUIRE(enum_contains("GREEN")); + REQUIRE(enum_contains("BLUE")); + REQUIRE_FALSE(enum_contains("None")); + + constexpr auto no = std::string_view{"one"}; + REQUIRE(enum_contains(no)); + REQUIRE(enum_contains("two")); + REQUIRE(enum_contains("three")); + REQUIRE_FALSE(enum_contains("many")); + REQUIRE_FALSE(enum_contains("None")); + + auto dr = std::string{"Right"}; + REQUIRE(enum_contains("Up")); + REQUIRE(enum_contains("Down")); + REQUIRE(enum_contains(dr)); + REQUIRE(enum_contains("Left")); + REQUIRE_FALSE(enum_contains("None")); + + constexpr auto nt = enum_contains("three"); + REQUIRE(enum_contains("one")); + REQUIRE(enum_contains("two")); + REQUIRE(nt); + REQUIRE_FALSE(enum_contains("four")); + REQUIRE_FALSE(enum_contains("None")); + } } TEST_CASE("enum_value") { @@ -221,7 +284,7 @@ TEST_CASE("enum_value") { constexpr auto dr = enum_value(3); REQUIRE(enum_value(0) == Directions::Left); - REQUIRE(enum_value(1) == Directions::Down); + REQUIRE(enum_value(1) == Directions::Down); REQUIRE(enum_value(2) == Directions::Up); REQUIRE(dr == Directions::Right); @@ -238,7 +301,7 @@ TEST_CASE("enum_values") { 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(); @@ -252,7 +315,7 @@ TEST_CASE("enum_count") { auto s2 = enum_count(); REQUIRE(s2 == 3); - constexpr auto s3 = enum_count(); + constexpr auto s3 = enum_count(); REQUIRE(s3 == 4); auto s4 = enum_count(); @@ -282,7 +345,7 @@ TEST_CASE("enum_name") { constexpr auto dr_name = enum_name(dr); Directions du = Directions::Up; REQUIRE(enum_name(du) == "Up"); - REQUIRE(enum_name(Directions::Down) == "Down"); + REQUIRE(enum_name(Directions::Down) == "Down"); REQUIRE(dr_name == "Right"); REQUIRE(enum_name(Directions::Left) == "Left"); REQUIRE(enum_name(static_cast(0)).empty()); @@ -334,7 +397,7 @@ TEST_CASE("enum_names") { 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();