diff --git a/README.md b/README.md index 0d472a0..fecd1c5 100644 --- a/README.md +++ b/README.md @@ -184,8 +184,10 @@ If you like this project, please consider donating to one of the funds that help }; magic_enum::enum_flags_name(Directions::Up | Directions::Right); // -> "Directions::Up|Directions::Right" + magic_enum::enum_flags_name(Directions::Up | Directions::Right, ','); // -> "Directions::Up,Directions::Right" magic_enum::enum_flags_contains(Directions::Up | Directions::Right); // -> true magic_enum::enum_flags_cast(3); // -> "Directions::Left|Directions::Down" + magic_enum::enum_flags_cast("Left,Down", ','); // -> Directions::Left|Directions::Down ``` * Enum type name diff --git a/doc/reference.md b/doc/reference.md index 4fc0f4b..c4cdfe8 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -502,16 +502,13 @@ constexpr auto enum_for_each(Lambda&& lambda); ```cpp template -string enum_flags_name(E value); +string enum_flags_name(E value, char_type sep); 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); +constexpr optional enum_flags_cast(string_view value, char_type sep, BinaryPredicate p) noexcept(is_nothrow_invocable_v); template constexpr bool enum_flags_contains(E value) noexcept; @@ -519,11 +516,8 @@ 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 bool enum_flags_contains(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v); +constexpr bool enum_flags_contains(string_view value, char_type sep, BinaryPredicate p) noexcept(is_nothrow_invocable_v); ``` * Defined in header `` @@ -558,11 +552,14 @@ constexpr bool enum_flags_contains(string_view value, BinaryPredicate p) noexcep magic_enum::enum_flags_name(Directions::Up | Directions::Right); // -> "Directions::Up|Directions::Right" magic_enum::enum_flags_name(Directions::LeftAndDown); // -> "Directions::Left|Directions::Down" + magic_enum::enum_flags_name(Directions::Up | Directions::Right, ','); // -> "Directions::Up,Directions::Right" magic_enum::enum_flags_contains(Directions::Up | Directions::Right); // -> true magic_enum::enum_flags_contains(Directions::LeftAndDown); // -> false magic_enum::enum_flags_cast(3); // -> "Directions::Left|Directions::Down" + magic_enum::enum_flags_cast("Directions::Left|Directions::Down"); // -> Directions::Left|Directions::Down + magic_enum::enum_flags_cast("Left,Down", ','); // -> Directions::Left|Directions::Down magic_enum::enum_flags_test(Left|Down, Down); // -> "true" magic_enum::enum_flags_test(Left|Down, Right); // -> "false" diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp index 532780a..49718ea 100644 --- a/example/enum_flag_example.cpp +++ b/example/enum_flag_example.cpp @@ -54,6 +54,13 @@ int main() { std::cout << "EatsFish|CanFly = " << magic_enum::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400 } + // String name to enum-flags value with custom separator. + auto f2_custom = magic_enum::enum_flags_cast("EatsFish,CanFly", ','); + if (f2_custom.has_value()) { + // Custom separator for enum-flags name. + std::cout << magic_enum::enum_flags_name(f2_custom.value(), ',') << " = " << magic_enum::enum_integer(f2_custom.value()) << std::endl; // CanFly,EatsFish = 1074790400 + } + // Integer value to enum-flags value. auto f3 = magic_enum::enum_cast(1073742848); if (f3.has_value()) { diff --git a/include/magic_enum/magic_enum_flags.hpp b/include/magic_enum/magic_enum_flags.hpp index 54a5507..8b9eb51 100644 --- a/include/magic_enum/magic_enum_flags.hpp +++ b/include/magic_enum/magic_enum_flags.hpp @@ -131,7 +131,7 @@ 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_v) -> detail::enable_if_t>, BinaryPredicate> { +[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] char_type sep = static_cast('|'), [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable_v) -> detail::enable_if_t>, BinaryPredicate> { using D = std::decay_t; using U = underlying_type_t; constexpr auto S = detail::enum_subtype::flags; @@ -143,7 +143,7 @@ template > } else { auto result = U{0}; while (!value.empty()) { - const auto d = detail::find(value, '|'); + const auto d = detail::find(value, sep); const auto s = (d == string_view::npos) ? value : value.substr(0, d); auto f = U{0}; for (std::size_t i = 0; i < detail::count_v; ++i) { @@ -185,10 +185,10 @@ template // Checks whether enum-flags contains enumerator with such name. template > -[[nodiscard]] constexpr auto enum_flags_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable_v) -> detail::enable_if_t { +[[nodiscard]] constexpr auto enum_flags_contains(string_view value, char_type sep = static_cast('|'), BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable_v) -> detail::enable_if_t { using D = std::decay_t; - return static_cast(enum_flags_cast(value, std::move(p))); + return static_cast(enum_flags_cast(value, sep, std::move(p))); } // Checks whether `flags set` contains `flag`. diff --git a/test/test_flags.cpp b/test/test_flags.cpp index d1c566e..35a7034 100644 --- a/test/test_flags.cpp +++ b/test/test_flags.cpp @@ -131,19 +131,26 @@ TEST_CASE("enum_cast") { 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("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()); REQUIRE(enum_flags_cast("A|B|C|D").value() == (CStyleFlags_A | CStyleFlags_B | CStyleFlags_C | CStyleFlags_D)); - REQUIRE(enum_flags_cast("a|e|f", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (CStyleFlags_A | CStyleFlags_E | CStyleFlags_F)); - REQUIRE_FALSE(enum_flags_cast("blue|E|F|C", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).has_value()); + REQUIRE(enum_flags_cast("a|e|f", '|', [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (CStyleFlags_A | CStyleFlags_E | CStyleFlags_F)); + REQUIRE_FALSE(enum_flags_cast("blue|E|F|C", '|', [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).has_value()); REQUIRE(enum_flags_cast("H|I|F|F|F").value() == (CStyleFlags_H | CStyleFlags_I | CStyleFlags_F)); REQUIRE(enum_flags_cast("E|B|C|A").value() == (CStyleFlags_A | CStyleFlags_B | CStyleFlags_C | CStyleFlags_E)); + // Test custom separator + REQUIRE(enum_flags_cast("GREEN,RED", ',').value() == (Color::GREEN | Color::RED)); + REQUIRE(enum_flags_cast("GREEN,RED,BLUE", ',').value() == (Color::GREEN | Color::RED | Color::BLUE)); + REQUIRE(enum_flags_cast("red,green", ',', [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::RED | Color::GREEN)); + REQUIRE_FALSE(enum_flags_cast("GREEN,RED,None", ',').has_value()); + REQUIRE(enum_flags_cast("A,B,C", ',').value() == (CStyleFlags_A | CStyleFlags_B | CStyleFlags_C)); + constexpr auto no = enum_cast("one"); REQUIRE(no.value() == Numbers::one); @@ -341,13 +348,18 @@ TEST_CASE("enum_contains") { 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("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")); + // Test custom separator + REQUIRE(enum_flags_contains("GREEN,RED", ',')); + REQUIRE(enum_flags_contains("green,red", ',', [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); + REQUIRE_FALSE(enum_flags_contains("GREEN,RED,None", ',')); + constexpr auto no = std::string_view{"one"}; REQUIRE(enum_contains(no)); REQUIRE(enum_contains("two")); @@ -534,6 +546,13 @@ TEST_CASE("enum_flags_name") { REQUIRE(enum_flags_name(static_cast(0)).empty()); REQUIRE(enum_flags_name(CStyleFlags_A | CStyleFlags_B | CStyleFlags_C | CStyleFlags_D) == "A|B|C|D"); + + // Test custom separator + REQUIRE(enum_flags_name(Color::RED | Color::GREEN, ',') == "RED,GREEN"); + REQUIRE(enum_flags_name(Color::RED | Color::GREEN | Color::BLUE, ',') == "RED,GREEN,BLUE"); + REQUIRE(enum_flags_name(Numbers::many | Numbers::two, ',') == "two,many"); + REQUIRE(enum_flags_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down, ',') == "Left,Down,Up,Right"); + REQUIRE(enum_flags_name(CStyleFlags_A | CStyleFlags_B | CStyleFlags_C, ',') == "A,B,C"); } TEST_CASE("enum_names") {