diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 1174591..a2b28aa 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -172,12 +172,6 @@ struct range_max : std::integral_constant {}; template struct range_max::max)>> : std::integral_constant::max), customize::enum_range::max> {}; -struct char_equal_to { - constexpr bool operator()(char lhs, char rhs) const noexcept { - return lhs == rhs; - } -}; - template class static_string { public: @@ -236,6 +230,16 @@ constexpr string_view pretty_name(string_view name) noexcept { return {}; // Invalid name. } +template +constexpr auto to_lower([[maybe_unused]] CharType ch) noexcept -> std::enable_if_t, char>, char> { +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + static_assert(!std::is_same_v, "magic_enum::detail::to_lower not supported Non-ASCII feature."); + return {}; +#else + return 'A' <= ch && ch <= 'Z' ? ch - 'A' + 'a' : ch; +#endif +} + constexpr std::size_t find(string_view str, char c) noexcept { #if defined(__clang__) && __clang_major__ < 9 && defined(__GLIBCXX__) || defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) // https://stackoverflow.com/questions/56484834/constexpr-stdstring-viewfind-last-of-doesnt-work-on-clang-8-with-libstdc @@ -264,7 +268,7 @@ constexpr std::array, N> to_array(T (&a)[N], std::index_sequ } template -constexpr bool cmp_equal(string_view lhs, string_view rhs, BinaryPredicate&& p) noexcept(std::is_nothrow_invocable_r_v) { +constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(std::is_nothrow_invocable_r_v) { #if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) // https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html // https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html @@ -272,7 +276,9 @@ constexpr bool cmp_equal(string_view lhs, string_view rhs, BinaryPredicate&& p) #else constexpr bool workaround = false; #endif - constexpr bool custom_predicate = std::negation_v, char_equal_to>>; + constexpr bool custom_predicate = + !std::is_same_v, std::equal_to> && + !std::is_same_v, std::equal_to<>>; if constexpr (custom_predicate || workaround) { if (lhs.size() != rhs.size()) { @@ -288,8 +294,6 @@ constexpr bool cmp_equal(string_view lhs, string_view rhs, BinaryPredicate&& p) return true; } else { - static_cast(p); - return lhs == rhs; } } @@ -803,10 +807,21 @@ template return {}; // Invalid value or out of range. } +// allows you to write magic_enum::enum_cast("bar", magic_enum::case_insensitive); +inline constexpr auto case_insensitive = [](auto lhs, auto rhs) noexcept + -> std::enable_if_t, char> && std::is_same_v, char>, bool> { +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + static_assert(!std::is_same_v, "magic_enum::case_insensitive not supported Non-ASCII feature."); + return {}; +#else + return detail::to_lower(lhs) == detail::to_lower(rhs); +#endif +}; + // Obtains enum value from name. // Returns optional with enum value. -template -[[nodiscard]] constexpr auto enum_cast(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { +template > +[[nodiscard]] constexpr auto enum_cast(string_view value, BinaryPredicate p = {}) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { static_assert(std::is_invocable_r_v, "magic_enum::enum_cast requires bool(char, char) invocable predicate."); using D = std::decay_t; using U = underlying_type_t; @@ -844,15 +859,6 @@ template return {}; // Invalid value or out of range. } -// Obtains enum value from name. -// Returns optional with enum value. -template -[[nodiscard]] constexpr auto enum_cast(string_view value) noexcept -> detail::enable_if_enum_t>> { - using D = std::decay_t; - - return enum_cast(value, detail::char_equal_to{}); -} - // Returns integer value from enum value. template [[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { @@ -898,19 +904,13 @@ template } // Checks whether enum contains enumerator with such name. -template -[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t { +template > +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t { static_assert(std::is_invocable_r_v, "magic_enum::enum_contains requires bool(char, char) invocable predicate."); return enum_cast>(value, std::move_if_noexcept(p)).has_value(); } -// Checks whether enum contains enumerator with such name. -template -[[nodiscard]] constexpr auto enum_contains(string_view value) noexcept -> detail::enable_if_enum_t { - return enum_cast>(value).has_value(); -} - namespace fusion_detail { template diff --git a/test/test.cpp b/test/test.cpp index 2486df8..3ffdd12 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -127,6 +127,12 @@ TEST_CASE("enum_cast") { REQUIRE(lang.value() == Language::日本語); REQUIRE(enum_cast("😃").value() == Language::😃); REQUIRE_FALSE(enum_cast("Französisch").has_value()); +#else // !defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto dr2 = enum_cast("RIGHT", case_insensitive); + REQUIRE(dr2.value() == Directions::Right); + REQUIRE(enum_cast("up", case_insensitive).value() == Directions::Up); + REQUIRE(enum_cast("dOwN", case_insensitive).value() == Directions::Down); + REQUIRE_FALSE(enum_cast("Left-", case_insensitive).has_value()); #endif constexpr auto nt = enum_cast("three"); @@ -372,6 +378,12 @@ TEST_CASE("enum_contains") { REQUIRE(enum_contains(lang)); REQUIRE(enum_contains("😃")); REQUIRE_FALSE(enum_contains("None")); +#else + auto dr2 = std::string{"RIGHT"}; + REQUIRE(enum_contains(dr2, case_insensitive)); + REQUIRE(enum_contains("up", case_insensitive)); + REQUIRE(enum_contains("dOwN", case_insensitive)); + REQUIRE_FALSE(enum_contains("Left-", case_insensitive)); #endif constexpr auto nt = enum_contains("three");