1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-10 23:44:29 +00:00

Change contains_value to enum_contains (#31)

* change contains_value to enum_contains

* update test

* update doc
This commit is contained in:
Daniil Goncharov 2020-03-13 18:10:09 +05:00 committed by GitHub
parent aa24461613
commit a76480629c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 179 additions and 57 deletions

View file

@ -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).

View file

@ -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<E> enum_cast(underlying_type_t<E> value) noexcept;
* String to enum value.
```cpp
std::string color_name{"GREEN"};
string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(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<Color>();
constexpr auto color_count = magic_enum::enum_count<Color>();
// color_count -> 3
```
@ -198,6 +199,48 @@ constexpr array<pair<E, string_view>, N> enum_entries() noexcept;
// color_entries[0].second -> "RED"
```
## `enum_index`
```cpp
template <typename E>
constexpr optional<size_t> enum_index() noexcept;
```
* Obtains index in enum value sequence from enum value.
* Returns `std::optional<std::size_t>` 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 <typename E>
constexpr bool enum_contains(E value) noexcept;
constexpr bool enum_contains(underlying_type_t<E> 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<Color>(2); // -> true
magic_enum::enum_contains<Color>(123); // -> false
magic_enum::enum_contains<Color>("GREEN"); // -> true
magic_enum::enum_contains<Color>("fda"); // -> false
```
## `is_unscoped_enum`
```cpp

View file

@ -356,21 +356,7 @@ struct enum_traits<E, true> {
inline static constexpr std::array<std::pair<E, std::string_view>, count> entries = detail::entries<E>(std::make_index_sequence<count_v<E>>{});
[[nodiscard]] static constexpr bool reflected(E value) noexcept {
return static_cast<U>(value) >= static_cast<U>(reflected_min_v<E>) && static_cast<U>(value) <= static_cast<U>(reflected_max_v<E>);
}
[[nodiscard]] static constexpr int index(underlying_type value) noexcept {
if (value >= min_v<E> && value <= max_v<E>) {
if constexpr (is_sparse) {
if (const auto i = indexes[value - min_v<E>]; i != invalid_index_v<E>) {
return i;
}
} else {
return value - min_v<E>;
}
}
return -1; // Value out of range.
return reflected(static_cast<U>(value));
}
[[nodiscard]] static constexpr int index(E value) noexcept {
@ -399,6 +385,24 @@ struct enum_traits<E, true> {
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<E>(std::make_integer_sequence<int, range_size_v<E>>{});
static constexpr bool reflected(U value) noexcept {
return value >= static_cast<U>(reflected_min_v<E>) && value <= static_cast<U>(reflected_max_v<E>);
}
static constexpr int index(U value) noexcept {
if (value >= static_cast<U>(min_v<E>) && value <= static_cast<U>(max_v<E>)) {
if constexpr (is_sparse) {
if (const auto i = indexes[value - min_v<E>]; i != invalid_index_v<E>) {
return i;
}
} else {
return value - min_v<E>;
}
}
return -1; // Value out of range.
}
};
} // namespace magic_enum::detail
@ -490,12 +494,24 @@ template <typename E>
return std::nullopt; // Value out of range.
}
// Checks whether enum contains enumerator with such value
// Checks whether enum contains enumerator with such value.
template <typename E>
[[nodiscard]] constexpr auto contains_value(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, bool> {
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t<E, bool> {
return enum_traits<E>::index(value) != -1;
}
// Checks whether enum contains enumerator with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, bool> {
return enum_cast<E>(value).has_value();
}
// Checks whether enum contains enumerator with such string enum name.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t<E, bool> {
return enum_cast<E>(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 <typename E>

View file

@ -68,7 +68,7 @@ TEST_CASE("enum_cast") {
constexpr auto dr = enum_cast<Directions>("Right");
REQUIRE(enum_cast<Directions&>("Up").value() == Directions::Up);
REQUIRE(enum_cast<Directions>("Down").value() == Directions::Down);
REQUIRE(enum_cast<const Directions>("Down").value() == Directions::Down);
REQUIRE(dr.value() == Directions::Right);
REQUIRE(enum_cast<Directions>("Left").value() == Directions::Left);
REQUIRE_FALSE(enum_cast<Directions>("None").has_value());
@ -98,7 +98,7 @@ TEST_CASE("enum_cast") {
constexpr auto dr = enum_cast<Directions>(120);
REQUIRE(enum_cast<Directions&>(85).value() == Directions::Up);
REQUIRE(enum_cast<Directions>(-42).value() == Directions::Down);
REQUIRE(enum_cast<const Directions>(-42).value() == Directions::Down);
REQUIRE(dr.value() == Directions::Right);
REQUIRE(enum_cast<Directions>(-120).value() == Directions::Left);
REQUIRE_FALSE(enum_cast<Directions>(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<Directions&>(dl) == -120);
REQUIRE(enum_integer(Directions::Down) == -42);
REQUIRE(enum_integer<const Directions>(Directions::Down) == -42);
REQUIRE(enum_integer(Directions::Up) == 85);
REQUIRE(dr == 120);
REQUIRE(enum_integer(static_cast<Directions>(0)) == 0);
@ -163,7 +163,7 @@ TEST_CASE("enum_index") {
constexpr auto dr = enum_index(Directions::Right);
Directions dl = Directions::Left;
REQUIRE(enum_index<Directions&>(dl).value() == 0);
REQUIRE(enum_index(Directions::Down).value() == 1);
REQUIRE(enum_index<const Directions>(Directions::Down).value() == 1);
REQUIRE(enum_index(Directions::Up).value() == 2);
REQUIRE(dr.value() == 3);
REQUIRE_FALSE(enum_index(static_cast<Directions>(0)).has_value());
@ -176,36 +176,99 @@ TEST_CASE("enum_index") {
REQUIRE_FALSE(enum_index(static_cast<number>(0)).has_value());
}
TEST_CASE("contains_value") {
REQUIRE(contains_value<Color>(-12));
REQUIRE(contains_value<Color>(7));
REQUIRE(contains_value<Color>(15));
REQUIRE_FALSE(contains_value<Color>(42));
REQUIRE_FALSE(contains_value<Color>(-120));
REQUIRE_FALSE(contains_value<Color>(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<Color&>(cg));
REQUIRE(enum_contains(cm[2]));
REQUIRE_FALSE(enum_contains(static_cast<Color>(0)));
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<Numbers>(0)));
constexpr auto dr = enum_contains(Directions::Right);
Directions dl = Directions::Left;
REQUIRE(enum_contains<Directions&>(dl));
REQUIRE(enum_contains<const Directions>(Directions::Down));
REQUIRE(enum_contains(Directions::Up));
REQUIRE(dr);
REQUIRE_FALSE(enum_contains(static_cast<Directions>(0)));
constexpr auto nt = enum_contains(number::three);
REQUIRE(enum_contains(number::one));
REQUIRE(enum_contains<number&>(number::two));
REQUIRE(nt);
REQUIRE_FALSE(enum_contains(number::four));
REQUIRE_FALSE(enum_contains(static_cast<number>(0)));
}
SECTION("integer") {
REQUIRE(enum_contains<Color>(-12));
REQUIRE(enum_contains<Color&>(7));
REQUIRE(enum_contains<Color>(15));
REQUIRE_FALSE(enum_contains<Color>(42));
REQUIRE_FALSE(enum_contains<Color>(-120));
REQUIRE_FALSE(enum_contains<Color>(0));
constexpr auto no = enum_integer(Numbers::one);
REQUIRE(contains_value<Numbers>(no));
REQUIRE(contains_value<Numbers>(enum_integer(Numbers::two)));
REQUIRE(contains_value<Numbers>(enum_integer(Numbers::three)));
REQUIRE_FALSE(contains_value<Numbers>(enum_integer(Numbers::many)));
REQUIRE(enum_contains<Numbers>(no));
REQUIRE(enum_contains<Numbers>(enum_integer(Numbers::two)));
REQUIRE(enum_contains<Numbers>(enum_integer(Numbers::three)));
REQUIRE_FALSE(enum_contains<Numbers>(enum_integer(Numbers::many)));
constexpr auto dr = enum_integer(Directions::Right);
REQUIRE(contains_value<Directions&>(dr));
REQUIRE(contains_value<Directions>(Directions::Down));
REQUIRE(contains_value<Directions>(Directions::Up));
REQUIRE_FALSE(contains_value<Directions>(static_cast<Directions>(0)));
REQUIRE(enum_contains<Directions&>(dr));
REQUIRE(enum_contains<const Directions>(Directions::Down));
REQUIRE(enum_contains<Directions>(Directions::Up));
REQUIRE_FALSE(enum_contains<Directions>(static_cast<Directions>(0)));
constexpr auto nt = contains_value<number>(number::three);
REQUIRE(contains_value<number>(number::one));
REQUIRE(contains_value<number>(100));
REQUIRE(contains_value<number>(200));
REQUIRE(contains_value<number>(300));
REQUIRE(contains_value<number>(number::two));
constexpr auto nt = enum_contains<number>(number::three);
REQUIRE(enum_contains<number>(number::one));
REQUIRE(enum_contains<number>(100));
REQUIRE(enum_contains<number>(200));
REQUIRE(enum_contains<number>(300));
REQUIRE(enum_contains<number>(number::two));
REQUIRE(nt);
REQUIRE_FALSE(contains_value<number>(number::four));
REQUIRE_FALSE(contains_value<number>(111));
REQUIRE_FALSE(contains_value<number>(0));
REQUIRE_FALSE(enum_contains<number>(number::four));
REQUIRE_FALSE(enum_contains<number>(111));
REQUIRE_FALSE(enum_contains<number>(0));
}
SECTION("string") {
constexpr auto cr = "RED";
REQUIRE(enum_contains<Color>(cr));
REQUIRE(enum_contains<Color&>("GREEN"));
REQUIRE(enum_contains<Color>("BLUE"));
REQUIRE_FALSE(enum_contains<Color>("None"));
constexpr auto no = std::string_view{"one"};
REQUIRE(enum_contains<Numbers>(no));
REQUIRE(enum_contains<Numbers>("two"));
REQUIRE(enum_contains<Numbers>("three"));
REQUIRE_FALSE(enum_contains<Numbers>("many"));
REQUIRE_FALSE(enum_contains<Numbers>("None"));
auto dr = std::string{"Right"};
REQUIRE(enum_contains<Directions&>("Up"));
REQUIRE(enum_contains<Directions>("Down"));
REQUIRE(enum_contains<const Directions>(dr));
REQUIRE(enum_contains<Directions>("Left"));
REQUIRE_FALSE(enum_contains<Directions>("None"));
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<number>("None"));
}
}
TEST_CASE("enum_value") {
@ -221,7 +284,7 @@ TEST_CASE("enum_value") {
constexpr auto dr = enum_value<Directions>(3);
REQUIRE(enum_value<Directions&>(0) == Directions::Left);
REQUIRE(enum_value<Directions>(1) == Directions::Down);
REQUIRE(enum_value<const Directions>(1) == Directions::Down);
REQUIRE(enum_value<Directions>(2) == Directions::Up);
REQUIRE(dr == Directions::Right);
@ -238,7 +301,7 @@ TEST_CASE("enum_values") {
auto s2 = enum_values<Numbers>();
REQUIRE(s2 == std::array<Numbers, 3>{{Numbers::one, Numbers::two, Numbers::three}});
constexpr auto s3 = enum_values<Directions&>();
constexpr auto s3 = enum_values<const Directions>();
REQUIRE(s3 == std::array<Directions, 4>{{Directions::Left, Directions::Down, Directions::Up, Directions::Right}});
auto s4 = enum_values<number>();
@ -252,7 +315,7 @@ TEST_CASE("enum_count") {
auto s2 = enum_count<Numbers>();
REQUIRE(s2 == 3);
constexpr auto s3 = enum_count<Directions&>();
constexpr auto s3 = enum_count<const Directions>();
REQUIRE(s3 == 4);
auto s4 = enum_count<number>();
@ -282,7 +345,7 @@ TEST_CASE("enum_name") {
constexpr auto dr_name = enum_name(dr);
Directions du = Directions::Up;
REQUIRE(enum_name<Directions&>(du) == "Up");
REQUIRE(enum_name(Directions::Down) == "Down");
REQUIRE(enum_name<const Directions>(Directions::Down) == "Down");
REQUIRE(dr_name == "Right");
REQUIRE(enum_name(Directions::Left) == "Left");
REQUIRE(enum_name(static_cast<Directions>(0)).empty());
@ -334,7 +397,7 @@ TEST_CASE("enum_names") {
auto s2 = enum_names<Numbers>();
REQUIRE(s2 == std::array<std::string_view, 3>{{"one", "two", "three"}});
constexpr auto s3 = enum_names<Directions&>();
constexpr auto s3 = enum_names<const Directions>();
REQUIRE(s3 == std::array<std::string_view, 4>{{"Left", "Down", "Up", "Right"}});
auto s4 = enum_names<number>();