diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 4853ad2..d51792d 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -467,8 +467,8 @@ constexpr bool is_sparse() noexcept { static_assert(is_enum_v, "magic_enum::detail::is_sparse requires enum type."); if constexpr (IsFlags) { - auto range_count = std::size_t{0}; - for (auto i = max_v; i != max_v; i <<= static_cast(1), ++range_count) {}; + auto range_count = std::size_t{1}; + for (auto i = min_v; i != max_v; i <<= static_cast(1), ++range_count) {}; return range_count != count_v; } else { @@ -860,7 +860,7 @@ template if constexpr (detail::is_sparse_v) { return assert((index < detail::count_v)), detail::values_v[index]; } else { - constexpr auto min = detail::log2(detail::min_v) - 1; + constexpr auto min = detail::log2(detail::min_v); return assert((index < detail::count_v)), detail::value(index); } @@ -877,7 +877,7 @@ template // Returns string 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_name(E value) -> detail::enable_if_enum_flags_t> { +[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_enum_flags_t> { using D = std::decay_t; using U = underlying_type_t; @@ -947,7 +947,7 @@ template } } - if (check_value == value) { + if (check_value != 0 && check_value == value) { return static_cast(value); } } else { @@ -969,32 +969,41 @@ template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t>> { static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_cast requires bool(char, char) invocable predicate."); using D = std::decay_t; - using U = underlying_type_t; - auto result = U{0}; - while (!value.empty()) { - const auto d = value.find_first_of('|'); - const auto s = (d == std::string_view::npos) ? value : value.substr(0, d); - auto f = U{0}; + if constexpr (Strict) { for (std::size_t i = 0; i < detail::count_v; ++i) { - if (detail::cmp_equal(s, detail::names_v[i], p)) { - f = static_cast(enum_value(i)); - result |= f; - break; + if (detail::cmp_equal(value, detail::names_v[i], p)) { + return enum_value(i); } } - if (f == 0) { - return std::nullopt; - } else { - result |= f; - } - value.remove_prefix((d == std::string_view::npos) ? value.size() : d + 1); - } - - if (result == 0) { return std::nullopt; } else { - return static_cast(result); + using U = underlying_type_t; + auto result = U{0}; + while (!value.empty()) { + const auto d = value.find_first_of('|'); + const auto s = (d == std::string_view::npos) ? value : value.substr(0, d); + auto f = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(s, detail::names_v[i], p)) { + f = static_cast(enum_value(i)); + result |= f; + break; + } + } + if (f == 0) { + return std::nullopt; + } else { + result |= f; + } + value.remove_prefix((d == std::string_view::npos) ? value.size() : d + 1); + } + + if (result == 0) { + return std::nullopt; + } else { + return static_cast(result); + } } } @@ -1018,7 +1027,8 @@ template if (detail::is_pow2(value)) { for (std::size_t i = 0; i < detail::count_v; ++i) { - if (enum_value(i) == value) { + auto v = enum_value(i); + if (v == value) { return i; } } @@ -1033,7 +1043,7 @@ template using D = std::decay_t; using U = underlying_type_t; - return enum_cast(static_cast(value)); + return enum_cast(static_cast(value)).has_value(); } // Checks whether enum-flags contains enumerator with such integer value. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8be3ef8..e310f9b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,8 +18,8 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") check_cxx_compiler_flag(-std=c++20 HAS_CPP20_FLAG) endif() -function(make_test target std) - add_executable(${target} ${SOURCES}) +function(make_test src target std) + add_executable(${target} ${src}) target_compile_options(${target} PRIVATE ${OPTIONS}) target_include_directories(${target} PRIVATE 3rdparty/Catch2) target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME}) @@ -34,12 +34,15 @@ function(make_test target std) add_test(NAME ${target} COMMAND ${target}) endfunction() -make_test(${CMAKE_PROJECT_NAME}-cpp17.t c++17) +make_test(test.cpp test-cpp17 c++17) +make_test(test_flags.cpp test_flags-cpp17 c++17) if(HAS_CPP20_FLAG) - make_test(${CMAKE_PROJECT_NAME}-cpp20.t c++20) + make_test(test.cpp test-cpp20 c++20) + make_test(test_flags.cpp test_flags-cpp20 c++20) endif() if(HAS_CPPLATEST_FLAG) - make_test(${CMAKE_PROJECT_NAME}-cpplatest.t c++latest) + make_test(test.cpp test-cpplatest c++latest) + make_test(test_flags.cpp test_flags-cpplatest c++latest) endif() diff --git a/test/test.cpp b/test/test.cpp index 671fb84..6842cc5 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -573,8 +573,8 @@ TEST_CASE("type_traits") { TEST_CASE("enum_type_name") { REQUIRE(enum_type_name() == "Color"); - REQUIRE(enum_type_name() == "Numbers"); - REQUIRE(enum_type_name() == "Directions"); + REQUIRE(enum_type_name() == "Numbers"); + REQUIRE(enum_type_name() == "Directions"); REQUIRE(enum_type_name() == "number"); } diff --git a/test/test_flags.cpp b/test/test_flags.cpp new file mode 100644 index 0000000..e6b7024 --- /dev/null +++ b/test/test_flags.cpp @@ -0,0 +1,757 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2020 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. + +#define CATCH_CONFIG_MAIN +#include + +#define MAGIC_ENUM_RANGE_MIN -120 +#define MAGIC_ENUM_RANGE_MAX 120 +#include + +#include +#include +#include +#include + +enum class Color { RED = 1, GREEN = 2, BLUE = 4 }; + +enum class Numbers : int { + one = 1 << 1, + two = 1 << 2, + three = 1 << 3, + many = 1 << 30, +}; + +enum Directions : std::uint64_t { + Left = std::uint64_t{1} << 10, + Down = std::uint64_t{1} << 20, + Up = std::uint64_t{1} << 31, + Right = std::uint64_t{1} << 63, +}; + +enum number : unsigned long { + one = 1 << 1, + two = 1 << 2, + three = 1 << 3, + four = 1 << 4, + +#if defined(MAGIC_ENUM_SUPPORTED_ALIASES) + _1 = one, + _2 = two, + _3 = three, + _4 = four +#endif +}; + +namespace magic_enum { +template <> +struct enum_range { + static constexpr int min = 100; + static constexpr int max = 300; +}; +} // namespace magic_enum + +using namespace magic_enum::flags; +using namespace magic_enum::flags::bitwise_operators; + +TEST_CASE("enum_cast") { + SECTION("string") { + constexpr auto cr = enum_cast("RED"); + 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("GREEN|RED|None").has_value()); + REQUIRE_FALSE(enum_cast("None").has_value()); + + constexpr auto no = enum_cast("one"); + REQUIRE(no.value() == Numbers::one); + REQUIRE(enum_cast("two").value() == Numbers::two); + REQUIRE(enum_cast("three").value() == Numbers::three); + REQUIRE(enum_cast("many") == Numbers::many); + REQUIRE_FALSE(enum_cast("None").has_value()); + + constexpr auto dr = enum_cast("Right"); + REQUIRE(enum_cast("Up").value() == Directions::Up); + REQUIRE(enum_cast("Down").value() == Directions::Down); + REQUIRE(dr.value() == Directions::Right); + REQUIRE(enum_cast("Left").value() == Directions::Left); + REQUIRE_FALSE(enum_cast("None").has_value()); + + constexpr auto nto = enum_cast("three|one"); + REQUIRE(enum_cast("one").value() == number::one); + REQUIRE(enum_cast("two").value() == number::two); + REQUIRE(enum_cast("three").value() == number::three); + REQUIRE(enum_cast("four") == number::four); + REQUIRE(nto.value() == (number::three | number::one)); + REQUIRE_FALSE(enum_cast("None").has_value()); + } + + SECTION("strict string") { + constexpr auto cr = enum_cast("RED"); + 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_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()); + + constexpr auto no = enum_cast("one"); + REQUIRE(no.value() == Numbers::one); + REQUIRE(enum_cast("two").value() == Numbers::two); + REQUIRE(enum_cast("three").value() == Numbers::three); + REQUIRE(enum_cast("many") == Numbers::many); + REQUIRE_FALSE(enum_cast("None").has_value()); + + constexpr auto dr = enum_cast("Right"); + REQUIRE(enum_cast("Up").value() == Directions::Up); + REQUIRE(enum_cast("Down").value() == Directions::Down); + REQUIRE(dr.value() == Directions::Right); + REQUIRE(enum_cast("Left").value() == Directions::Left); + REQUIRE_FALSE(enum_cast("None").has_value()); + + constexpr auto nto = enum_cast("three|one"); + REQUIRE(enum_cast("one").value() == number::one); + REQUIRE(enum_cast("two").value() == number::two); + REQUIRE(enum_cast("three").value() == number::three); + REQUIRE(enum_cast("four").value() == number::four); + REQUIRE_FALSE(nto.has_value()); + REQUIRE_FALSE(enum_cast("None").has_value()); + } + + SECTION("integer") { + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + constexpr auto cr = enum_cast(1); + 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 | 8).has_value()); + REQUIRE_FALSE(enum_cast(0).has_value()); + + constexpr auto no = enum_cast(2); + REQUIRE(no.value() == Numbers::one); + REQUIRE(enum_cast(4).value() == Numbers::two); + REQUIRE(enum_cast(8).value() == Numbers::three); + REQUIRE(enum_cast(1 << 30).value() == Numbers::many); + REQUIRE_FALSE(enum_cast(127).has_value()); + REQUIRE_FALSE(enum_cast(0).has_value()); + + constexpr auto dr = enum_cast(std::uint64_t{1} << 63); + REQUIRE(enum_cast(std::uint64_t{1} << 31).value() == Directions::Up); + REQUIRE(enum_cast(std::uint64_t{1} << 20).value() == Directions::Down); + REQUIRE(dr.value() == Directions::Right); + REQUIRE(enum_cast(std::uint64_t{1} << 10).value() == Directions::Left); + REQUIRE_FALSE(enum_cast(0).has_value()); + + constexpr auto nto = enum_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); + REQUIRE(enum_cast(1 << 4).value() == number::four); + REQUIRE(nto.value() == (number::three | number::one)); + REQUIRE_FALSE(enum_cast(0).has_value()); + } + + SECTION("strict integer") { + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + constexpr auto cr = enum_cast(1); + REQUIRE(cr.value() == Color::RED); + REQUIRE(enum_cast(2).value() == Color::GREEN); + REQUIRE(enum_cast(static_cast(cm[2])).value() == Color::BLUE); + 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()); + + constexpr auto no = enum_cast(2); + REQUIRE(no.value() == Numbers::one); + REQUIRE(enum_cast(4).value() == Numbers::two); + REQUIRE(enum_cast(8).value() == Numbers::three); + REQUIRE(enum_cast(1 << 30).value() == Numbers::many); + REQUIRE_FALSE(enum_cast(127).has_value()); + REQUIRE_FALSE(enum_cast(0).has_value()); + + constexpr auto dr = enum_cast(std::uint64_t{1} << 63); + REQUIRE(enum_cast(std::uint64_t{1} << 31).value() == Directions::Up); + REQUIRE(enum_cast(std::uint64_t{1} << 20).value() == Directions::Down); + REQUIRE(dr.value() == Directions::Right); + REQUIRE(enum_cast(std::uint64_t{1} << 10).value() == Directions::Left); + REQUIRE_FALSE(enum_cast(0).has_value()); + + constexpr auto nto = enum_cast(2 | 8); + REQUIRE(enum_cast(1 << 1).value() == number::one); + REQUIRE(enum_cast(1 << 2).value() == number::two); + REQUIRE(enum_cast(1 << 3) == number::three); + REQUIRE(enum_cast(1 << 4) == number::four); + REQUIRE_FALSE(nto.has_value()); + REQUIRE_FALSE(enum_cast(0).has_value()); + } +} + +TEST_CASE("enum_index") { + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + constexpr auto cr = enum_index(Color::RED); + Color cg = Color::GREEN; + REQUIRE(cr.value() == 0); + REQUIRE(enum_index(cg).value() == 1); + REQUIRE(enum_index(cm[2]).value() == 2); + REQUIRE_FALSE(enum_index(Color::RED | Color::GREEN).has_value()); + REQUIRE_FALSE(enum_index(Color::RED | Color::GREEN | Color::RED).has_value()); + REQUIRE_FALSE(enum_index(Color::RED | Color{8}).has_value()); + REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); + + constexpr auto no = enum_index(Numbers::one); + REQUIRE(no.value() == 0); + REQUIRE(enum_index(Numbers::two).value() == 1); + REQUIRE(enum_index(Numbers::three).value() == 2); + REQUIRE(enum_index(Numbers::many).value() == 3); + REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); + + constexpr auto dr = enum_index(Directions::Right); + Directions dl = Directions::Left; + REQUIRE(enum_index(dl).value() == 0); + REQUIRE(enum_index(Directions::Down).value() == 1); + REQUIRE(enum_index(Directions::Up).value() == 2); + REQUIRE(dr.value() == 3); + REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); + + constexpr auto nto = enum_index(number::three | number::one); + REQUIRE(enum_index(number::one).value() == 0); + REQUIRE(enum_index(number::two).value() == 1); + REQUIRE(enum_index(number::three).value() == 2); + REQUIRE(enum_index(number::four).value() == 3); + REQUIRE_FALSE(nto.has_value()); + REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); +} + +TEST_CASE("enum_contains") { + SECTION("value") { + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + constexpr auto cr = enum_contains(Color::RED); + Color cg = Color::GREEN; + REQUIRE(cr); + REQUIRE(enum_contains(cg)); + REQUIRE(enum_contains(cm[2])); + REQUIRE(enum_contains(Color::RED | Color::GREEN)); + REQUIRE(enum_contains(Color::RED | Color::GREEN | Color::GREEN)); + REQUIRE_FALSE(enum_contains(Color::RED | Color{8})); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto no = enum_contains(Numbers::one); + REQUIRE(no); + REQUIRE(enum_contains(Numbers::two)); + REQUIRE(enum_contains(Numbers::three)); + REQUIRE(enum_contains(Numbers::many)); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto dr = enum_contains(Directions::Right); + Directions dl = Directions::Left; + REQUIRE(enum_contains(dl)); + REQUIRE(enum_contains(Directions::Down)); + REQUIRE(enum_contains(Directions::Up)); + REQUIRE(dr); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto nto = enum_contains(number::three | number::one); + REQUIRE(enum_contains(number::one)); + REQUIRE(enum_contains(number::two)); + REQUIRE(enum_contains(number::one)); + REQUIRE(enum_contains(number::four)); + REQUIRE(nto); + REQUIRE_FALSE(enum_contains(static_cast(0))); + } + + SECTION("strict value") { + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + constexpr auto cr = enum_contains(Color::RED); + Color cg = Color::GREEN; + REQUIRE(cr); + REQUIRE(enum_contains(cg)); + REQUIRE(enum_contains(cm[2])); + REQUIRE_FALSE(enum_contains(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))); + + constexpr auto no = enum_contains(Numbers::one); + REQUIRE(no); + REQUIRE(enum_contains(Numbers::two)); + REQUIRE(enum_contains(Numbers::three)); + REQUIRE(enum_contains(Numbers::many)); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto dr = enum_contains(Directions::Right); + Directions dl = Directions::Left; + REQUIRE(enum_contains(dl)); + REQUIRE(enum_contains(Directions::Down)); + REQUIRE(enum_contains(Directions::Up)); + REQUIRE(dr); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto nto = enum_contains(number::three | number::one); + REQUIRE(enum_contains(number::one)); + REQUIRE(enum_contains(number::two)); + REQUIRE(enum_contains(number::one)); + REQUIRE(enum_contains(number::four)); + REQUIRE_FALSE(nto); + REQUIRE_FALSE(enum_contains(static_cast(0))); + } + + SECTION("integer") { + REQUIRE(enum_contains(1)); + REQUIRE(enum_contains(2)); + REQUIRE(enum_contains(4)); + 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)); + + constexpr auto no = enum_contains(1 << 1); + REQUIRE(no); + REQUIRE(enum_contains(1 << 2)); + REQUIRE(enum_contains(1 << 3)); + REQUIRE(enum_contains(1 << 30)); + + constexpr auto dr = enum_contains(std::uint64_t{1} << 63); + REQUIRE(dr); + REQUIRE(enum_contains(std::uint64_t{1} << 10)); + REQUIRE(enum_contains(std::uint64_t{1} << 20)); + REQUIRE(enum_contains(std::uint64_t{1} << 31)); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto nto = enum_contains(8 | 2); + REQUIRE(enum_contains(1 << 1)); + REQUIRE(enum_contains(1 << 2)); + REQUIRE(enum_contains(1 << 3)); + REQUIRE(enum_contains(1 << 4)); + 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)); + } + + SECTION("strict 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_FALSE(enum_contains(1 | 2 | 8)); + REQUIRE_FALSE(enum_contains(0)); + + constexpr auto no = enum_contains(1 << 1); + REQUIRE(no); + REQUIRE(enum_contains(1 << 2)); + REQUIRE(enum_contains(1 << 3)); + REQUIRE(enum_contains(1 << 30)); + + constexpr auto dr = enum_contains(std::uint64_t{1} << 63); + REQUIRE(dr); + REQUIRE(enum_contains(std::uint64_t{1} << 10)); + REQUIRE(enum_contains(std::uint64_t{1} << 20)); + REQUIRE(enum_contains(std::uint64_t{1} << 31)); + REQUIRE_FALSE(enum_contains(static_cast(0))); + + constexpr auto nto = enum_contains(8 | 2); + REQUIRE(enum_contains(1 << 1)); + 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 | 64)); + REQUIRE_FALSE(enum_contains(0)); + } + + SECTION("string") { + 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_FALSE(enum_contains("GREEN|RED|None")); + REQUIRE_FALSE(enum_contains("None")); + + constexpr auto no = std::string_view{"one"}; + REQUIRE(enum_contains(no)); + REQUIRE(enum_contains("two")); + REQUIRE(enum_contains("three")); + REQUIRE(enum_contains("many")); + REQUIRE_FALSE(enum_contains("None")); + + auto dr = std::string{"Right"}; + REQUIRE(enum_contains("Up")); + REQUIRE(enum_contains("Down")); + REQUIRE(enum_contains(dr)); + REQUIRE(enum_contains("Left")); + REQUIRE_FALSE(enum_contains("None")); + + constexpr auto nto = enum_contains("three|one"); + REQUIRE(enum_contains("one")); + REQUIRE(enum_contains("two")); + REQUIRE(enum_contains("three")); + REQUIRE(enum_contains("four")); + REQUIRE(nto); + REQUIRE_FALSE(enum_contains("None")); + } + + SECTION("strict string") { + 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_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")); + + constexpr auto no = std::string_view{"one"}; + REQUIRE(enum_contains(no)); + REQUIRE(enum_contains("two")); + REQUIRE(enum_contains("three")); + REQUIRE(enum_contains("many")); + REQUIRE_FALSE(enum_contains("None")); + + auto dr = std::string{"Right"}; + REQUIRE(enum_contains("Up")); + REQUIRE(enum_contains("Down")); + REQUIRE(enum_contains(dr)); + REQUIRE(enum_contains("Left")); + REQUIRE_FALSE(enum_contains("None")); + + constexpr auto nto = enum_contains("three|one"); + REQUIRE(enum_contains("one")); + REQUIRE(enum_contains("two")); + REQUIRE(enum_contains("three")); + REQUIRE(enum_contains("four")); + REQUIRE_FALSE(nto); + REQUIRE_FALSE(enum_contains("None")); + } +} + +TEST_CASE("enum_value") { + constexpr auto cr = enum_value(0); + REQUIRE(cr == Color::RED); + REQUIRE(enum_value(1) == Color::GREEN); + REQUIRE(enum_value(2) == Color::BLUE); + + constexpr auto no = enum_value(0); + REQUIRE(no == Numbers::one); + REQUIRE(enum_value(1) == Numbers::two); + REQUIRE(enum_value(2) == Numbers::three); + REQUIRE(enum_value(3) == Numbers::many); + + constexpr auto dr = enum_value(3); + REQUIRE(enum_value(0) == Directions::Left); + REQUIRE(enum_value(1) == Directions::Down); + REQUIRE(enum_value(2) == Directions::Up); + REQUIRE(dr == Directions::Right); + + constexpr auto nt = enum_value(2); + REQUIRE(enum_value(0) == number::one); + REQUIRE(enum_value(1) == number::two); + REQUIRE(nt == number::three); + REQUIRE(enum_value(3) == number::four); +} + +TEST_CASE("enum_values") { + REQUIRE(std::is_same_v()), const std::array&>); + + constexpr auto s1 = enum_values(); + REQUIRE(s1 == std::array{{Color::RED, Color::GREEN, Color::BLUE}}); + + auto s2 = enum_values(); + REQUIRE(s2 == std::array{{Numbers::one, Numbers::two, Numbers::three, Numbers::many}}); + + constexpr auto s3 = enum_values(); + REQUIRE(s3 == std::array{{Directions::Left, Directions::Down, Directions::Up, Directions::Right}}); + + auto s4 = enum_values(); + REQUIRE(s4 == std::array{{number::one, number::two, number::three, number::four}}); +} + +TEST_CASE("enum_count") { + constexpr auto s1 = enum_count(); + REQUIRE(s1 == 3); + + auto s2 = enum_count(); + REQUIRE(s2 == 4); + + constexpr auto s3 = enum_count(); + REQUIRE(s3 == 4); + + auto s4 = enum_count(); + REQUIRE(s4 == 4); +} + +TEST_CASE("enum_name") { + SECTION("automatic storage") { + constexpr Color cr = Color::RED; + auto cr_name = enum_name(cr); + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + Color cb = Color::BLUE; + REQUIRE(cr_name == "RED"); + REQUIRE(enum_name(cb) == "BLUE"); + REQUIRE(enum_name(cm[1]) == "GREEN"); + REQUIRE(enum_name(Color::RED | Color{0}) == "RED"); + REQUIRE(enum_name(Color::RED | Color::GREEN) == "RED|GREEN"); + REQUIRE(enum_name(Color::RED | Color{8}).empty()); + REQUIRE(enum_name(static_cast(0)).empty()); + + constexpr Numbers no = Numbers::one; + 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::many) == "many"); + REQUIRE(enum_name(Numbers::many | Numbers::two) == "two|many"); + REQUIRE(enum_name(static_cast(0)).empty()); + + constexpr Directions dr = Directions::Right; + auto dr_name = enum_name(dr); + Directions du = Directions::Up; + REQUIRE(enum_name(du) == "Up"); + REQUIRE(enum_name(Directions::Down) == "Down"); + REQUIRE(dr_name == "Right"); + REQUIRE(enum_name(Directions::Left) == "Left"); + REQUIRE(enum_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down) == "Left|Down|Up|Right"); + REQUIRE(enum_name(static_cast(0)).empty()); + + constexpr number nto = number::three | number::one; + auto nto_name = enum_name(nto); + REQUIRE(enum_name(number::one) == "one"); + REQUIRE(enum_name(number::two) == "two"); + REQUIRE(enum_name(number::three) == "three"); + REQUIRE(enum_name(number::four) == "four"); + REQUIRE(nto_name == "one|three"); + REQUIRE(enum_name(static_cast(0)).empty()); + } + + SECTION("strict automatic storage") { + constexpr Color cr = Color::RED; + constexpr auto cr_name = enum_name(cr); + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + Color cb = Color::BLUE; + REQUIRE(cr_name == "RED"); + REQUIRE(enum_name(cb) == "BLUE"); + REQUIRE(enum_name(cm[1]) == "GREEN"); + REQUIRE(enum_name(Color::RED | Color{0}) == "RED"); + REQUIRE(enum_name(Color::RED | Color::GREEN).empty()); + REQUIRE(enum_name(Color::RED | Color{8}).empty()); + 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::many) == "many"); + REQUIRE(enum_name(Numbers::many | Numbers::two).empty()); + REQUIRE(enum_name(static_cast(0)).empty()); + + constexpr Directions dr = Directions::Right; + constexpr auto dr_name = enum_name(dr); + Directions du = Directions::Up; + REQUIRE(enum_name(du) == "Up"); + REQUIRE(enum_name(Directions::Down) == "Down"); + REQUIRE(dr_name == "Right"); + REQUIRE(enum_name(Directions::Left) == "Left"); + REQUIRE(enum_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down).empty()); + REQUIRE(enum_name(static_cast(0)).empty()); + + constexpr number nto = number::three | number::one; + auto nto_name = enum_name(nto); + REQUIRE(enum_name(number::one) == "one"); + REQUIRE(enum_name(number::two) == "two"); + REQUIRE(enum_name(number::three) == "three"); + REQUIRE(enum_name(number::four) == "four"); + REQUIRE(nto_name.empty()); + REQUIRE(enum_name(static_cast(0)).empty()); + } +} + +TEST_CASE("enum_names") { + REQUIRE(std::is_same_v()), const std::array&>); + + constexpr auto s1 = enum_names(); + REQUIRE(s1 == std::array{{"RED", "GREEN", "BLUE"}}); + + auto s2 = enum_names(); + REQUIRE(s2 == std::array{{"one", "two", "three", "many"}}); + + constexpr auto s3 = enum_names(); + REQUIRE(s3 == std::array{{"Left", "Down", "Up", "Right"}}); + + auto s4 = enum_names(); + REQUIRE(s4 == std::array{{"one", "two", "three", "four"}}); +} + +TEST_CASE("enum_entries") { + REQUIRE(std::is_same_v()), const std::array, 3>&>); + + constexpr auto s1 = enum_entries(); + REQUIRE(s1 == std::array, 3>{{{Color::RED, "RED"}, {Color::GREEN, "GREEN"}, {Color::BLUE, "BLUE"}}}); + + auto s2 = enum_entries(); + REQUIRE(s2 == std::array, 4>{{{Numbers::one, "one"}, {Numbers::two, "two"}, {Numbers::three, "three"}, {Numbers::many, "many"}}}); + + constexpr auto s3 = enum_entries(); + REQUIRE(s3 == std::array, 4>{{{Directions::Left, "Left"}, {Directions::Down, "Down"}, {Directions::Up, "Up"}, {Directions::Right, "Right"}}}); + + auto s4 = enum_entries(); + REQUIRE(s4 == std::array, 4>{{{number::one, "one"}, {number::two, "two"}, {number::three, "three"}, {number::four, "four"}}}); +} + +TEST_CASE("ostream_operators") { + auto test_ostream = [](auto e, std::string_view name) { + using namespace magic_enum::flags::ostream_operators; + std::stringstream ss; + ss << e; + REQUIRE(ss.str() == name); + }; + + test_ostream(std::make_optional(Color::RED), "RED"); + test_ostream(Color::GREEN, "GREEN"); + test_ostream(Color::BLUE, "BLUE"); + test_ostream(Color::BLUE | Color::RED, "RED|BLUE"); + test_ostream(Color::BLUE | Color::RED | Color::RED, "RED|BLUE"); + test_ostream(static_cast(0), "0"); + test_ostream(std::make_optional(static_cast(0)), "0"); + + test_ostream(std::make_optional(Numbers::one), "one"); + test_ostream(Numbers::two, "two"); + test_ostream(Numbers::three, "three"); + test_ostream(Numbers::many, "many"); + test_ostream(static_cast(0), "0"); + test_ostream(std::make_optional(static_cast(0)), "0"); + + test_ostream(std::make_optional(Directions::Up), "Up"); + test_ostream(Directions::Down, "Down"); + test_ostream(Directions::Right, "Right"); + test_ostream(Directions::Left, "Left"); + test_ostream(Directions::Right | Directions::Left, "Left|Right"); + test_ostream(static_cast(0), "0"); + test_ostream(std::make_optional(static_cast(0)), "0"); + + test_ostream(std::make_optional(number::one), "one"); + test_ostream(number::two, "two"); + test_ostream(number::three, "three"); + test_ostream(number::four, "four"); + test_ostream(number::four | number::one, "one|four"); + test_ostream(static_cast(0), "0"); + test_ostream(std::make_optional(static_cast(0)), "0"); +} + +TEST_CASE("bitwise_operators") { + SECTION("operator^") { + REQUIRE(enum_integer(~Color::RED) == ~enum_integer(Color::RED)); + REQUIRE(enum_integer(~Numbers::one) == ~enum_integer(Numbers::one)); + REQUIRE(enum_integer(~Directions::Up) == ~enum_integer(Directions::Up)); + REQUIRE(enum_integer(~number::one) == ~enum_integer(number::one)); + } + + SECTION("operator|") { + REQUIRE(enum_integer(Color::RED | Color::BLUE) == (enum_integer(Color::RED) | enum_integer(Color::BLUE))); + REQUIRE(enum_integer(Numbers::one | Numbers::two) == (enum_integer(Numbers::one) | enum_integer(Numbers::two))); + REQUIRE(enum_integer(Directions::Up | Directions::Down) == (enum_integer(Directions::Up) | enum_integer(Directions::Down))); + REQUIRE(enum_integer(number::one | number::two) == (enum_integer(number::one) | enum_integer(number::two))); + } + + SECTION("operator&") { + REQUIRE(enum_integer(Color::RED & Color::BLUE) == (enum_integer(Color::RED) & enum_integer(Color::BLUE))); + REQUIRE(enum_integer(Numbers::one & Numbers::two) == (enum_integer(Numbers::one) & enum_integer(Numbers::two))); + REQUIRE(enum_integer(Directions::Up & Directions::Down) == (enum_integer(Directions::Up) & enum_integer(Directions::Down))); + REQUIRE(enum_integer(number::one & number::two) == (enum_integer(number::one) & enum_integer(number::two))); + } + + SECTION("operator^") { + REQUIRE(enum_integer(Color::RED ^ Color::BLUE) == (enum_integer(Color::RED) ^ enum_integer(Color::BLUE))); + REQUIRE(enum_integer(Numbers::one ^ Numbers::two) == (enum_integer(Numbers::one) ^ enum_integer(Numbers::two))); + REQUIRE(enum_integer(Directions::Up ^ Directions::Down) == (enum_integer(Directions::Up) ^ enum_integer(Directions::Down))); + REQUIRE(enum_integer(number::one ^ number::two) == (enum_integer(number::one) ^ enum_integer(number::two))); + } + + SECTION("operator|=") { + Color x1 = Color::RED; + x1 |= Color::BLUE; + REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) | enum_integer(Color::BLUE))); + + Numbers x2 = Numbers::one; + x2 |= Numbers::two; + REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) | enum_integer(Numbers::two))); + + Directions x3 = Directions::Up; + x3 |= Directions::Down; + REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) | enum_integer(Directions::Down))); + + number x4 = number::one; + x4 |= number::two; + REQUIRE(enum_integer(x4) == (enum_integer(number::one) | enum_integer(number::two))); + } + + SECTION("operator&=") { + Color x1 = Color::RED; + x1 &= Color::BLUE; + REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) & enum_integer(Color::BLUE))); + + Numbers x2 = Numbers::one; + x2 &= Numbers::two; + REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) & enum_integer(Numbers::two))); + + Directions x3 = Directions::Up; + x3 &= Directions::Down; + REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) & enum_integer(Directions::Down))); + + number x4 = number::one; + x4 &= number::two; + REQUIRE(enum_integer(x4) == (enum_integer(number::one) & enum_integer(number::two))); + } + + SECTION("operator^=") { + Color x1 = Color::RED; + x1 ^= Color::BLUE; + REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) ^ enum_integer(Color::BLUE))); + + Numbers x2 = Numbers::one; + x2 ^= Numbers::two; + REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) ^ enum_integer(Numbers::two))); + + Directions x3 = Directions::Up; + x3 ^= Directions::Down; + REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) ^ enum_integer(Directions::Down))); + + number x4 = number::one; + x4 ^= number::two; + REQUIRE(enum_integer(x4) == (enum_integer(number::one) ^ enum_integer(number::two))); + } +}