From 604054df5ac9434e8aefbba48d88cff13f09c92c Mon Sep 17 00:00:00 2001 From: neargye Date: Sun, 5 Jul 2020 13:18:29 +0500 Subject: [PATCH] wip --- doc/reference.md | 10 +-- include/magic_enum.hpp | 146 ++++++++++++++++++++++++++--------------- 2 files changed, 99 insertions(+), 57 deletions(-) diff --git a/doc/reference.md b/doc/reference.md index cf253e6..7d2c1fe 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -88,7 +88,7 @@ template constexpr array enum_values() noexcept; ``` -* Returns `std::array` with all enum value where `N = number of enum values`, sorted by enum value. +* Returns `std::array` with all enum values where `N = number of enum values`, sorted by enum value. * Examples @@ -141,7 +141,7 @@ template constexpr string_view enum_name() noexcept; ``` -* Returns `std::string_view` with null-terminated string enum name from enum value. +* Returns `std::string_view` with null-terminated string name from enum value. * If enum value does not have name or [out of range](limitations.md), `enum_name(value)` returns empty string. * If enum value does not have name, `enum_name()` occurs the compilation error `"Enum value does not have a name."`. @@ -172,7 +172,7 @@ template constexpr array enum_names() noexcept; ``` -* Returns `std::array` with all string enum name where `N = number of enum values`, sorted by enum value. +* Returns `std::array` with all string names where `N = number of enum values`, sorted by enum value. * Examples @@ -189,7 +189,7 @@ template constexpr array, N> enum_entries() noexcept; ``` -* Returns `std::array, N>` with all `std::pair` (value enum, string enum name) where `N = number of enum values`, sorted by enum value. +* Returns `std::array, N>` with all pairs (enum value, string name) where `N = number of enum values`, sorted by enum value. * Examples @@ -207,7 +207,7 @@ template constexpr optional enum_index() noexcept; ``` -* Obtains index in enum value sequence from enum value. +* Obtains index in enum values from enum value. * Returns `std::optional` with index. diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 81e724f..0c7da1e 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -204,6 +204,31 @@ constexpr bool cmp_less(L lhs, R rhs) noexcept { } } +template +constexpr std::uint8_t log2(T value) noexcept { + if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; + + return log2(static_cast(value)); + } else { + auto ret = std::uint8_t{0}; + for (; value > static_cast(1U); value >>= static_cast(1U), ++ret) {}; + + return ret; + } +} + +template +constexpr bool is_pow2(T x) noexcept { + if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; + + return is_pow2(static_cast(x)); + } else { + return x != 0 && (x & (x - 1)) == 0; + } +} + #if defined(NEARGYE_NAMEOF_HPP) using ::nameof::detail::type_name_v; #else @@ -294,12 +319,12 @@ inline constexpr auto reflected_min_v = reflected_min(); template inline constexpr auto reflected_max_v = reflected_max(); -template > +template > constexpr auto value(std::size_t i) noexcept { static_assert(is_enum_v, "magic_enum::detail::value requires enum type."); if constexpr (IsFlags) { - return static_cast(static_cast(1) << static_cast(i)); + return static_cast(static_cast(1) << static_cast(i + O)); } else { return static_cast(static_cast(i) + O); } @@ -308,13 +333,13 @@ constexpr auto value(std::size_t i) noexcept { template constexpr auto values(std::integer_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); - constexpr std::array valid{{is_valid(I)>()...}}; + constexpr std::array valid{{is_valid(I)>()...}}; constexpr std::size_t count = ((valid[I] ? 1 : 0) + ...); std::array values{}; for (std::size_t i = 0, v = 0; v < count; ++i) { if (valid[i]) { - values[v++] = value(i); + values[v++] = value(i); } } @@ -376,7 +401,7 @@ inline constexpr auto invalid_index_v = (std::numeric_limits>::max)() template constexpr auto indexes(std::integer_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::indexes requires enum type."); - [[maybe_unused]] index_t i = 0; + [[maybe_unused]] auto i = index_t{0}; return std::array, sizeof...(I)>{{(is_valid>() ? i++ : invalid_index_v)...}}; } @@ -410,6 +435,20 @@ inline constexpr auto entries_v = entries(std::make_index_sequence> using entries_t = decltype((entries_v)); +template > +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) {}; + + return range_count != count_v; + } else { + return range_size_v != count_v; + } +} + template inline constexpr bool is_sparse_v = range_size_v != count_v; @@ -453,7 +492,7 @@ struct enable_if_enum { using type = R; using D = std::decay_t; static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(count_v > 0, "magic_enum::flag requires enum flag implementation."); + static_assert(count_v > 0, "magic_enum::flag requires enum-flags implementation."); }; template @@ -543,11 +582,10 @@ template if constexpr (detail::is_sparse_v) { return assert(index < detail::count_v), detail::values_v[index]; } else { - return assert(index < detail::count_v), detail::value>(index); + return assert(index < detail::count_v), detail::value>(index); } } -// Obtains value enum sequence. // Returns std::array with enum values, sorted by enum value. template [[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t> { @@ -556,7 +594,7 @@ template return detail::values_v; } -// Returns string enum name from static storage enum variable. +// Returns string name from static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. template [[nodiscard]] constexpr std::string_view enum_name() noexcept { @@ -568,7 +606,7 @@ template return name; } -// Returns string enum name from enum value. +// Returns string name from enum value. // If enum value does not have name or value out of range, returns empty string. template [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { @@ -581,8 +619,7 @@ template return {}; // Value out of range. } -// Obtains string enum name sequence. -// Returns std::array with string enum names, sorted by enum value. +// Returns std::array with string names, sorted by enum value. template [[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; @@ -590,8 +627,7 @@ template return detail::names_v; } -// Obtains pair (value enum, string enum name) sequence. -// Returns std::array with std::pair (value enum, string enum name), sorted by enum value. +// Returns std::array with pairs (enum value, string name), sorted by enum value. template [[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; @@ -599,7 +635,7 @@ template return detail::entries_v; } -// Obtains enum value from enum string name. +// Obtains enum value from string name. // Returns std::optional with enum value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { @@ -615,6 +651,8 @@ template return std::nullopt; // Invalid value or out of range. } +// Obtains enum value from string name. +// Returns std::optional with enum value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t>> { using D = std::decay_t; @@ -647,7 +685,7 @@ template return static_cast>(value); } -// Obtains index in enum value sequence from enum value. +// Obtains index in enum values from enum value. // Returns std::optional with index. template [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { @@ -660,7 +698,7 @@ template return std::nullopt; // Value out of range. } -// Checks whether enum contains enumerator with such value. +// Checks whether enum contains enumerator with such enum value. template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; @@ -676,7 +714,7 @@ template return detail::undex(value) != -1; } -// Checks whether enum contains enumerator with such string enum name. +// Checks whether enum contains enumerator with such string name. template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; @@ -751,7 +789,7 @@ constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t [[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; @@ -759,8 +797,8 @@ template return detail::count_v; } -// Returns enum flag value at specified index. -// No bounds checking is performed: the behavior is undefined if index >= number of enum values. +// Returns enum-flags value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum-flags values. template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -768,12 +806,13 @@ template if constexpr (detail::is_sparse_v) { return assert((index < detail::count_v)), detail::values_v[index]; } else { - return assert((index < detail::count_v)), detail::value(index); + constexpr auto min = detail::log2(detail::min_v) - 1; + + return assert((index < detail::count_v)), detail::value(index); } } -// Obtains value enum flag sequence. -// Returns std::array with enum flag values, sorted by enum flag value. +// Returns std::array with enum-flags values, sorted by enum-flags value. template [[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -781,8 +820,8 @@ template return detail::values_v; } -// Returns string enum flag name from enum value. -// If enum value does not have name or value out of range, returns empty string. +// 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 { using D = std::decay_t; @@ -802,8 +841,7 @@ template return name; } -// Obtains string enum flag name sequence. -// Returns std::array with string enum flag names, sorted by enum flag value. +// Returns std::array with string names, sorted by enum-flags value. template [[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -811,8 +849,7 @@ template return detail::names_v; } -// Obtains pair (value enum flag, string enum flag name) sequence. -// Returns std::array with std::pair (value enum flag, string enum flag name), sorted by enum flag value. +// Returns std::array with pairs (enum-flags value, string name), sorted by enum-flags value. template [[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -822,18 +859,14 @@ template // TODO: enum_cast -// Obtains enum flag value from integer value. -// Returns std::optional with enum flag value. +// Obtains enum-flags value from integer value. +// Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t>> { using D = std::decay_t; using U = std::underlying_type_t; - if (value < detail::min_v || value > detail::max_v) { - return std::nullopt; // Out of range. - } - - U check_value = U{0}; + auto check_value = U{0}; for (std::size_t i = 0; i < detail::count_v; ++i) { if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { check_value |= static_cast(v); @@ -844,11 +877,11 @@ template return static_cast(value); } - return std::nullopt; // Invalid value. + return std::nullopt; // Invalid value or out of range. } -// Obtains enum flag value from enum flag string name. -// Returns std::optional with enum flag value. +// Obtains enum-flags value from string name. +// Returns std::optional with enum-flags value. 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::flag::enum_cast requires bool(char, char) invocable predicate."); @@ -856,29 +889,38 @@ template return {}; } -// Obtains enum flag value from enum flag string name. -// Returns std::optional with enum flag value. +// Obtains enum-flags value from string name. +// Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { static_assert(sizeof(E) == 0, "not implemented"); return {}; } -// Returns integer value from enum flag value. +// Returns integer value from enum-flags value. template [[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_flags_t> { return static_cast>(value); } -// Obtains index in enum flag value sequence from enum flag value. +// Obtains index in enum-flags values from enum-flags value. // Returns std::optional with index. template [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { - static_assert(sizeof(E) == 0, "not implemented"); - return {}; + using D = std::decay_t; + + if (detail::is_pow2(value)) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (enum_value(i) == value) { + return i; + } + } + } + + return std::nullopt; // Value out of range. } -// Checks whether enum flag contains enumerator with such integer value. +// Checks whether enum-flags contains enumerator with such integer value. template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; @@ -886,7 +928,7 @@ template return enum_cast(value).has_value(); } -// Checks whether enum flag contains enumerator with such value. +// Checks whether enum-flags contains enumerator with such enum-flags value. template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; @@ -895,7 +937,7 @@ template return enum_cast(static_cast(value)).has_value(); } -// Checks whether enum flag contains enumerator with such string enum name. +// Checks whether enum-flags contains enumerator with such string name. template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { static_assert(sizeof(E) == 0, "not implemented"); @@ -907,15 +949,15 @@ namespace ostream_operators { // TODO: operator<< using namespace magic_enum::ostream_operators; -} // namespace magic_enum::flag::ostream_operators +} // namespace magic_enum::flags::ostream_operators namespace bitwise_operators { using namespace magic_enum::bitwise_operators; -} // namespace magic_enum::flag::bitwise_operators +} // namespace magic_enum::flags::bitwise_operators -} // namespace magic_enum::flag +} // namespace magic_enum::flags } // namespace magic_enum