diff --git a/example/example.cpp b/example/example.cpp index fab8274..055c0aa 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -47,5 +47,19 @@ int main() { std::cout << c3_name.value() << std::endl; // BLUE } + constexpr auto colors = magic_enum::enum_sequence(); + std::cout << "Color sequence:"; + for (auto e : colors) { + std::cout << " " << magic_enum::enum_to_string(e).value(); + } + std::cout << std::endl; + + constexpr auto colors_name = magic_enum::enum_to_string_sequence(); + std::cout << "Color sequence:"; + for (auto e : colors_name) { + std::cout << " " << e; + } + std::cout << std::endl; + return 0; } diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 32556b1..648fb6a 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -5,7 +5,7 @@ // | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| // |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| // __/ | https://github.com/Neargye/magic_enum -// |___/ vesion 0.1.1 +// |___/ vesion 0.1.2 // // Licensed under the MIT License . // SPDX-License-Identifier: MIT @@ -36,6 +36,8 @@ #include #include #include +#include +#include // Enum variable must be in range (-MAGIC_ENUM_RANGE, MAGIC_ENUM_RANGE). If you need a larger range, redefine the macro MAGIC_ENUM_RANGE. #if !defined(MAGIC_ENUM_RANGE) @@ -46,8 +48,6 @@ namespace magic_enum { static_assert(MAGIC_ENUM_RANGE > 0, "MAGIC_ENUM_RANGE must be positive and greater than zero."); -static_assert(MAGIC_ENUM_RANGE % 8 == 0, - "MAGIC_ENUM_RANGE must be a multiple of 8."); static_assert(MAGIC_ENUM_RANGE < std::numeric_limits::max(), "MAGIC_ENUM_RANGE must be less INT_MAX."); @@ -58,7 +58,7 @@ namespace detail { } template -[[nodiscard]] constexpr std::optional enum_to_string_impl() noexcept { +[[nodiscard]] constexpr std::string_view enum_to_string_impl() noexcept { static_assert(std::is_enum_v, "magic_enum::enum_to_string require enum type."); #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) std::string_view name{__PRETTY_FUNCTION__}; @@ -82,113 +82,125 @@ template if (name.length() > 0 && is_name_char(name.front(), true)) { return name; } else { - return std::nullopt; // Enum variable does not have name. + return {}; // Enum variable does not have name. } #endif } -template -struct enum_to_string_impl_t final { - [[nodiscard]] constexpr std::optional operator()(int value) const noexcept { - static_assert(std::is_enum_v, "magic_enum::enum_to_string require enum type."); - if constexpr (V > std::numeric_limits>::max()) { - return std::nullopt; // Enum variable out of range. - } +template +[[nodiscard]] constexpr std::string_view enum_to_string_impl(int value, std::integer_sequence) noexcept { + constexpr std::size_t n = sizeof...(I); + constexpr std::array names{{enum_to_string_impl(I + O)>()...}}; + return names[value - O]; +} - switch (value - V) { - case 0: - return enum_to_string_impl(V)>(); - case 1: - return enum_to_string_impl(V + 1)>(); - case 2: - return enum_to_string_impl(V + 2)>(); - case 3: - return enum_to_string_impl(V + 3)>(); - case 4: - return enum_to_string_impl(V + 4)>(); - case 5: - return enum_to_string_impl(V + 5)>(); - case 6: - return enum_to_string_impl(V + 6)>(); - case 7: - return enum_to_string_impl(V + 7)>(); - default: - return enum_to_string_impl_t{}(value); +template +[[nodiscard]] constexpr std::optional enum_from_string_impl(std::string_view name, std::integer_sequence) noexcept { + std::optional value; + (((enum_to_string_impl(I + O)>() == name) ? (value = static_cast(I + O), false) : true) && ...); + return value; +} + +template +[[nodiscard]] constexpr decltype(auto) enum_sequence_impl(std::integer_sequence) noexcept { + constexpr std::size_t n = sizeof...(I); + constexpr std::array valid{{!enum_to_string_impl(I + O)>().empty()...}}; + constexpr std::size_t num_valid = ((valid[I] ? 1 : 0) + ...); + + std::array sequence{}; + int v = 0; + for (int i = 0; i < n && v < num_valid; ++i) { + if (valid[i]) { + sequence[v++] = static_cast(i + O); } } -}; -template -struct enum_to_string_impl_t final { - [[nodiscard]] constexpr std::optional operator()(int) const noexcept { - static_assert(std::is_enum_v, "magic_enum::enum_to_string require enum type."); - return std::nullopt; // Enum variable out of range MAGIC_ENUM_RANGE. - } -}; + return sequence; +} -template -struct enum_from_string_impl_t final { - [[nodiscard]] constexpr std::optional operator()(std::string_view name) const noexcept { - static_assert(std::is_enum_v, "magic_enum::enum_from_string require enum type."); - if constexpr (V > std::numeric_limits>::max()) { - return std::nullopt; // Enum variable out of range. - } +template +[[nodiscard]] constexpr decltype(auto) enum_to_string_sequence_impl(std::integer_sequence s) noexcept { + constexpr std::size_t n = sizeof...(I); + constexpr std::array valid{{!enum_to_string_impl(I + O)>().empty()...}}; + constexpr std::size_t num_valid = ((valid[I] ? 1 : 0) + ...); - if (enum_to_string_impl(V)>() == name) { - return static_cast(V); - } else if (enum_to_string_impl(V + 1)>() == name) { - return static_cast(V + 1); - } else if (enum_to_string_impl(V + 2)>() == name) { - return static_cast(V + 2); - } else if (enum_to_string_impl(V + 3)>() == name) { - return static_cast(V + 3); - } else if (enum_to_string_impl(V + 4)>() == name) { - return static_cast(V + 4); - } else if (enum_to_string_impl(V + 5)>() == name) { - return static_cast(V + 5); - } else if (enum_to_string_impl(V + 6)>() == name) { - return static_cast(V + 6); - } else if (enum_to_string_impl(V + 7)>() == name) { - return static_cast(V + 7); - } else { - return enum_from_string_impl_t{}(name); + std::array sequence{}; + int v = 0; + for (int i = 0; i < n && v < num_valid; ++i) { + if (valid[i]) { + sequence[v++] = enum_to_string_impl(i + O, s); } } -}; -template -struct enum_from_string_impl_t final { - [[nodiscard]] constexpr std::optional operator()(std::string_view) const noexcept { - static_assert(std::is_enum_v, "magic_enum::enum_from_string require enum type."); - return std::nullopt; // Enum variable out of range MAGIC_ENUM_RANGE. - } -}; + return sequence; +} } // namespace detail // enum_to_string(enum) obtains string enum name from enum variable. template >>> [[nodiscard]] constexpr std::optional enum_to_string(T value) noexcept { - constexpr bool s = std::is_signed_v>>; - constexpr int min = s ? -MAGIC_ENUM_RANGE : 0; + using D = std::decay_t; + using U = std::underlying_type_t; + using C = std::common_type_t; + constexpr bool s = std::is_signed_v; + constexpr int min = s ? std::max(-MAGIC_ENUM_RANGE, std::numeric_limits::min()) : 0; + constexpr int max = std::min(MAGIC_ENUM_RANGE, std::numeric_limits::max()); + constexpr int range = max - min; if (static_cast(value) >= MAGIC_ENUM_RANGE || static_cast(value) <= -MAGIC_ENUM_RANGE) { return std::nullopt; // Enum variable out of range MAGIC_ENUM_RANGE. } - return detail::enum_to_string_impl_t, min>{}(static_cast(value)); + auto name = detail::enum_to_string_impl(static_cast(value), std::make_integer_sequence{}); + if (name.empty()) { + return std::nullopt; + } else { + return name; // Enum variable does not have name. + } } // enum_to_string() obtains string enum name from static storage enum variable. template >>> [[nodiscard]] constexpr std::optional enum_to_string() noexcept { - return detail::enum_to_string_impl(); + constexpr auto name = detail::enum_to_string_impl(); + if (name.empty()) { + return std::nullopt; + } else { + return name; // Enum variable does not have name. + } } // enum_from_string(name) obtains enum variable from enum string name. template >> [[nodiscard]] constexpr std::optional enum_from_string(std::string_view name) noexcept { - constexpr bool s = std::is_signed_v>; - constexpr int min = s ? -MAGIC_ENUM_RANGE : 0; - return detail::enum_from_string_impl_t{}(name); + using U = std::underlying_type_t; + using C = std::common_type_t; + constexpr bool s = std::is_signed_v; + constexpr int min = s ? std::max(-MAGIC_ENUM_RANGE, std::numeric_limits::min()) : 0; + constexpr int max = std::min(MAGIC_ENUM_RANGE, std::numeric_limits::max()); + constexpr int range = max - min; + return detail::enum_from_string_impl(name, std::make_integer_sequence{}); +} + +template >> +[[nodiscard]] constexpr decltype(auto) enum_sequence() noexcept { + using U = std::underlying_type_t; + using C = std::common_type_t; + constexpr bool s = std::is_signed_v; + constexpr int min = s ? std::max(-MAGIC_ENUM_RANGE, std::numeric_limits::min()) : 0; + constexpr int max = std::min(MAGIC_ENUM_RANGE, std::numeric_limits::max()); + constexpr int range = max - min; + return detail::enum_sequence_impl(std::make_integer_sequence{}); +} + +template >> +[[nodiscard]] constexpr decltype(auto) enum_to_string_sequence() noexcept { + using U = std::underlying_type_t; + using C = std::common_type_t; + constexpr bool s = std::is_signed_v; + constexpr int min = s ? std::max(-MAGIC_ENUM_RANGE, std::numeric_limits::min()) : 0; + constexpr int max = std::min(MAGIC_ENUM_RANGE, std::numeric_limits::max()); + constexpr int range = max - min; + return detail::enum_to_string_sequence_impl(std::make_integer_sequence{}); } } // namespace magic_enum diff --git a/test/test.cpp b/test/test.cpp index a45cb21..561f793 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -32,7 +32,7 @@ enum class Numbers : char { one = 10, two = 20, three = 30 }; enum Directions { Up = 85, Down = -42, Right = 119, Left = -119 }; -enum number : int { one = 10, two = 20, three = 30 }; +enum number : long { one = 10, two = 20, three = 30 }; TEST_CASE("magic_enum::enum_to_string(enum)") { Color cr = Color::RED;