diff --git a/BUILD.bazel b/BUILD.bazel index 2e08379..7ad3c08 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -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"], ) diff --git a/README.md b/README.md index 217f5f3..aca0c5e 100644 --- a/README.md +++ b/README.md @@ -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; // ... diff --git a/doc/reference.md b/doc/reference.md index 493c233..e90bd61 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -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 -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 constexpr Result enum_switch(Lambda&& lambda, E value, Result&& result); - -template , typename Lambda> -constexpr Result enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}); - -template , typename Lambda> -constexpr Result enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}); - -template -constexpr Result enum_switch(Lambda&& lambda, underlying_type_t value); - -template -constexpr Result enum_switch(Lambda&& lambda, underlying_type_t value, Result&& result); ``` +* You should add the required file ``. + +* 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 constexpr auto enum_for_each(Lambda&& lambda); ``` +* Examples + + ```cpp + magic_enum::enum_for_each([] (auto val) { + constexpr Color c_color = val; + // ... + }); + ``` + +## `enum_flags` + +```cpp +template +string enum_flags_name(E value); + +template +constexpr optional enum_flags_cast(underlying_type_t value) noexcept; + +template +constexpr optional enum_flags_cast(string_view value) noexcept; + +template +constexpr optional enum_flags_cast(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v); + +template +constexpr bool enum_flags_contains(E value) noexcept; + +template +constexpr bool enum_flags_contains(underlying_type_t value) noexcept; + +template +constexpr bool enum_flags_contains(string_view value) noexcept; + +template +constexpr optional enum_flags_contains(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v); +``` + +* Examples + + ```cpp + auto directions_name = magic_enum::enum_flags_name(Directions::Up | Directions::Right); + // directions_name -> "Directions::Up | Directions::Right" + ``` + + ## `is_unscoped_enum` ```cpp diff --git a/example/example_switch.cpp b/example/example_switch.cpp index a756169..b1e452c 100644 --- a/example/example_switch.cpp +++ b/example/example_switch.cpp @@ -22,8 +22,8 @@ // SOFTWARE. #include - -#include +#define MAGIC_ENUM_ENABLE_HASH +#include enum class Color { RED, BLUE, GREEN }; @@ -64,9 +64,9 @@ int main() { } }; - magic_enum::enum_switch(switcher1, 2 /* GREEN */); // prints nothing - magic_enum::enum_switch(switcher1, 1 /* BLUE */); // prints "Blue" - magic_enum::enum_switch(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(switcher2, -3, "unrecognized") << std::endl; // prints "unrecognized" + std::cout << magic_enum::enum_switch(switcher2, static_cast(-3), "unrecognized") << std::endl; // prints "unrecognized" auto switcher3 = overloaded{ - [] (magic_enum::enum_constant) { + [] (magic_enum::enum_constant) -> std::optional { return "red result"; }, - [] (magic_enum::enum_constant) { + [] (magic_enum::enum_constant) -> std::optional { return std::nullopt; } }; std::cout << std::boolalpha; - std::cout << magic_enum::enum_switch(switcher3, "GREEN", std::make_optional("cica")).value() << std::endl; // prints default: "cica" - std::cout << magic_enum::enum_switch(switcher3, "RED", std::make_optional("cica")).value() << std::endl; // prints: "red result" - std::cout << magic_enum::enum_switch(switcher3, "BLUE", std::make_optional("cica")).has_value() << std::endl; // prints: false - std::cout << magic_enum::enum_switch(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 diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index e6ee10c..fa17dde 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -171,6 +171,11 @@ namespace detail { template , std::enable_if_t, int> = 0> using enum_constant = std::integral_constant; +enum class value_type { + default_value, + flags_value +}; + template inline constexpr bool always_false_v = false; @@ -695,8 +700,14 @@ struct enable_if_enum { static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); }; -template > -using enable_if_t = typename enable_if_enum> && std::is_invocable_r_v, R>::type; +template , typename D = std::decay_t> +using enable_if_t = typename enable_if_enum && std::is_invocable_r_v, R>::type; + +template , typename D = std::decay_t> +using enable_if_default_t = typename enable_if_enum && VT == value_type::default_value && std::is_invocable_r_v, R>::type; + +template , typename D = std::decay_t> +using enable_if_flags_t = typename enable_if_enum && VT == value_type::flags_value && std::is_invocable_r_v, R>::type; template >, int> = 0> using enum_concept = T; @@ -719,10 +730,7 @@ struct underlying_type {}; template struct underlying_type : std::underlying_type> {}; -#if defined(MAGIC_ENUM_NO_HASH) -template -inline constexpr bool has_hash = false; -#else +#if defined(MAGIC_ENUM_ENABLE_HASH) template 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 @@ -856,7 +865,7 @@ template <> inline constexpr auto default_result_type_lambda = []() noexcept {}; template -constexpr bool no_duplicate() noexcept { +constexpr bool has_duplicate() noexcept { using value_t = std::decay_t; using hash_value_t = std::invoke_result_t; std::arraysize()> hashes{}; @@ -887,41 +896,43 @@ constexpr bool no_duplicate() noexcept { T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) -#define MAGIC_ENUM_CASE(val) \ - case cases[val]: \ - if constexpr ((val) + Page < size) { \ - if (!pred(values[val + Page], searched)) { \ - break; \ - } \ - if constexpr (CallValue == case_call_t::index) { \ - if constexpr (std::is_invocable_r_v>) { \ - return detail::invoke_r(std::forward(lambda), std::integral_constant{}); \ - } else if constexpr (std::is_invocable_v>) { \ - assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ - } \ - } else if constexpr (CallValue == case_call_t::value) { \ - if constexpr (std::is_invocable_r_v>) { \ - return detail::invoke_r(std::forward(lambda), enum_constant{}); \ - } else if constexpr (std::is_invocable_r_v>) { \ - assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ - } \ - } \ - break; \ +#define MAGIC_ENUM_CASE(val) \ + case cases[val]: \ + if constexpr ((val) + Page < size) { \ + if (!pred(values[val + Page], searched)) { \ + break; \ + } \ + if constexpr (CallValue == case_call_t::index) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), std::integral_constant{}); \ + } else if constexpr (std::is_invocable_v>) { \ + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ + } \ + } else if constexpr (CallValue == case_call_t::value) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), enum_constant{}); \ + } else if constexpr (std::is_invocable_r_v>) { \ + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ + } \ + } \ + break; \ } else [[fallthrough]]; template ::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 constexpr_switch( Lambda&& lambda, typename std::decay_t::value_type searched, - ResultGetterType&& def = default_result_type_lambda<>, + ResultGetterType&& def, BinaryPredicate&& pred = {}) { using result_t = std::invoke_result_t; - using hash_t = std::conditional_t(), Hash, typename Hash::secondary_hash>; + using hash_t = std::conditional_t(), Hash, typename Hash::secondary_hash>; + static_assert(has_duplicate(), "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(Page); @@ -939,28 +950,36 @@ constexpr std::invoke_result_t constexpr_switch( #undef MAGIC_ENUM_FOR_EACH_256 #undef MAGIC_ENUM_CASE + +#else +template +inline constexpr bool has_hash = false; #endif -template -constexpr auto for_each(Lambda&& lambda, std::index_sequence) { +template +constexpr auto for_each(F&& f, std::index_sequence) { static_assert(is_enum_v, "magic_enum::detail::for_each requires enum type."); - constexpr bool has_void_return = (std::is_void_v[I]>>> || ...); - constexpr bool all_same_return = (std::is_same_v[0]>>, std::invoke_result_t[I]>>> && ...); + constexpr bool has_void_return = (std::is_void_v[I]>>> || ...); + constexpr bool all_same_return = (std::is_same_v[0]>>, std::invoke_result_t[I]>>> && ...); if constexpr (has_void_return) { - (lambda(enum_constant[I]>{}), ...); + (f(enum_constant[I]>{}), ...); } else if constexpr (all_same_return) { - return std::array{lambda(enum_constant[I]>{})...}; + return std::array{f(enum_constant[I]>{})...}; } else { - return std::tuple{lambda(enum_constant[I]>{})...}; + return std::tuple{f(enum_constant[I]>{})...}; } } -template +template constexpr bool all_invocable(std::index_sequence) { static_assert(is_enum_v, "magic_enum::detail::all_invocable requires enum type."); - return (std::is_invocable_v[I]>> && ...); + if constexpr (count_v == 0) { + return false; + } else { + return (std::is_invocable_v[I]>> && ...); + } } } // namespace magic_enum::detail @@ -1066,18 +1085,18 @@ template if constexpr (detail::count_v == 0) { return {}; // Empty enum. } else if constexpr (detail::is_sparse_v || detail::is_flags_v) { -#if defined(MAGIC_ENUM_NO_HASH) +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::index>( + [](std::size_t i) { return optional{i}; }, + value, + detail::default_result_type_lambda>); +#else for (std::size_t i = 0; i < detail::count_v; ++i) { if (enum_value(i) == value) { return i; } } return {}; // Invalid value or out of range. -#else - return detail::constexpr_switch<&detail::values_v, detail::case_call_t::index>( - [](std::size_t i) { return optional{i}; }, - value, - detail::default_result_type_lambda>); #endif } else { const auto v = static_cast(value); @@ -1109,8 +1128,8 @@ template // Returns name from enum value. // If enum value does not have name or value out of range, returns empty string. -template -[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t { +template +[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_default_t { using D = std::decay_t; if (const auto i = enum_index(value)) { @@ -1119,38 +1138,61 @@ template return {}; } +// Returns name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_default_t { + using D = std::decay_t; + + return enum_name(value); +} + +// Returns name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] auto enum_name(E value) -> detail::enable_if_flags_t { + using D = std::decay_t; + using U = underlying_type_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_name requires enum-flags type."); + + string name; + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { + check_value |= v; + const auto n = detail::names_v[i]; + if (!name.empty()) { + name.append(1, '|'); + } + name.append(n.data(), n.size()); + } + } + + if (check_value != 0 && check_value == static_cast(value)) { + return name; + } + + 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 +[[nodiscard]] auto enum_name(E value) -> detail::enable_if_flags_t { + using D = std::decay_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_name requires enum-flags type."); + + return enum_name(value); +} + // Returns name from enum-flags value. // If enum-flags value does not have name or value out of range, returns empty string. template [[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t { using D = std::decay_t; - using U = underlying_type_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_name requires enum-flags type."); - if constexpr (detail::is_flags_v) { - string name; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { - check_value |= v; - const auto n = detail::names_v[i]; - if (!name.empty()) { - name.append(1, '|'); - } - name.append(n.data(), n.size()); - } - } - - if (check_value != 0 && check_value == static_cast(value)) { - return name; - } - - return {}; // Invalid value or out of range. - } else { - if (const auto name = enum_name(value); !name.empty()) { - return {name.data(), name.size()}; - } - return {}; // Invalid value or out of range. - } + return enum_name(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 +template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_t>> { using D = std::decay_t; using U = underlying_type_t; if constexpr (detail::count_v == 0) { return {}; // Empty enum. - } else if constexpr (detail::is_sparse_v) { - if constexpr (detail::is_flags_v) { + } else if constexpr (VT == detail::value_type::flags_value && detail::is_flags_v) { + if constexpr (detail::is_sparse_v) { auto check_value = U{0}; for (std::size_t i = 0; i < detail::count_v; ++i) { if (const auto v = static_cast(enum_value(i)); (value & v) != 0) { @@ -1191,42 +1233,51 @@ template } return {}; // Invalid value or out of range. } else { -#if defined(MAGIC_ENUM_NO_HASH) - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (value == static_cast(enum_value(i))) { - return static_cast(value); - } + constexpr auto min = detail::min_v; + constexpr auto max = detail::values_ors(); + + if (value >= min && value <= max) { + return static_cast(value); } return {}; // Invalid value or out of range. -#else - return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( - [](D v) { return optional{v}; }, - static_cast(value), - detail::default_result_type_lambda>); -#endif } } else { - constexpr auto min = detail::min_v; - constexpr auto max = detail::is_flags_v ? detail::values_ors() : detail::max_v; - - if (value >= min && value <= max) { - return static_cast(value); +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( + [](D v) { return optional{v}; }, + static_cast(value), + detail::default_result_type_lambda>); +#else + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (value == static_cast(enum_value(i))) { + return static_cast(value); + } } return {}; // Invalid value or out of range. +#endif } } +// Obtains enum-flags value from integer value. +// Returns optional with enum-flags value. +template +[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t value) noexcept -> detail::enable_if_t>> { + using D = std::decay_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_cast requires enum-flags type."); + + return enum_cast(value); +} + // Obtains enum value from name. // Returns optional with enum value. -template > -[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t>, BinaryPredicate> { - static_assert(std::is_invocable_r_v, "magic_enum::enum_cast requires bool(char, char) invocable predicate."); +template > +[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t>, BinaryPredicate> { using D = std::decay_t; using U = underlying_type_t; if constexpr (detail::count_v == 0) { return {}; // Empty enum. - } else if constexpr (detail::is_flags_v) { + } else if constexpr (VT == detail::value_type::flags_value && detail::is_flags_v) { auto result = U{0}; while (!value.empty()) { const auto d = detail::find(value, '|'); @@ -1249,21 +1300,14 @@ template > return static_cast(result); } return {}; // Invalid value or out of range. - } else if constexpr (detail::count_v > 0) { - if constexpr (detail::is_default_predicate()) { -#if defined(MAGIC_ENUM_NO_HASH) - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (detail::cmp_equal(value, detail::names_v[i], p)) { - return enum_value(i); - } - } - return {}; // Invalid value or out of range. -#else - return detail::constexpr_switch<&detail::names_v, detail::case_call_t::index>( - [](std::size_t i) { return optional{detail::values_v[i]}; }, - value, - detail::default_result_type_lambda>, - [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); + } else { + if constexpr (detail::is_default_predicate() && detail::has_hash) { +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::names_v, detail::case_call_t::index>( + [](std::size_t i) { return optional{detail::values_v[i]}; }, + value, + detail::default_result_type_lambda>, + [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); #endif } else { for (std::size_t i = 0; i < detail::count_v; ++i) { @@ -1276,113 +1320,84 @@ template > } } -// Checks whether enum contains enumerator with such enum value. -template +// Obtains enum-flags value from name. +// Returns optional with enum-flags value. +template > +[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t>, BinaryPredicate> { + using D = std::decay_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_cast requires enum-flags type."); + + return enum_cast(value, std::move(p)); +} + +// Checks whether enum contains value with such value. +template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t { using D = std::decay_t; using U = underlying_type_t; - return static_cast(enum_cast(static_cast(value))); + return static_cast(enum_cast(static_cast(value))); } -// Checks whether enum contains enumerator with such integer value. +// Checks whether enum-flags contains value with such value. template +[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_contains requires enum-flags type."); + + return enum_contains(value); +} + +// Checks whether enum contains value with such integer value. +template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_t { using D = std::decay_t; - return static_cast(enum_cast(value)); + return static_cast(enum_cast(value)); +} + +// Checks whether enum-flags contains value with such integer value. +template +[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_contains requires enum-flags type."); + + return enum_contains(value); } // Checks whether enum contains enumerator with such name. +template > +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t { + using D = std::decay_t; + + return static_cast(enum_cast(value, std::move(p))); +} + +// Checks whether enum-flags contains enumerator with such name. template > -[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t { - static_assert(std::is_invocable_r_v, "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()) -> detail::enable_if_t { using D = std::decay_t; + static_assert(detail::is_flags_v, "magic_enum::enum_flags_contains requires enum-flags type."); - return static_cast(enum_cast(value, std::forward(p))); + return enum_contains(value, std::move(p)); } -template -constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t { - using D = std::decay_t; - static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); - - return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( - std::forward(lambda), - value, - detail::default_result_type_lambda); -} - -template -constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t { - using D = std::decay_t; - static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); - - return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( - std::forward(lambda), - value, - [&result] { return std::forward(result); }); -} - -template , typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}) -> detail::enable_if_t { - static_assert(std::is_invocable_r_v, "magic_enum::enum_switch requires bool(char, char) invocable predicate."); - using D = std::decay_t; - static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); - - if (const auto v = enum_cast(name, std::forward(p))) { - return enum_switch(std::forward(lambda), *v); - } - return detail::default_result_type_lambda(); -} - -template , typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}) -> detail::enable_if_t { - static_assert(std::is_invocable_r_v, "magic_enum::enum_switch requires bool(char, char) invocable predicate."); - using D = std::decay_t; - static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); - - if (const auto v = enum_cast(name, std::forward(p))) { - return enum_switch(std::forward(lambda), *v, std::forward(result)); - } - return std::forward(result); -} - -template -constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value) -> detail::enable_if_t { - using D = std::decay_t; - static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); - - if (const auto v = enum_cast(value)) { - return enum_switch(std::forward(lambda), *v); - } - return detail::default_result_type_lambda(); -} - -template -constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value, Result&& result) -> detail::enable_if_t { - using D = std::decay_t; - static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); - - if (const auto v = enum_cast(value)) { - return enum_switch(std::forward(lambda), *v, std::forward(result)); - } - return std::forward(result); -} - -template = 0> -constexpr auto enum_for_each(Lambda&& lambda) { +template = 0> +constexpr auto enum_for_each(F&& f) { using D = std::decay_t; static_assert(std::is_enum_v, "magic_enum::enum_for_each requires enum type."); constexpr auto sep = std::make_index_sequence>{}; - if constexpr (detail::all_invocable(sep)) { - return detail::for_each(std::forward(lambda), sep); + if constexpr (detail::all_invocable(sep)) { + return detail::for_each(std::forward(f), sep); } else { static_assert(detail::always_false_v, "magic_enum::enum_for_each requires invocable of all enum value."); } } +template +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& operator<<(std::basic_ostream& o using U = underlying_type_t; if constexpr (detail::supported::value) { - if (const auto name = enum_flags_name(value); !name.empty()) { + if (const auto name = enum_name>>(value); !name.empty()) { for (const auto c : name) { os.put(c); } @@ -1418,7 +1433,7 @@ std::basic_istream& operator>>(std::basic_istream& i std::basic_string s; is >> s; - if (const auto v = enum_cast(s)) { + if (const auto v = enum_cast>>(s)) { value = *v; } else { is.setstate(std::basic_ios::failbit); diff --git a/include/magic_enum_format.hpp b/include/magic_enum_format.hpp index c7968e2..d34421b 100644 --- a/include/magic_enum_format.hpp +++ b/include/magic_enum_format.hpp @@ -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 @@ -62,18 +56,15 @@ namespace magic_enum::customize { template struct std::formatter && magic_enum::customize::enum_format_enabled(), char>> : std::formatter { auto format(E e, format_context& ctx) { + static_assert(std::is_same_v, "formatter requires string_view::value_type type same as char."); using D = std::decay_t; - if constexpr (magic_enum::detail::is_flags_v) { - if (auto name = magic_enum::enum_flags_name(e); !name.empty()) { - return this->std::formatter::format(std::string_view{name.data(), name.size()}, ctx); - } - } else { - if (auto name = magic_enum::enum_name(e); !name.empty()) { - return this->std::formatter::format(std::string_view{name.data(), name.size()}, ctx); + + if constexpr (detail::supported::value) { + if (const auto name = magic_enum::enum_name>>(e); !name.empty()) { + return std::formatter::format(std::string_view{name.data(), name.size()}, ctx); } } - constexpr auto type_name = magic_enum::enum_type_name(); - MAGIC_ENUM_THROW("Type of " + std::string{type_name.data(), type_name.size()} + " enum value: " + std::to_string(magic_enum::enum_integer(e)) + " is not exists."); + return std::formatter::format(std::to_string(magic_enum::enum_integer(e)), ctx); } }; diff --git a/include/magic_enum_switch.hpp b/include/magic_enum_switch.hpp new file mode 100644 index 0000000..dd49615 --- /dev/null +++ b/include/magic_enum_switch.hpp @@ -0,0 +1,139 @@ +// __ __ _ ______ _____ +// | \/ | (_) | ____| / ____|_ _ +// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ +// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| +// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| +// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| +// __/ | https://github.com/Neargye/magic_enum +// |___/ version 0.8.1 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2022 Daniil Goncharov . +// +// 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 +struct identity { + using type = T; +}; + +struct nonesuch {}; + +template > +struct invoke_result : identity {}; + +template +struct invoke_result : std::invoke_result {}; + +template +using invoke_result_t = typename invoke_result::type; + +template +constexpr auto common_invocable(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::invocable_index requires enum type."); + + if constexpr (count_v == 0) { + return identity{}; + } else { + return std::common_type[I]>>...>{}; + } +} + +template +constexpr auto result_type() noexcept { + static_assert(is_enum_v, "magic_enum::detail::result_type requires enum type."); + + constexpr auto seq = std::make_index_sequence>{}; + using R = typename decltype(common_invocable(seq))::type; + if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { + return identity{}; + } else { + return identity{}; + } + } else { + if constexpr (std::is_convertible_v) { + return identity{}; + } else if constexpr (std::is_convertible_v) { + return identity{}; + } else { + return identity{}; + } + } +} + +template , typename R = typename decltype(result_type())::type> +using result_t = std::enable_if_t && !std::is_same_v, R>; + +} // namespace magic_enum::detail + +template > +constexpr decltype(auto) enum_switch(F&& f, E value) { + using D = std::decay_t; + static_assert(std::is_enum_v, "magic_enum::enum_switch requires enum type."); + +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( + std::forward(f), + value, + detail::default_result_type_lambda); +#else + static_assert(detail::has_hash, "magic_enum::enum_switch requires defined MAGIC_ENUM_ENABLE_HASH"); +#endif +} + +template > +constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) { + using D = std::decay_t; + static_assert(std::is_enum_v, "magic_enum::enum_switch requires enum type."); + +#if defined(MAGIC_ENUM_ENABLE_HASH) + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( + std::forward(f), + value, + [&result]() -> R { return std::forward(result); }); +#else + static_assert(detail::has_hash, "magic_enum::enum_switch requires defined MAGIC_ENUM_ENABLE_HASH"); +#endif +} + +} // namespace magic_enum + +template <> +struct std::common_type : magic_enum::detail::identity {}; + +template +struct std::common_type : magic_enum::detail::identity {}; + +template +struct std::common_type : magic_enum::detail::identity {}; + +#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP diff --git a/test/test.cpp b/test/test.cpp index 7517c40..5a394e5 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -545,14 +545,14 @@ TEST_CASE("enum_name") { Color cb = Color::BLUE; REQUIRE(cr_name == "red"); REQUIRE(enum_name(cb) == "BLUE"); - REQUIRE(enum_name(cm[1]) == "GREEN"); - REQUIRE(enum_name(static_cast(0)).empty()); + REQUIRE(enum_name>(cm[1]) == "GREEN"); + REQUIRE(enum_name(static_cast(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::two) == "two"); + REQUIRE(enum_name, Numbers>(Numbers::three) == "three"); REQUIRE(enum_name(Numbers::many).empty()); REQUIRE(enum_name(static_cast(0)).empty()); @@ -1083,22 +1083,6 @@ constexpr std::string_view DoWork() { return "override"; } -TEST_CASE("enum_switch") { - constexpr auto bind_enum_switch = [] (Color c) { - - return enum_switch([](auto val) { - return DoWork(); - }, 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(0)) == "unrecognized"); -} - TEST_CASE("enum_for_each") { SECTION("no return type") { underlying_type_t sum{}; @@ -1207,6 +1191,8 @@ TEST_CASE("multdimensional-switch-case") { #include 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~~~~."); } diff --git a/test/test_aliases.cpp b/test/test_aliases.cpp index 666875b..709033c 100644 --- a/test/test_aliases.cpp +++ b/test/test_aliases.cpp @@ -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 diff --git a/test/test_flags.cpp b/test/test_flags.cpp index 2773158..fb0f28c 100644 --- a/test/test_flags.cpp +++ b/test/test_flags.cpp @@ -93,12 +93,20 @@ TEST_CASE("enum_cast") { REQUIRE(cr.value() == Color::RED); REQUIRE(enum_cast("GREEN").value() == Color::GREEN); REQUIRE(enum_cast("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE); - REQUIRE(enum_cast("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::BLUE | Color::RED)); - REQUIRE(enum_cast("GREEN|RED").value() == (Color::GREEN | Color::RED)); - REQUIRE(enum_cast("GREEN|RED|RED").value() == (Color::GREEN | Color::RED)); + REQUIRE_FALSE(enum_cast("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).has_value()); + REQUIRE_FALSE(enum_cast("GREEN|RED").has_value()); + REQUIRE_FALSE(enum_cast("GREEN|RED|RED").has_value()); REQUIRE_FALSE(enum_cast("GREEN|RED|None").has_value()); REQUIRE_FALSE(enum_cast("None").has_value()); + REQUIRE(enum_flags_cast("GREEN").value() == Color::GREEN); + REQUIRE(enum_flags_cast("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE); + REQUIRE(enum_flags_cast("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::BLUE | Color::RED)); + REQUIRE(enum_flags_cast("GREEN|RED").value() == (Color::GREEN | Color::RED)); + REQUIRE(enum_flags_cast("GREEN|RED|RED").value() == (Color::GREEN | Color::RED)); + REQUIRE_FALSE(enum_flags_cast("GREEN|RED|None").has_value()); + REQUIRE_FALSE(enum_flags_cast("None").has_value()); + constexpr auto no = enum_cast("one"); REQUIRE(no.value() == Numbers::one); REQUIRE(enum_cast("two").value() == Numbers::two); @@ -122,7 +130,7 @@ TEST_CASE("enum_cast") { REQUIRE_FALSE(enum_cast("None").has_value()); #endif - constexpr auto nto = enum_cast("three|one"); + constexpr auto nto = enum_flags_cast("three|one"); REQUIRE(enum_cast("one").value() == number::one); REQUIRE(enum_cast("two").value() == number::two); REQUIRE(enum_cast("three").value() == number::three); @@ -137,11 +145,18 @@ TEST_CASE("enum_cast") { REQUIRE(cr.value() == Color::RED); REQUIRE(enum_cast(2).value() == Color::GREEN); REQUIRE(enum_cast(static_cast(cm[2])).value() == Color::BLUE); - REQUIRE(enum_cast(1 | 2).value() == (Color::GREEN | Color::RED)); - REQUIRE(enum_cast(1 | 2 | 1).value() == (Color::GREEN | Color::RED)); + REQUIRE_FALSE(enum_cast(1 | 2).has_value()); + REQUIRE_FALSE(enum_cast(1 | 2 | 1).has_value()); REQUIRE_FALSE(enum_cast(1 | 2 | 8).has_value()); REQUIRE_FALSE(enum_cast(0).has_value()); + REQUIRE(enum_flags_cast(2).value() == Color::GREEN); + REQUIRE(enum_flags_cast(static_cast(cm[2])).value() == Color::BLUE); + REQUIRE(enum_flags_cast(1 | 2).value() == (Color::GREEN | Color::RED)); + REQUIRE(enum_flags_cast(1 | 2 | 1).value() == (Color::GREEN | Color::RED)); + REQUIRE_FALSE(enum_flags_cast(1 | 2 | 8).has_value()); + REQUIRE_FALSE(enum_flags_cast(0).has_value()); + constexpr auto no = enum_cast(2); REQUIRE(no.value() == Numbers::one); REQUIRE(enum_cast(4).value() == Numbers::two); @@ -166,7 +181,7 @@ TEST_CASE("enum_cast") { REQUIRE_FALSE(enum_cast(0).has_value()); #endif - constexpr auto nto = enum_cast(2 | 8); + constexpr auto nto = enum_flags_cast(2 | 8); REQUIRE(enum_cast(1 << 1).value() == number::one); REQUIRE(enum_cast(1 << 2).value() == number::two); REQUIRE(enum_cast(1 << 3).value() == number::three); @@ -230,11 +245,20 @@ TEST_CASE("enum_contains") { REQUIRE(cr); REQUIRE(enum_contains(cg)); REQUIRE(enum_contains(cm[2])); - REQUIRE(enum_contains(Color::RED | Color::GREEN)); - REQUIRE(enum_contains(Color::RED | Color::GREEN | Color::GREEN)); + REQUIRE(enum_contains>(Color::RED | Color::GREEN)); + REQUIRE(enum_contains>(Color::RED | Color::GREEN | Color::GREEN)); + REQUIRE_FALSE(enum_contains(Color::RED | Color::GREEN)); + REQUIRE_FALSE(enum_contains(Color::RED | Color::GREEN | Color::GREEN)); REQUIRE_FALSE(enum_contains(Color::RED | Color{8})); REQUIRE_FALSE(enum_contains(static_cast(0))); + REQUIRE(enum_flags_contains(cg)); + REQUIRE(enum_flags_contains(cm[2])); + REQUIRE(enum_flags_contains(Color::RED | Color::GREEN)); + REQUIRE(enum_flags_contains(Color::RED | Color::GREEN | Color::GREEN)); + REQUIRE_FALSE(enum_flags_contains(Color::RED | Color{8})); + REQUIRE_FALSE(enum_flags_contains(static_cast(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::two)); REQUIRE(enum_contains(number::one)); REQUIRE(enum_contains(number::four)); - REQUIRE(nto); + REQUIRE_FALSE(nto); REQUIRE_FALSE(enum_contains(static_cast(0))); + + REQUIRE(enum_flags_contains(number::three | number::one)); } SECTION("integer") { REQUIRE(enum_contains(1)); REQUIRE(enum_contains(2)); - REQUIRE(enum_contains(4)); - REQUIRE(enum_contains(1 | 2)); - REQUIRE(enum_contains(1 | 2 | 1)); + REQUIRE(enum_contains(4)); + REQUIRE(enum_contains>(1 | 2)); + REQUIRE(enum_contains>(1 | 2 | 1)); + REQUIRE_FALSE(enum_contains(1 | 2)); + REQUIRE_FALSE(enum_contains(1 | 2 | 1)); REQUIRE_FALSE(enum_contains(1 | 2 | 8)); REQUIRE_FALSE(enum_contains(0)); + REQUIRE(enum_flags_contains(1)); + REQUIRE(enum_flags_contains(2)); + REQUIRE(enum_flags_contains(4)); + REQUIRE(enum_flags_contains(1 | 2)); + REQUIRE(enum_flags_contains(1 | 2 | 1)); + REQUIRE_FALSE(enum_flags_contains(1 | 2 | 8)); + REQUIRE_FALSE(enum_flags_contains(0)); + constexpr auto no = enum_contains(1 << 1); REQUIRE(no); REQUIRE(enum_contains(1 << 2)); @@ -305,9 +341,9 @@ TEST_CASE("enum_contains") { REQUIRE(enum_contains(1 << 2)); REQUIRE(enum_contains(1 << 3)); REQUIRE(enum_contains(1 << 4)); - REQUIRE(enum_contains(8 | 2 | 16)); - REQUIRE(enum_contains(8 | 16 | 16)); - REQUIRE(nto); + REQUIRE_FALSE(enum_contains(8 | 2 | 16)); + REQUIRE_FALSE(enum_contains(8 | 16 | 16)); + REQUIRE_FALSE(nto); REQUIRE_FALSE(enum_contains(8 | 64)); REQUIRE_FALSE(enum_contains(0)); } @@ -316,13 +352,24 @@ TEST_CASE("enum_contains") { constexpr auto cr = "RED"; REQUIRE(enum_contains(cr)); REQUIRE(enum_contains("GREEN")); - REQUIRE(enum_contains("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); - REQUIRE(enum_contains("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); - REQUIRE(enum_contains("GREEN|RED")); - REQUIRE(enum_contains("GREEN|RED|RED")); + REQUIRE(enum_contains("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); + REQUIRE(enum_contains>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); + REQUIRE(enum_contains>("GREEN|RED")); + REQUIRE(enum_contains>("GREEN|RED|RED")); + REQUIRE_FALSE(enum_contains("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); + REQUIRE_FALSE(enum_contains("GREEN|RED")); + REQUIRE_FALSE(enum_contains("GREEN|RED|RED")); REQUIRE_FALSE(enum_contains("GREEN|RED|None")); REQUIRE_FALSE(enum_contains("None")); + REQUIRE(enum_flags_contains("GREEN")); + REQUIRE(enum_flags_contains("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); + REQUIRE(enum_flags_contains("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); + REQUIRE(enum_flags_contains("GREEN|RED")); + REQUIRE(enum_flags_contains("GREEN|RED|RED")); + REQUIRE_FALSE(enum_flags_contains("GREEN|RED|None")); + REQUIRE_FALSE(enum_flags_contains("None")); + constexpr auto no = std::string_view{"one"}; REQUIRE(enum_contains(no)); REQUIRE(enum_contains("two")); @@ -351,8 +398,10 @@ TEST_CASE("enum_contains") { REQUIRE(enum_contains("two")); REQUIRE(enum_contains("three")); REQUIRE(enum_contains("four")); - REQUIRE(nto); + REQUIRE_FALSE(nto); REQUIRE_FALSE(enum_contains("None")); + + REQUIRE(enum_flags_contains("three|one")); } }