diff --git a/README.md b/README.md index 79fdfb2..c91a52e 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Header-only C++17 library provides static reflection for enums, work with any en * `enum_index` obtains index in enum value sequence from enum value. * `enum_contains` checks whether enum contains enumerator with such value. * `enum_type_name` returns name of enum type. +* `enum_fuse` allows multidimensional switch/cases. * `is_unscoped_enum` checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration). * `is_scoped_enum` checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations). * `underlying_type` improved UB-free "SFINAE-friendly" [underlying_type](https://en.cppreference.com/w/cpp/types/underlying_type). @@ -136,6 +137,16 @@ enum class Color { RED = 2, BLUE = 4, GREEN = 8 }; // color_entries[0].first -> Color::RED // color_entries[0].second -> "RED" ``` + +* Enum fusion for multi-level switch/case statements + + ```cpp + switch (magic_enum::enum_fuse(color1, color2)) { + case magic_enum::enum_fuse(RED, BLUE): // ... + case magic_enum::enum_fuse(RED, RED): // ... + // ... + } + ``` * Ostream operator for enum diff --git a/doc/reference.md b/doc/reference.md index 2584982..7e4094d 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -12,6 +12,7 @@ * [`enum_index` obtains index in enum value sequence from enum value.](#enum_index) * [`enum_contains` checks whether enum contains enumerator with such value.](#enum_contains) * [`enum_type_name` returns type name of enum.](#enum_type_name) +* [`enum_fuse` returns a bijective mix of enum values.](#enum_fuse) * [`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) @@ -325,6 +326,25 @@ constexpr string_view enum_type_name() noexcept; // color_name -> "Color" ``` +## `enum_fuse` + +```cpp +template +[[nodiscard]] constexpr size_t enum_fuse(Es ... values); +``` + +* Returns a bijective mix of several enum values with [Cantor pairing function](https://en.wikipedia.org/wiki/Pairing_function). This can be used to emulate 2D switch/case statements. + +* Examples + + ```cpp + switch (magic_enum::enum_fuse(color1, color2)) { + case magic_enum::enum_fuse(RED, BLUE): // ... + case magic_enum::enum_fuse(RED, RED): // ... + // ... + } + ``` + ## `is_unscoped_enum` ```cpp diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index e4b66cc..bb63bac 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -609,6 +609,15 @@ struct underlying_type {}; template struct underlying_type : std::underlying_type> {}; +constexpr size_t cantor_pair(size_t v1, size_t v2) noexcept { + return (((v1 + v2) * (v1 + v2 + 1)) >> 1) + v2; +} + +template +constexpr size_t cantor_pair(size_t v1, size_t head, Ts ... tail) noexcept { + return cantor_pair(cantor_pair(v1, head), tail...); +} + } // namespace magic_enum::detail // Checks is magic_enum supported compiler. @@ -878,6 +887,13 @@ template return {}; // Invalid value or out of range. } +template +[[nodiscard]] constexpr size_t enum_fuse(Es ... enums) { + static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 enums"); + // Add 1 to prevent matching 2D fusions with 3D fusions etc. + return detail::cantor_pair((enum_index(enums).value() + 1)...); +} + // Checks whether enum contains enumerator with such enum value. template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { diff --git a/test/test.cpp b/test/test.cpp index 193c10b..3540924 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1014,4 +1014,44 @@ TEST_CASE("constexpr_for") { }); } +static int switch_case_2d(Color color, Directions number) +{ + switch (magic_enum::enum_fuse(color, number)) + { + case magic_enum::enum_fuse(Color::RED, Directions::Up): + return 1; + case magic_enum::enum_fuse(Color::BLUE, Directions::Down): + return 2; + default: + return 0; + } +} + +enum class Index { zero = 0, one = 1, two = 2 }; + +static int switch_case_3d(Color color, Directions number, Index index) +{ + switch (magic_enum::enum_fuse(color, number, index)) + { + case magic_enum::enum_fuse(Color::RED, Directions::Up, Index::zero): + return 1; + // model accidental removal of last index, must not match anything + case magic_enum::enum_fuse(Color::BLUE, Directions::Up): + return 2; + default: + return 0; + } +} + +TEST_CASE("multdimensional-switch-case") { + REQUIRE(switch_case_2d(Color::RED, Directions::Up) == 1); + REQUIRE(switch_case_2d(Color::RED, Directions::Down) == 0); + REQUIRE(switch_case_2d(Color::BLUE, Directions::Up) == 0); + REQUIRE(switch_case_2d(Color::BLUE, Directions::Down) == 2); + REQUIRE(switch_case_3d(Color::RED, Directions::Up, Index::zero) == 1); + REQUIRE(switch_case_3d(Color::BLUE, Directions::Up, Index::zero) == 0); + REQUIRE(switch_case_3d(Color::BLUE, Directions::Up, Index::one) == 0); + REQUIRE(switch_case_3d(Color::BLUE, Directions::Up, Index::two) == 0); +} + #endif