1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-09 23:34:23 +00:00
This commit is contained in:
neargye 2022-11-07 21:00:16 +04:00
parent 596f00c0db
commit 8bd403f888
10 changed files with 502 additions and 298 deletions

View file

@ -6,7 +6,7 @@ package(default_visibility = ["//visibility:public"])
cc_library(
name = "magic_enum",
hdrs = ["include/magic_enum.hpp", "include/magic_enum_fuse.hpp"],
hdrs = ["include/magic_enum.hpp", "include/magic_enum_format.hpp", "include/magic_enum_fuse.hpp", "include/magic_enum_switch.hpp"],
includes = ["include"],
)

View file

@ -154,7 +154,6 @@ Header-only C++17 library provides static reflection for enums, work with any en
* Enum switch runtime value as constexpr constant
```cpp
Color color = Color::RED;
magic_enum::enum_switch([] (auto val) {
constexpr Color c_color = val;
// ...

View file

@ -6,7 +6,6 @@
* [`enum_count` returns number of enum values.](#enum_count)
* [`enum_integer` obtains integer value from enum value.](#enum_integer)
* [`enum_name` returns name from enum value.](#enum_name)
* [`enum_flags_name` returns name from enum-flags value.](#enum_flags_name)
* [`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)
@ -15,6 +14,7 @@
* [`enum_fuse` returns a bijective mix of enum values.](#enum_fuse)
* [`enum_switch` allows runtime enum value transformation to constexpr context.](#enum_switch)
* [`enum_for_each` calls a function with all enum constexpr value.](#enum_for_each)
* [`enum_flags` API from enum-flags.](#enum_flags)
* [`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" underlying_type.](#underlying_type)
@ -219,25 +219,6 @@ constexpr string_view enum_name() noexcept;
// color_name -> "BLUE"
```
## `enum_flags_name`
```cpp
template <typename E>
string enum_flags_name(E value);
```
* Returns name from enum-flags value as `string` with null-terminated string.
* If enum-flags value does not have name or [out of range](limitations.md), returns empty string.
* Examples
```cpp
auto directions_name = magic_enum::enum_flags_name(Directions::Up | Directions::Right);
// directions_name -> "Directions::Up | Directions::Right"
```
## `enum_names`
```cpp
@ -383,20 +364,21 @@ constexpr Result enum_switch(Lambda&& lambda, E value);
template <typename Result, typename E, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, E value, Result&& result);
template <typename E, typename Result = void, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {});
template <typename E, typename Result, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {});
template <typename E, typename Result = void, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, underlying_type_t<E> value);
template <typename E, typename Result, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, underlying_type_t<E> value, Result&& result);
```
* You should add the required file `<magic_enum_switch.hpp>`.
* Examples
```cpp
Color color = Color::RED;
magic_enum::enum_switch([] (auto val) {
constexpr Color c_color = val;
// ...
}, color);
```
## `enum_for_each`
```cpp
@ -404,6 +386,51 @@ template <typename E, typename Lambda>
constexpr auto enum_for_each(Lambda&& lambda);
```
* Examples
```cpp
magic_enum::enum_for_each<Color>([] (auto val) {
constexpr Color c_color = val;
// ...
});
```
## `enum_flags`
```cpp
template <typename E>
string enum_flags_name(E value);
template <typename E>
constexpr optional<E> enum_flags_cast(underlying_type_t<E> value) noexcept;
template <typename E>
constexpr optional<E> enum_flags_cast(string_view value) noexcept;
template <typename E, typename BinaryPredicate>
constexpr optional<E> enum_flags_cast(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v<BinaryPredicate>);
template <typename E>
constexpr bool enum_flags_contains(E value) noexcept;
template <typename E>
constexpr bool enum_flags_contains(underlying_type_t<E> value) noexcept;
template <typename E>
constexpr bool enum_flags_contains(string_view value) noexcept;
template <typename E, typename BinaryPredicate>
constexpr optional<E> enum_flags_contains(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v<BinaryPredicate>);
```
* Examples
```cpp
auto directions_name = magic_enum::enum_flags_name(Directions::Up | Directions::Right);
// directions_name -> "Directions::Up | Directions::Right"
```
## `is_unscoped_enum`
```cpp

View file

@ -22,8 +22,8 @@
// SOFTWARE.
#include <iostream>
#include <magic_enum.hpp>
#define MAGIC_ENUM_ENABLE_HASH
#include <magic_enum_switch.hpp>
enum class Color { RED, BLUE, GREEN };
@ -64,9 +64,9 @@ int main() {
}
};
magic_enum::enum_switch<Color>(switcher1, 2 /* GREEN */); // prints nothing
magic_enum::enum_switch<Color>(switcher1, 1 /* BLUE */); // prints "Blue"
magic_enum::enum_switch<Color>(switcher1, 0 /* RED */); // prints "Red"
magic_enum::enum_switch(switcher1, Color::GREEN); // prints nothing
magic_enum::enum_switch(switcher1, Color::BLUE); // prints "Blue"
magic_enum::enum_switch(switcher1, Color::RED); // prints "Red"
// explicit result type
auto switcher2 = overloaded{
@ -86,23 +86,18 @@ int main() {
assert(empty.empty());
// result with default object
std::cout << magic_enum::enum_switch<Color, std::string>(switcher2, -3, "unrecognized") << std::endl; // prints "unrecognized"
std::cout << magic_enum::enum_switch<std::string>(switcher2, static_cast<Color>(-3), "unrecognized") << std::endl; // prints "unrecognized"
auto switcher3 = overloaded{
[] (magic_enum::enum_constant<Color::RED>) {
[] (magic_enum::enum_constant<Color::RED>) -> std::optional<std::string> {
return "red result";
},
[] (magic_enum::enum_constant<Color::BLUE>) {
[] (magic_enum::enum_constant<Color::BLUE>) -> std::optional<std::string> {
return std::nullopt;
}
};
std::cout << std::boolalpha;
std::cout << magic_enum::enum_switch<Color>(switcher3, "GREEN", std::make_optional("cica")).value() << std::endl; // prints default: "cica"
std::cout << magic_enum::enum_switch<Color>(switcher3, "RED", std::make_optional("cica")).value() << std::endl; // prints: "red result"
std::cout << magic_enum::enum_switch<Color>(switcher3, "BLUE", std::make_optional("cica")).has_value() << std::endl; // prints: false
std::cout << magic_enum::enum_switch<Color>(switcher3, "red", std::make_optional("cica"), magic_enum::case_insensitive).value() << std::endl; // prints: "red result"
std::cout << magic_enum::enum_switch(switcher3, Color::GREEN, std::make_optional("cica")).value() << std::endl; // prints default: "cica"
std::cout << magic_enum::enum_switch(switcher3, Color::RED, std::make_optional("cica")).value() << std::endl; // prints: "red result"
std::cout << magic_enum::enum_switch(switcher3, Color::BLUE, std::make_optional("cica")).has_value() << std::endl; // prints: false

View file

@ -171,6 +171,11 @@ namespace detail {
template <auto V, typename E = std::decay_t<decltype(V)>, std::enable_if_t<std::is_enum_v<E>, int> = 0>
using enum_constant = std::integral_constant<E, V>;
enum class value_type {
default_value,
flags_value
};
template <typename... T>
inline constexpr bool always_false_v = false;
@ -695,8 +700,14 @@ struct enable_if_enum<true, R> {
static_assert(supported<R>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
};
template <typename T, typename R, typename BinaryPredicate = std::equal_to<>>
using enable_if_t = typename enable_if_enum<std::is_enum_v<std::decay_t<T>> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
template <typename T, typename R, typename BinaryPredicate = std::equal_to<>, typename D = std::decay_t<T>>
using enable_if_t = typename enable_if_enum<std::is_enum_v<D> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
template <typename T, typename R, value_type VT, typename BinaryPredicate = std::equal_to<>, typename D = std::decay_t<T>>
using enable_if_default_t = typename enable_if_enum<std::is_enum_v<D> && VT == value_type::default_value && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
template <typename T, typename R, value_type VT, typename BinaryPredicate = std::equal_to<>, typename D = std::decay_t<T>>
using enable_if_flags_t = typename enable_if_enum<std::is_enum_v<D> && VT == value_type::flags_value && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
template <typename T, std::enable_if_t<std::is_enum_v<std::decay_t<T>>, int> = 0>
using enum_concept = T;
@ -719,10 +730,7 @@ struct underlying_type {};
template <typename T>
struct underlying_type<T, true> : std::underlying_type<std::decay_t<T>> {};
#if defined(MAGIC_ENUM_NO_HASH)
template <typename T>
inline constexpr bool has_hash = false;
#else
#if defined(MAGIC_ENUM_ENABLE_HASH)
template <typename T>
inline constexpr bool has_hash = true;
@ -846,7 +854,8 @@ constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r
}
enum class case_call_t {
index, value
index,
value
};
template <typename T = void>
@ -856,7 +865,7 @@ template <>
inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
template <auto* Arr, typename Hash>
constexpr bool no_duplicate() noexcept {
constexpr bool has_duplicate() noexcept {
using value_t = std::decay_t<decltype((*Arr)[0])>;
using hash_value_t = std::invoke_result_t<Hash, value_t>;
std::array<hash_value_t, Arr->size()> hashes{};
@ -913,15 +922,17 @@ template <auto* GlobValues,
case_call_t CallValue,
std::size_t Page = 0,
typename Hash = constexpr_hash_t<typename std::decay_t<decltype(*GlobValues)>::value_type>,
typename Lambda, typename ResultGetterType = decltype(default_result_type_lambda<>),
typename BinaryPredicate = std::equal_to<>>
typename BinaryPredicate = std::equal_to<>,
typename Lambda,
typename ResultGetterType>
constexpr std::invoke_result_t<ResultGetterType> constexpr_switch(
Lambda&& lambda,
typename std::decay_t<decltype(*GlobValues)>::value_type searched,
ResultGetterType&& def = default_result_type_lambda<>,
ResultGetterType&& def,
BinaryPredicate&& pred = {}) {
using result_t = std::invoke_result_t<ResultGetterType>;
using hash_t = std::conditional_t<no_duplicate<GlobValues, Hash>(), Hash, typename Hash::secondary_hash>;
using hash_t = std::conditional_t<has_duplicate<GlobValues, Hash>(), Hash, typename Hash::secondary_hash>;
static_assert(has_duplicate<GlobValues, hash_t>(), "magic_enum::detail::constexpr_switch duplicated hash found, please report it: https://github.com/Neargye/magic_enum/issues.");
constexpr std::array values = *GlobValues;
constexpr std::size_t size = values.size();
constexpr std::array cases = calculate_cases<GlobValues, hash_t>(Page);
@ -939,28 +950,36 @@ constexpr std::invoke_result_t<ResultGetterType> constexpr_switch(
#undef MAGIC_ENUM_FOR_EACH_256
#undef MAGIC_ENUM_CASE
#else
template <typename T>
inline constexpr bool has_hash = false;
#endif
template <typename E, typename Lambda, std::size_t... I>
constexpr auto for_each(Lambda&& lambda, std::index_sequence<I...>) {
template <typename E, typename F, std::size_t... I>
constexpr auto for_each(F&& f, std::index_sequence<I...>) {
static_assert(is_enum_v<E>, "magic_enum::detail::for_each requires enum type.");
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> || ...);
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[0]>>, std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> && ...);
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<F, enum_constant<values_v<E>[I]>>> || ...);
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<F, enum_constant<values_v<E>[0]>>, std::invoke_result_t<F, enum_constant<values_v<E>[I]>>> && ...);
if constexpr (has_void_return) {
(lambda(enum_constant<values_v<E>[I]>{}), ...);
(f(enum_constant<values_v<E>[I]>{}), ...);
} else if constexpr (all_same_return) {
return std::array{lambda(enum_constant<values_v<E>[I]>{})...};
return std::array{f(enum_constant<values_v<E>[I]>{})...};
} else {
return std::tuple{lambda(enum_constant<values_v<E>[I]>{})...};
return std::tuple{f(enum_constant<values_v<E>[I]>{})...};
}
}
template <typename E, typename Lambda, std::size_t... I>
template <typename E, typename F, std::size_t... I>
constexpr bool all_invocable(std::index_sequence<I...>) {
static_assert(is_enum_v<E>, "magic_enum::detail::all_invocable requires enum type.");
return (std::is_invocable_v<Lambda, enum_constant<values_v<E>[I]>> && ...);
if constexpr (count_v<E> == 0) {
return false;
} else {
return (std::is_invocable_v<F, enum_constant<values_v<E>[I]>> && ...);
}
}
} // namespace magic_enum::detail
@ -1066,18 +1085,18 @@ template <typename E>
if constexpr (detail::count_v<D> == 0) {
return {}; // Empty enum.
} else if constexpr (detail::is_sparse_v<D> || detail::is_flags_v<D>) {
#if defined(MAGIC_ENUM_NO_HASH)
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>(
[](std::size_t i) { return optional<std::size_t>{i}; },
value,
detail::default_result_type_lambda<optional<std::size_t>>);
#else
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (enum_value<D>(i) == value) {
return i;
}
}
return {}; // Invalid value or out of range.
#else
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>(
[](std::size_t i) { return optional<std::size_t>{i}; },
value,
detail::default_result_type_lambda<optional<std::size_t>>);
#endif
} else {
const auto v = static_cast<U>(value);
@ -1109,8 +1128,8 @@ template <auto V>
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <typename E>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> {
template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_default_t<E, string_view, VT> {
using D = std::decay_t<E>;
if (const auto i = enum_index<D>(value)) {
@ -1119,14 +1138,23 @@ template <typename E>
return {};
}
// Returns name from enum-flags value.
// If enum-flags value does not have name or value out of range, returns empty string.
template <typename E>
[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t<E, string> {
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <detail::value_type VT, typename E>
[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_default_t<E, string_view, VT> {
using D = std::decay_t<E>;
return enum_name<D, VT>(value);
}
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <typename E, detail::value_type VT>
[[nodiscard]] auto enum_name(E value) -> detail::enable_if_flags_t<E, string, VT> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_name requires enum-flags type.");
if constexpr (detail::is_flags_v<D>) {
string name;
auto check_value = U{0};
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
@ -1145,12 +1173,26 @@ template <typename E>
}
return {}; // Invalid value or out of range.
} else {
if (const auto name = enum_name<D>(value); !name.empty()) {
return {name.data(), name.size()};
}
return {}; // Invalid value or out of range.
}
}
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <detail::value_type VT, typename E>
[[nodiscard]] auto enum_name(E value) -> detail::enable_if_flags_t<E, string, VT> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_name requires enum-flags type.");
return enum_name<D, VT>(value);
}
// Returns name from enum-flags value.
// If enum-flags value does not have name or value out of range, returns empty string.
template <typename E>
[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t<E, string> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_name requires enum-flags type.");
return enum_name<D, detail::value_type::flags_value>(value);
}
// Returns std::array with names, sorted by enum value.
@ -1170,15 +1212,15 @@ inline constexpr auto case_insensitive = detail::case_insensitive{};
// Obtains enum value from integer value.
// Returns optional with enum value.
template <typename E>
template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
if constexpr (detail::count_v<D> == 0) {
return {}; // Empty enum.
} else if constexpr (detail::is_sparse_v<D>) {
if constexpr (detail::is_flags_v<D>) {
} else if constexpr (VT == detail::value_type::flags_value && detail::is_flags_v<D>) {
if constexpr (detail::is_sparse_v<D>) {
auto check_value = U{0};
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (const auto v = static_cast<U>(enum_value<D>(i)); (value & v) != 0) {
@ -1191,42 +1233,51 @@ template <typename E>
}
return {}; // Invalid value or out of range.
} else {
#if defined(MAGIC_ENUM_NO_HASH)
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (value == static_cast<U>(enum_value<D>(i))) {
return static_cast<D>(value);
}
}
return {}; // Invalid value or out of range.
#else
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>);
#endif
}
} else {
constexpr auto min = detail::min_v<D>;
constexpr auto max = detail::is_flags_v<D> ? detail::values_ors<D>() : detail::max_v<D>;
constexpr auto max = detail::values_ors<D>();
if (value >= min && value <= max) {
return static_cast<D>(value);
}
return {}; // Invalid value or out of range.
}
} else {
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>);
#else
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (value == static_cast<U>(enum_value<D>(i))) {
return static_cast<D>(value);
}
}
return {}; // Invalid value or out of range.
#endif
}
}
// Obtains enum-flags value from integer value.
// Returns optional with enum-flags value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_cast requires enum-flags type.");
return enum_cast<D, detail::value_type::flags_value>(value);
}
// Obtains enum value from name.
// Returns optional with enum value.
template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_cast requires bool(char, char) invocable predicate.");
template <typename E, detail::value_type VT = detail::value_type::default_value, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
if constexpr (detail::count_v<D> == 0) {
return {}; // Empty enum.
} else if constexpr (detail::is_flags_v<D>) {
} else if constexpr (VT == detail::value_type::flags_value && detail::is_flags_v<D>) {
auto result = U{0};
while (!value.empty()) {
const auto d = detail::find(value, '|');
@ -1249,16 +1300,9 @@ template <typename E, typename BinaryPredicate = std::equal_to<>>
return static_cast<D>(result);
}
return {}; // Invalid value or out of range.
} else if constexpr (detail::count_v<D> > 0) {
if constexpr (detail::is_default_predicate<BinaryPredicate>()) {
#if defined(MAGIC_ENUM_NO_HASH)
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (detail::cmp_equal(value, detail::names_v<D>[i], p)) {
return enum_value<D>(i);
}
}
return {}; // Invalid value or out of range.
#else
} else {
if constexpr (detail::is_default_predicate<BinaryPredicate>() && detail::has_hash<D>) {
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::names_v<D>, detail::case_call_t::index>(
[](std::size_t i) { return optional<D>{detail::values_v<D>[i]}; },
value,
@ -1276,113 +1320,84 @@ template <typename E, typename BinaryPredicate = std::equal_to<>>
}
}
// Checks whether enum contains enumerator with such enum value.
template <typename E>
// Obtains enum-flags value from name.
// Returns optional with enum-flags value.
template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_cast requires enum-flags type.");
return enum_cast<D, detail::value_type::flags_value>(value, std::move(p));
}
// Checks whether enum contains value with such value.
template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
return static_cast<bool>(enum_cast<D>(static_cast<U>(value)));
return static_cast<bool>(enum_cast<D, VT>(static_cast<U>(value)));
}
// Checks whether enum contains enumerator with such integer value.
// Checks whether enum-flags contains value with such value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_contains requires enum-flags type.");
return enum_contains<D, detail::value_type::flags_value>(value);
}
// Checks whether enum contains value with such integer value.
template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
return static_cast<bool>(enum_cast<D>(value));
return static_cast<bool>(enum_cast<D, VT>(value));
}
// Checks whether enum-flags contains value with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_contains requires enum-flags type.");
return enum_contains<D, detail::value_type::flags_value>(value);
}
// Checks whether enum contains enumerator with such name.
template <typename E, detail::value_type VT = detail::value_type::default_value, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
using D = std::decay_t<E>;
return static_cast<bool>(enum_cast<D, VT>(value, std::move(p)));
}
// Checks whether enum-flags contains enumerator with such name.
template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_contains requires bool(char, char) invocable predicate.");
[[nodiscard]] constexpr auto enum_flags_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_contains requires enum-flags type.");
return static_cast<bool>(enum_cast<D>(value, std::forward<BinaryPredicate>(p)));
return enum_contains<D, detail::value_type::flags_value>(value, std::move(p));
}
template <typename Result = void, typename E, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t<E, Result> {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<Lambda>(lambda),
value,
detail::default_result_type_lambda<Result>);
}
template <typename Result, typename E, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t<E, Result> {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<Lambda>(lambda),
value,
[&result] { return std::forward<Result>(result); });
}
template <typename E, typename Result = void, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v);
}
return detail::default_result_type_lambda<Result>();
}
template <typename E, typename Result, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result));
}
return std::forward<Result>(result);
}
template <typename E, typename Result = void, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value) -> detail::enable_if_t<E, Result> {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(value)) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v);
}
return detail::default_result_type_lambda<Result>();
}
template <typename E, typename Result, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value, Result&& result) -> detail::enable_if_t<E, Result> {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(value)) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result));
}
return std::forward<Result>(result);
}
template <typename E, typename Lambda, detail::enable_if_t<E, int> = 0>
constexpr auto enum_for_each(Lambda&& lambda) {
template <typename E, typename F, detail::enable_if_t<E, int> = 0>
constexpr auto enum_for_each(F&& f) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
constexpr auto sep = std::make_index_sequence<detail::count_v<D>>{};
if constexpr (detail::all_invocable<D, Lambda>(sep)) {
return detail::for_each<D>(std::forward<Lambda>(lambda), sep);
if constexpr (detail::all_invocable<D, F>(sep)) {
return detail::for_each<D>(std::forward<F>(f), sep);
} else {
static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value.");
}
}
template <bool AsFlags = true>
inline constexpr auto as_flags = AsFlags ? detail::value_type::flags_value : detail::value_type::default_value;
#if !defined(MAGIC_ENUM_NO_STREAMS)
namespace ostream_operators {
@ -1393,7 +1408,7 @@ std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& o
using U = underlying_type_t<D>;
if constexpr (detail::supported<D>::value) {
if (const auto name = enum_flags_name<D>(value); !name.empty()) {
if (const auto name = enum_name<D, as_flags<detail::is_flags_v<D>>>(value); !name.empty()) {
for (const auto c : name) {
os.put(c);
}
@ -1418,7 +1433,7 @@ std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& i
std::basic_string<Char, Traits> s;
is >> s;
if (const auto v = enum_cast<D>(s)) {
if (const auto v = enum_cast<D, as_flags<detail::is_flags_v<D>>>(s)) {
value = *v;
} else {
is.setstate(std::basic_ios<Char>::failbit);

View file

@ -43,12 +43,6 @@
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
#endif // MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT
#if !defined(MAGIC_ENUM_NO_EXCEPTIONS) && (defined(__cpp_exceptions) || defined(_EXCEPTIONS) || defined(_HAS_EXCEPTIONS))
# define MAGIC_ENUM_THROW throw std::format_error
#else
# define MAGIC_ENUM_THROW std::terminate(); (void)
#endif
namespace magic_enum::customize {
// customize enum to enable/disable automatic std::format
template <typename E>
@ -62,18 +56,15 @@ namespace magic_enum::customize {
template <typename E>
struct std::formatter<E, std::enable_if_t<std::is_enum_v<E> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> {
auto format(E e, format_context& ctx) {
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
using D = std::decay_t<E>;
if constexpr (magic_enum::detail::is_flags_v<D>) {
if (auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
return this->std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
}
} else {
if (auto name = magic_enum::enum_name<D>(e); !name.empty()) {
return this->std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
if constexpr (detail::supported<D>::value) {
if (const auto name = magic_enum::enum_name<D, magic_enum::as_flags<magic_enum::detail::is_flags_v<D>>>(e); !name.empty()) {
return std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
}
}
constexpr auto type_name = magic_enum::enum_type_name<E>();
MAGIC_ENUM_THROW("Type of " + std::string{type_name.data(), type_name.size()} + " enum value: " + std::to_string(magic_enum::enum_integer<D>(e)) + " is not exists.");
return std::formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
}
};

View file

@ -0,0 +1,139 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.8.1
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP
#define NEARGYE_MAGIC_ENUM_SWITCH_HPP
#include "magic_enum.hpp"
namespace magic_enum {
namespace detail {
struct default_result_type {};
template <typename T>
struct identity {
using type = T;
};
struct nonesuch {};
template <typename F, typename V, bool = std::is_invocable_v<F, V>>
struct invoke_result : identity<nonesuch> {};
template <typename F, typename V>
struct invoke_result<F, V, true> : std::invoke_result<F, V> {};
template <typename F, typename V>
using invoke_result_t = typename invoke_result<F, V>::type;
template <typename E, typename F, std::size_t... I>
constexpr auto common_invocable(std::index_sequence<I...>) noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::invocable_index requires enum type.");
if constexpr (count_v<E> == 0) {
return identity<nonesuch>{};
} else {
return std::common_type<invoke_result_t<F, enum_constant<values_v<E>[I]>>...>{};
}
}
template <typename E, typename Result, typename F>
constexpr auto result_type() noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::result_type requires enum type.");
constexpr auto seq = std::make_index_sequence<detail::count_v<E>>{};
using R = typename decltype(common_invocable<E, F>(seq))::type;
if constexpr (std::is_same_v<Result, default_result_type>) {
if constexpr (std::is_same_v<R, nonesuch>) {
return identity<void>{};
} else {
return identity<R>{};
}
} else {
if constexpr (std::is_convertible_v<R, Result>) {
return identity<Result>{};
} else if constexpr (std::is_convertible_v<Result, R>) {
return identity<R>{};
} else {
return identity<nonesuch>{};
}
}
}
template <typename T, typename Result, typename F, typename D = std::decay_t<T>, typename R = typename decltype(result_type<D, Result, F>())::type>
using result_t = std::enable_if_t<std::is_enum_v<D> && !std::is_same_v<R, nonesuch>, R>;
} // namespace magic_enum::detail
template <typename Result = detail::default_result_type, typename E, typename F, typename R = detail::result_t<E, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<F>(f),
value,
detail::default_result_type_lambda<R>);
#else
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires defined MAGIC_ENUM_ENABLE_HASH");
#endif
}
template <typename Result, typename E, typename F, typename R = detail::result_t<E, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<F>(f),
value,
[&result]() -> R { return std::forward<Result>(result); });
#else
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires defined MAGIC_ENUM_ENABLE_HASH");
#endif
}
} // namespace magic_enum
template <>
struct std::common_type<magic_enum::detail::nonesuch, magic_enum::detail::nonesuch> : magic_enum::detail::identity<magic_enum::detail::nonesuch> {};
template <typename T>
struct std::common_type<T, magic_enum::detail::nonesuch> : magic_enum::detail::identity<T> {};
template <typename T>
struct std::common_type<magic_enum::detail::nonesuch, T> : magic_enum::detail::identity<T> {};
#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP

View file

@ -545,14 +545,14 @@ TEST_CASE("enum_name") {
Color cb = Color::BLUE;
REQUIRE(cr_name == "red");
REQUIRE(enum_name<Color&>(cb) == "BLUE");
REQUIRE(enum_name(cm[1]) == "GREEN");
REQUIRE(enum_name(static_cast<Color>(0)).empty());
REQUIRE(enum_name<as_flags<false>>(cm[1]) == "GREEN");
REQUIRE(enum_name<detail::value_type::default_value>(static_cast<Color>(0)).empty());
constexpr Numbers no = Numbers::one;
constexpr auto no_name = enum_name(no);
REQUIRE(no_name == "one");
REQUIRE(enum_name(Numbers::two) == "two");
REQUIRE(enum_name(Numbers::three) == "three");
REQUIRE(enum_name<Numbers, as_flags<false>>(Numbers::two) == "two");
REQUIRE(enum_name<as_flags<false>, Numbers>(Numbers::three) == "three");
REQUIRE(enum_name(Numbers::many).empty());
REQUIRE(enum_name(static_cast<Numbers>(0)).empty());
@ -1083,22 +1083,6 @@ constexpr std::string_view DoWork<Color::GREEN>() {
return "override";
}
TEST_CASE("enum_switch") {
constexpr auto bind_enum_switch = [] (Color c) {
return enum_switch([](auto val) {
return DoWork<val>();
}, c, string_view{"unrecognized"});
};
constexpr auto def = bind_enum_switch(Color::BLUE);
REQUIRE(def == "default");
REQUIRE(bind_enum_switch(Color::RED) == "default");
REQUIRE(bind_enum_switch(Color::GREEN) == "override");
REQUIRE(bind_enum_switch(static_cast<Color>(0)) == "unrecognized");
}
TEST_CASE("enum_for_each") {
SECTION("no return type") {
underlying_type_t<Color> sum{};
@ -1207,6 +1191,8 @@ TEST_CASE("multdimensional-switch-case") {
#include <magic_enum_format.hpp>
TEST_CASE("format-base") {
REQUIRE(std::format("{}", Color::RED) == "red");
REQUIRE(std::format("{}", Color{0}) == "0");
REQUIRE(std::format("Test-{:~^10}.", Color::RED) == "Test-~~~red~~~~.");
}

View file

@ -43,6 +43,9 @@ struct MyOpt {
};
struct MyString {
using value_type = char; // required
static constexpr auto npos = std::string_view::npos; // required
MyString() : str{} {} // required
MyString(const char* s, std::size_t l) : str{s, l} {} // required
bool empty() const { return str.empty(); } // required

View file

@ -93,12 +93,20 @@ TEST_CASE("enum_cast") {
REQUIRE(cr.value() == Color::RED);
REQUIRE(enum_cast<Color&>("GREEN").value() == Color::GREEN);
REQUIRE(enum_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
REQUIRE(enum_cast<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::BLUE | Color::RED));
REQUIRE(enum_cast<Color&>("GREEN|RED").value() == (Color::GREEN | Color::RED));
REQUIRE(enum_cast<Color&>("GREEN|RED|RED").value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_cast<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).has_value());
REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED").has_value());
REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED|RED").has_value());
REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED|None").has_value());
REQUIRE_FALSE(enum_cast<Color>("None").has_value());
REQUIRE(enum_flags_cast<Color&>("GREEN").value() == Color::GREEN);
REQUIRE(enum_flags_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
REQUIRE(enum_flags_cast<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::BLUE | Color::RED));
REQUIRE(enum_flags_cast<Color&>("GREEN|RED").value() == (Color::GREEN | Color::RED));
REQUIRE(enum_flags_cast<Color&>("GREEN|RED|RED").value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_flags_cast<Color&>("GREEN|RED|None").has_value());
REQUIRE_FALSE(enum_flags_cast<Color>("None").has_value());
constexpr auto no = enum_cast<Numbers>("one");
REQUIRE(no.value() == Numbers::one);
REQUIRE(enum_cast<Numbers>("two").value() == Numbers::two);
@ -122,7 +130,7 @@ TEST_CASE("enum_cast") {
REQUIRE_FALSE(enum_cast<Language>("None").has_value());
#endif
constexpr auto nto = enum_cast<number>("three|one");
constexpr auto nto = enum_flags_cast<number>("three|one");
REQUIRE(enum_cast<number>("one").value() == number::one);
REQUIRE(enum_cast<number>("two").value() == number::two);
REQUIRE(enum_cast<number>("three").value() == number::three);
@ -137,11 +145,18 @@ TEST_CASE("enum_cast") {
REQUIRE(cr.value() == Color::RED);
REQUIRE(enum_cast<Color&>(2).value() == Color::GREEN);
REQUIRE(enum_cast<Color>(static_cast<int>(cm[2])).value() == Color::BLUE);
REQUIRE(enum_cast<Color>(1 | 2).value() == (Color::GREEN | Color::RED));
REQUIRE(enum_cast<Color>(1 | 2 | 1).value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_cast<Color>(1 | 2).has_value());
REQUIRE_FALSE(enum_cast<Color>(1 | 2 | 1).has_value());
REQUIRE_FALSE(enum_cast<Color>(1 | 2 | 8).has_value());
REQUIRE_FALSE(enum_cast<Color>(0).has_value());
REQUIRE(enum_flags_cast<Color&>(2).value() == Color::GREEN);
REQUIRE(enum_flags_cast<Color>(static_cast<int>(cm[2])).value() == Color::BLUE);
REQUIRE(enum_flags_cast<Color>(1 | 2).value() == (Color::GREEN | Color::RED));
REQUIRE(enum_flags_cast<Color>(1 | 2 | 1).value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_flags_cast<Color>(1 | 2 | 8).has_value());
REQUIRE_FALSE(enum_flags_cast<Color>(0).has_value());
constexpr auto no = enum_cast<Numbers>(2);
REQUIRE(no.value() == Numbers::one);
REQUIRE(enum_cast<Numbers>(4).value() == Numbers::two);
@ -166,7 +181,7 @@ TEST_CASE("enum_cast") {
REQUIRE_FALSE(enum_cast<Language>(0).has_value());
#endif
constexpr auto nto = enum_cast<number>(2 | 8);
constexpr auto nto = enum_flags_cast<number>(2 | 8);
REQUIRE(enum_cast<number>(1 << 1).value() == number::one);
REQUIRE(enum_cast<number>(1 << 2).value() == number::two);
REQUIRE(enum_cast<number>(1 << 3).value() == number::three);
@ -230,11 +245,20 @@ TEST_CASE("enum_contains") {
REQUIRE(cr);
REQUIRE(enum_contains<Color&>(cg));
REQUIRE(enum_contains(cm[2]));
REQUIRE(enum_contains<Color>(Color::RED | Color::GREEN));
REQUIRE(enum_contains<Color>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE(enum_contains<Color, as_flags<>>(Color::RED | Color::GREEN));
REQUIRE(enum_contains<Color, as_flags<true>>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color::GREEN));
REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color{8}));
REQUIRE_FALSE(enum_contains(static_cast<Color>(0)));
REQUIRE(enum_flags_contains<Color&>(cg));
REQUIRE(enum_flags_contains(cm[2]));
REQUIRE(enum_flags_contains<Color>(Color::RED | Color::GREEN));
REQUIRE(enum_flags_contains<Color>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE_FALSE(enum_flags_contains<Color>(Color::RED | Color{8}));
REQUIRE_FALSE(enum_flags_contains(static_cast<Color>(0)));
constexpr auto no = enum_contains(Numbers::one);
REQUIRE(no);
REQUIRE(enum_contains(Numbers::two));
@ -265,19 +289,31 @@ TEST_CASE("enum_contains") {
REQUIRE(enum_contains<number&>(number::two));
REQUIRE(enum_contains(number::one));
REQUIRE(enum_contains(number::four));
REQUIRE(nto);
REQUIRE_FALSE(nto);
REQUIRE_FALSE(enum_contains(static_cast<number>(0)));
REQUIRE(enum_flags_contains(number::three | number::one));
}
SECTION("integer") {
REQUIRE(enum_contains<Color>(1));
REQUIRE(enum_contains<Color&>(2));
REQUIRE(enum_contains<Color>(4));
REQUIRE(enum_contains<Color>(1 | 2));
REQUIRE(enum_contains<Color>(1 | 2 | 1));
REQUIRE(enum_contains<const Color>(4));
REQUIRE(enum_contains<Color, as_flags<>>(1 | 2));
REQUIRE(enum_contains<Color, as_flags<true>>(1 | 2 | 1));
REQUIRE_FALSE(enum_contains<Color>(1 | 2));
REQUIRE_FALSE(enum_contains<Color>(1 | 2 | 1));
REQUIRE_FALSE(enum_contains<Color>(1 | 2 | 8));
REQUIRE_FALSE(enum_contains<Color>(0));
REQUIRE(enum_flags_contains<Color>(1));
REQUIRE(enum_flags_contains<Color&>(2));
REQUIRE(enum_flags_contains<Color>(4));
REQUIRE(enum_flags_contains<Color>(1 | 2));
REQUIRE(enum_flags_contains<Color>(1 | 2 | 1));
REQUIRE_FALSE(enum_flags_contains<Color>(1 | 2 | 8));
REQUIRE_FALSE(enum_flags_contains<Color>(0));
constexpr auto no = enum_contains<Numbers>(1 << 1);
REQUIRE(no);
REQUIRE(enum_contains<Numbers>(1 << 2));
@ -305,9 +341,9 @@ TEST_CASE("enum_contains") {
REQUIRE(enum_contains<number>(1 << 2));
REQUIRE(enum_contains<number>(1 << 3));
REQUIRE(enum_contains<number>(1 << 4));
REQUIRE(enum_contains<number>(8 | 2 | 16));
REQUIRE(enum_contains<number>(8 | 16 | 16));
REQUIRE(nto);
REQUIRE_FALSE(enum_contains<number>(8 | 2 | 16));
REQUIRE_FALSE(enum_contains<number>(8 | 16 | 16));
REQUIRE_FALSE(nto);
REQUIRE_FALSE(enum_contains<number>(8 | 64));
REQUIRE_FALSE(enum_contains<number>(0));
}
@ -316,13 +352,24 @@ TEST_CASE("enum_contains") {
constexpr auto cr = "RED";
REQUIRE(enum_contains<Color>(cr));
REQUIRE(enum_contains<Color&>("GREEN"));
REQUIRE(enum_contains<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&>("GREEN|RED"));
REQUIRE(enum_contains<Color&>("GREEN|RED|RED"));
REQUIRE(enum_contains<const Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&, as_flags<>>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&, as_flags<true>>("GREEN|RED"));
REQUIRE(enum_contains<Color, as_flags<true>>("GREEN|RED|RED"));
REQUIRE_FALSE(enum_contains<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE_FALSE(enum_contains<Color&>("GREEN|RED"));
REQUIRE_FALSE(enum_contains<Color&>("GREEN|RED|RED"));
REQUIRE_FALSE(enum_contains<Color>("GREEN|RED|None"));
REQUIRE_FALSE(enum_contains<Color>("None"));
REQUIRE(enum_flags_contains<Color&>("GREEN"));
REQUIRE(enum_flags_contains<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_flags_contains<Color>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_flags_contains<Color>("GREEN|RED"));
REQUIRE(enum_flags_contains<Color>("GREEN|RED|RED"));
REQUIRE_FALSE(enum_flags_contains<Color>("GREEN|RED|None"));
REQUIRE_FALSE(enum_flags_contains<Color>("None"));
constexpr auto no = std::string_view{"one"};
REQUIRE(enum_contains<Numbers>(no));
REQUIRE(enum_contains<Numbers>("two"));
@ -351,8 +398,10 @@ TEST_CASE("enum_contains") {
REQUIRE(enum_contains<number>("two"));
REQUIRE(enum_contains<number>("three"));
REQUIRE(enum_contains<number>("four"));
REQUIRE(nto);
REQUIRE_FALSE(nto);
REQUIRE_FALSE(enum_contains<number>("None"));
REQUIRE(enum_flags_contains<number>("three|one"));
}
}