diff --git a/README.md b/README.md index c91a52e..c4459f6 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ enum class Color { RED = 2, BLUE = 4, GREEN = 8 }; * 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): // ... + switch (magic_enum::enum_fuse(color, direction)) { + case magic_enum::enum_fuse(Color::RED, Directions::Up): // ... + case magic_enum::enum_fuse(Color::BLUE, Directions::Down): // ... // ... } ``` diff --git a/doc/reference.md b/doc/reference.md index 7e4094d..867e347 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -329,8 +329,8 @@ constexpr string_view enum_type_name() noexcept; ## `enum_fuse` ```cpp -template -[[nodiscard]] constexpr size_t enum_fuse(Es ... values); +template +[[nodiscard]] constexpr std::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. @@ -338,9 +338,9 @@ template * Examples ```cpp - switch (magic_enum::enum_fuse(color1, color2)) { - case magic_enum::enum_fuse(RED, BLUE): // ... - case magic_enum::enum_fuse(RED, RED): // ... + switch (magic_enum::enum_fuse(color, direction)) { + case magic_enum::enum_fuse(Color::RED, Directions::Up): // ... + case magic_enum::enum_fuse(Color::BLUE, Directions::Down): // ... // ... } ``` diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index bb63bac..6ac75ee 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -324,6 +324,15 @@ constexpr I log2(I value) noexcept { return ret; } +constexpr std::size_t cantor_pair(size_t v1, size_t v2) noexcept { + return (((v1 + v2) * (v1 + v2 + 1)) >> 1) + v2; +} + +template +constexpr std::size_t cantor_pair(std::size_t v1, std::size_t head, Ts... tail) noexcept { + return cantor_pair(cantor_pair(v1, head), tail...); +} + template inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; @@ -609,15 +618,6 @@ 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. @@ -887,11 +887,17 @@ template return {}; // Invalid value or out of range. } -template -[[nodiscard]] constexpr size_t enum_fuse(Es ... enums) { +// 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. +template +[[nodiscard]] constexpr auto enum_fuse(Es... values) -> std::enable_if_t<(std::is_enum_v> && ...), std::size_t>{ static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 enums"); +#ifdef __cpp_lib_is_constant_evaluated + if (std::is_constant_evaluated() && !(enum_index(values).has_value() && ...)) { + throw std::logic_error("magic_enum::enum_fuse accepts only in-range enum values"); + } +#endif // Add 1 to prevent matching 2D fusions with 3D fusions etc. - return detail::cantor_pair((enum_index(enums).value() + 1)...); + return assert((enum_index(values).has_value() && ...)), detail::cantor_pair((enum_index(values).value() + 1)...); } // Checks whether enum contains enumerator with such enum value. diff --git a/test/test.cpp b/test/test.cpp index 3540924..348ff30 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1014,32 +1014,28 @@ 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; +static int switch_case_2d(Color color, Directions direction) { + switch (magic_enum::enum_fuse(color, direction)) { + 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; +static int switch_case_3d(Color color, Directions direction, Index index) { + switch (magic_enum::enum_fuse(color, direction, 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; } }