From 5a3d4b53e6ebac2fcb58ac8a88de4d02d62e97d1 Mon Sep 17 00:00:00 2001 From: terik23 Date: Sat, 6 Apr 2019 18:35:09 +0500 Subject: [PATCH] v0.2.0 --- CMakeLists.txt | 2 +- README.md | 30 +++++++++++--- example/example.cpp | 6 +-- include/magic_enum.hpp | 93 +++++++++++++++++++++++------------------- test/test.cpp | 44 ++++++++++++-------- 5 files changed, 105 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index caac5f5..13d7514 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6) -project(magic_enum VERSION "0.1.1" LANGUAGES CXX) +project(magic_enum VERSION "0.2.0" LANGUAGES CXX) option(MAGIC_ENUM_OPT_BUILD_EXAMPLES "Build magic_enum examples" ON) option(MAGIC_ENUM_OPT_BUILD_TESTS "Build and perform magic_enum tests" ON) diff --git a/README.md b/README.md index 48c3a6a..797f944 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,15 @@ [![Build Status](https://travis-ci.org/Neargye/magic_enum.svg?branch=master)](https://travis-ci.org/Neargye/magic_enum) [![Build status](https://ci.appveyor.com/api/projects/status/0rpr966p9ssrvwu3/branch/master?svg=true)](https://ci.appveyor.com/project/Neargye/magic-enum-hf8vk/branch/master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/64d04f150af14c3e8bd1090057b68538)](https://www.codacy.com/app/Neargye/magic_enum?utm_source=github.com&utm_medium=referral&utm_content=Neargye/magic_enum&utm_campaign=Badge_Grade) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/gkjJ86ur57I3KOO6) +[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/uCF1Op6ZgSI6cDJS) ## What is Magic Enum? Header-only C++17 library provides Enum-to-String and String-to-Enum functions. * `magic_enum::enum_to_string` obtains string enum name from enum variable. -* `magic_enum::enum_from_string` obtains enum variable from enum string name. +* `magic_enum::enum_from_string` obtains enum value from enum string name. +* `magic_enum::enum_to_sequence` obtains value enum sequence. +* `magic_enum::enum_to_string_sequence` obtains string enum name sequence. ## Features @@ -53,7 +55,7 @@ Header-only C++17 library provides Enum-to-String and String-to-Enum functions. } ``` -* String enum name to enum variable +* String enum name to enum value ```cpp constexpr auto color = magic_enum::enum_from_string("GREEN"); if (color.has_value()) { @@ -61,13 +63,29 @@ Header-only C++17 library provides Enum-to-String and String-to-Enum functions. } ``` +* Enum to value sequence + ```cpp + constexpr auto colors = magic_enum::enum_to_sequence(); + // colors -> {Color::RED, Color::BLUE, Color::GREEN} + ``` + +* Enum to string enum name sequence + ```cpp + constexpr auto color_names = magic_enum::enum_to_string_sequence(); + // color_names -> {"RED", "BLUE", "GREEN"} + ``` + ## Remarks -* `magic_enum::enum_to_string` returns `std::optional`, using `has_value()` to check contains enum name and `value()` to get the enum name. If enum variable does not have name or out of range `MAGIC_ENUM_RANGE`, returns `std::nullopt`. +* `magic_enum::enum_to_string` returns `std::optional`, using `has_value()` to check contains enum name and `value()` to get the enum name. If enum value does not have name or out of range `MAGIC_ENUM_RANGE`, returns `std::nullopt`. -* `magic_enum::enum_from_string` returns `std::optional`, using `has_value()` to check contains enum variable and `value()` to get the enum variable. If enum variable does not have name or out of range `MAGIC_ENUM_RANGE`, returns `std::nullopt`. +* `magic_enum::enum_from_string` returns `std::optional`, using `has_value()` to check contains enum value and `value()` to get the enum value. If enum value does not have name or out of range `MAGIC_ENUM_RANGE`, returns `std::nullopt`. -* Enum variable must be in range `(-MAGIC_ENUM_RANGE, MAGIC_ENUM_RANGE)`. By default `MAGIC_ENUM_RANGE = 128`. If you need larger range, redefine the macro `MAGIC_ENUM_RANGE`. +* `magic_enum::enum_to_sequence` returns `std::array` with all value enum, sorted by enum value. + +* `magic_enum::enum_to_string_sequence` returns `std::array` with all string enum name, sorted by enum value. + +* Enum value must be in range `(-MAGIC_ENUM_RANGE, MAGIC_ENUM_RANGE)`. By default `MAGIC_ENUM_RANGE = 128`. If you need larger range, redefine the macro `MAGIC_ENUM_RANGE`. ```cpp #define MAGIC_ENUM_RANGE 1028 // Redefine MAGIC_ENUM_RANGE for larger range. #include diff --git a/example/example.cpp b/example/example.cpp index 131ec73..5ecf1d3 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -47,8 +47,8 @@ int main() { std::cout << c3_name.value() << std::endl; // BLUE } - constexpr auto colors = magic_enum::enum_sequence(); - std::cout << "Color sequence:"; + constexpr auto colors = magic_enum::enum_to_sequence(); + std::cout << "Colors:"; for (auto e : colors) { std::cout << " " << magic_enum::enum_to_string(e).value(); } @@ -56,7 +56,7 @@ int main() { // Color sequence: RED BLUE GREEN constexpr auto color_names = magic_enum::enum_to_string_sequence(); - std::cout << "Color sequence:"; + std::cout << "Color names:"; for (auto e : color_names) { std::cout << " " << e; } diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 59d36b3..80caa8e 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -5,7 +5,7 @@ // | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| // |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| // __/ | https://github.com/Neargye/magic_enum -// |___/ vesion 0.1.2 +// |___/ vesion 0.2.0 // // Licensed under the MIT License . // SPDX-License-Identifier: MIT @@ -39,7 +39,7 @@ #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. +// Enum value 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) # define MAGIC_ENUM_RANGE 128 #endif @@ -60,14 +60,17 @@ namespace detail { template [[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) +#if defined(__clang__) std::string_view name{__PRETTY_FUNCTION__}; constexpr auto suffix = sizeof("]") - 1; +#elif defined(__GNUC__) && __GNUC__ >= 9 + std::string_view name{__PRETTY_FUNCTION__}; + constexpr auto suffix = sizeof("; std::string_view = std::basic_string_view]") - 1; #elif defined(_MSC_VER) std::string_view name{__FUNCSIG__}; constexpr auto suffix = sizeof(">(void) noexcept") - 1; #else - return std::nullopt; // Unsupported compiler. + return {}; // Unsupported compiler. #endif #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER) @@ -82,34 +85,38 @@ template if (name.length() > 0 && is_name_char(name.front(), true)) { return name; } else { - return {}; // Enum variable does not have name. + return {}; // Enum value does not have name. } #endif } template [[nodiscard]] constexpr std::string_view enum_to_string_impl(int value, std::integer_sequence) noexcept { - constexpr std::size_t n = sizeof...(I); + static_assert(std::is_enum_v, "magic_enum::enum_to_string require enum type."); + constexpr int n = sizeof...(I); constexpr std::array names{{enum_to_string_impl(I + O)>()...}}; + return names[value - O]; } 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) && ...); + static_assert(std::is_enum_v, "magic_enum::enum_from_string require enum type."); + std::optional value{std::nullopt}; + (void)(((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); +[[nodiscard]] constexpr decltype(auto) enum_to_sequence_impl(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::enum_to_sequence require enum type."); + constexpr int n = sizeof...(I); constexpr std::array valid{{!enum_to_string_impl(I + O)>().empty()...}}; - constexpr std::size_t num_valid = ((valid[I] ? 1 : 0) + ...); + constexpr int num_valid = ((valid[I] ? 1 : 0) + ...); std::array sequence{}; - int v = 0; - for (int i = 0; i < n && v < num_valid; ++i) { + for (int i = 0, v = 0; i < n && v < num_valid; ++i) { if (valid[i]) { sequence[v++] = static_cast(i + O); } @@ -120,13 +127,13 @@ template template [[nodiscard]] constexpr decltype(auto) enum_to_string_sequence_impl(std::integer_sequence s) noexcept { - constexpr std::size_t n = sizeof...(I); + static_assert(std::is_enum_v, "magic_enum::enum_to_string_sequence require enum type."); + constexpr int n = sizeof...(I); constexpr std::array valid{{!enum_to_string_impl(I + O)>().empty()...}}; - constexpr std::size_t num_valid = ((valid[I] ? 1 : 0) + ...); + constexpr int num_valid = ((valid[I] ? 1 : 0) + ...); std::array sequence{}; - int v = 0; - for (int i = 0; i < n && v < num_valid; ++i) { + for (int i = 0, v = 0; i < n && v < num_valid; ++i) { if (valid[i]) { sequence[v++] = enum_to_string_impl(i + O, s); } @@ -142,19 +149,20 @@ template [[nodiscard]] constexpr std::optional enum_to_string(T value) noexcept { 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; + using C = std::common_type_t; + constexpr int min = std::max(std::is_signed_v ? -MAGIC_ENUM_RANGE : 0, std::numeric_limits::min()); 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. + constexpr int range = max - min + 1; + + if (static_cast(value) > max || static_cast(value) < min) { + return std::nullopt; // Enum value out of range. } - auto name = detail::enum_to_string_impl(static_cast(value), std::make_integer_sequence{}); + + const 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. + return name; // Enum value does not have name. } } @@ -162,46 +170,47 @@ template template >>> [[nodiscard]] constexpr std::optional enum_to_string() noexcept { constexpr auto name = detail::enum_to_string_impl(); + if (name.empty()) { return std::nullopt; } else { - return name; // Enum variable does not have name. + return name; // Enum value does not have name. } } -// enum_from_string(name) obtains enum variable from enum string name. +// enum_from_string(name) obtains enum value from enum string name. template >> [[nodiscard]] constexpr std::optional enum_from_string(std::string_view name) 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; + using C = std::common_type_t; + constexpr int min = std::max(std::is_signed_v ? -MAGIC_ENUM_RANGE : 0, std::numeric_limits::min()); constexpr int max = std::min(MAGIC_ENUM_RANGE, std::numeric_limits::max()); - constexpr int range = max - min; + constexpr int range = max - min + 1; + return detail::enum_from_string_impl(name, std::make_integer_sequence{}); } -// enum_sequence() obtains string enum sequence. +// enum_to_sequence() obtains value enum sequence. template >> -[[nodiscard]] constexpr decltype(auto) enum_sequence() noexcept { +[[nodiscard]] constexpr decltype(auto) enum_to_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; + using C = std::common_type_t; + constexpr int min = std::max(std::is_signed_v ? -MAGIC_ENUM_RANGE : 0, std::numeric_limits::min()); 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{}); + constexpr int range = max - min + 1; + + return detail::enum_to_sequence_impl(std::make_integer_sequence{}); } // enum_to_string_sequence() obtains string enum name 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; + using C = std::common_type_t; + constexpr int min = std::max(std::is_signed_v ? -MAGIC_ENUM_RANGE : 0, std::numeric_limits::min()); constexpr int max = std::min(MAGIC_ENUM_RANGE, std::numeric_limits::max()); - constexpr int range = max - min; + constexpr int range = max - min + 1; + return detail::enum_to_string_sequence_impl(std::make_integer_sequence{}); } diff --git a/test/test.cpp b/test/test.cpp index 433f5af..daac01b 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -34,7 +34,7 @@ enum class Numbers : char { one = 10, two = 20, three = 30 }; enum Directions { Up = 85, Down = -42, Right = 119, Left = -119 }; -enum number : long { one = 10, two = 20, three = 30 }; +enum number : unsigned long { one = 10, two = 20, three = 30 }; TEST_CASE("magic_enum::enum_to_string(enum)") { Color cr = Color::RED; @@ -42,26 +42,30 @@ TEST_CASE("magic_enum::enum_to_string(enum)") { REQUIRE(magic_enum::enum_to_string(cr).value() == "RED"); REQUIRE(magic_enum::enum_to_string(Color::BLUE).value() == "BLUE"); REQUIRE(magic_enum::enum_to_string(cm[1]).value() == "GREEN"); - REQUIRE(!magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(-MAGIC_ENUM_RANGE)).has_value()); Numbers no = Numbers::one; REQUIRE(magic_enum::enum_to_string(no).value() == "one"); REQUIRE(magic_enum::enum_to_string(Numbers::two).value() == "two"); REQUIRE(magic_enum::enum_to_string(Numbers::three).value() == "three"); - REQUIRE(!magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(-MAGIC_ENUM_RANGE)).has_value()); Directions dr = Directions::Right; REQUIRE(magic_enum::enum_to_string(Directions::Up).value() == "Up"); REQUIRE(magic_enum::enum_to_string(Directions::Down).value() == "Down"); REQUIRE(magic_enum::enum_to_string(dr).value() == "Right"); REQUIRE(magic_enum::enum_to_string(Directions::Left).value() == "Left"); - REQUIRE(!magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(-MAGIC_ENUM_RANGE)).has_value()); number nt = number::three; REQUIRE(magic_enum::enum_to_string(number::one).value() == "one"); REQUIRE(magic_enum::enum_to_string(number::two).value() == "two"); REQUIRE(magic_enum::enum_to_string(nt).value() == "three"); - REQUIRE(!magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(static_cast(-MAGIC_ENUM_RANGE)).has_value()); } TEST_CASE("magic_enum::enum_to_string()") { @@ -70,62 +74,66 @@ TEST_CASE("magic_enum::enum_to_string()") { REQUIRE(magic_enum::enum_to_string().value() == "RED"); REQUIRE(magic_enum::enum_to_string().value() == "BLUE"); REQUIRE(magic_enum::enum_to_string().value() == "GREEN"); - REQUIRE(!magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(-MAGIC_ENUM_RANGE)>().has_value()); constexpr Numbers no = Numbers::one; REQUIRE(magic_enum::enum_to_string().value() == "one"); REQUIRE(magic_enum::enum_to_string().value() == "two"); REQUIRE(magic_enum::enum_to_string().value() == "three"); - REQUIRE(!magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(-MAGIC_ENUM_RANGE)>().has_value()); constexpr Directions dr = Directions::Right; REQUIRE(magic_enum::enum_to_string().value() == "Up"); REQUIRE(magic_enum::enum_to_string().value() == "Down"); REQUIRE(magic_enum::enum_to_string().value() == "Right"); REQUIRE(magic_enum::enum_to_string().value() == "Left"); - REQUIRE(!magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(-MAGIC_ENUM_RANGE)>().has_value()); constexpr number nt = number::three; REQUIRE(magic_enum::enum_to_string().value() == "one"); REQUIRE(magic_enum::enum_to_string().value() == "two"); REQUIRE(magic_enum::enum_to_string().value() == "three"); - REQUIRE(!magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); + REQUIRE_FALSE(magic_enum::enum_to_string(-MAGIC_ENUM_RANGE)>().has_value()); } TEST_CASE("magic_enum::enum_from_string(name)") { REQUIRE(magic_enum::enum_from_string("RED").value() == Color::RED); REQUIRE(magic_enum::enum_from_string("GREEN").value() == Color::GREEN); REQUIRE(magic_enum::enum_from_string("BLUE").value() == Color::BLUE); - REQUIRE(!magic_enum::enum_from_string("None").has_value()); + REQUIRE_FALSE(magic_enum::enum_from_string("None").has_value()); REQUIRE(magic_enum::enum_from_string("one").value() == Numbers::one); REQUIRE(magic_enum::enum_from_string("two").value() == Numbers::two); REQUIRE(magic_enum::enum_from_string("three").value() == Numbers::three); - REQUIRE(!magic_enum::enum_from_string("None").has_value()); + REQUIRE_FALSE(magic_enum::enum_from_string("None").has_value()); REQUIRE(magic_enum::enum_from_string("Up").value() == Directions::Up); REQUIRE(magic_enum::enum_from_string("Down").value() == Directions::Down); REQUIRE(magic_enum::enum_from_string("Right").value() == Directions::Right); REQUIRE(magic_enum::enum_from_string("Left").value() == Directions::Left); - REQUIRE(!magic_enum::enum_from_string("None").has_value()); + REQUIRE_FALSE(magic_enum::enum_from_string("None").has_value()); REQUIRE(magic_enum::enum_from_string("one").value() == number::one); REQUIRE(magic_enum::enum_from_string("two").value() == number::two); REQUIRE(magic_enum::enum_from_string("three").value() == number::three); - REQUIRE(!magic_enum::enum_from_string("None").has_value()); + REQUIRE_FALSE(magic_enum::enum_from_string("None").has_value()); } TEST_CASE("magic_enum::enum_sequence()") { - constexpr auto mge_s1 = magic_enum::enum_sequence(); + constexpr auto mge_s1 = magic_enum::enum_to_sequence(); REQUIRE(mge_s1 == std::array{Color::RED, Color::GREEN, Color::BLUE}); - constexpr auto mge_s2 = magic_enum::enum_sequence(); + constexpr auto mge_s2 = magic_enum::enum_to_sequence(); REQUIRE(mge_s2 == std::array{Numbers::one, Numbers::two, Numbers::three}); - constexpr auto mge_s3 = magic_enum::enum_sequence(); + constexpr auto mge_s3 = magic_enum::enum_to_sequence(); REQUIRE(mge_s3 == std::array{Directions::Left, Directions::Down, Directions::Up, Directions::Right}); - constexpr auto mge_s4 = magic_enum::enum_sequence(); + constexpr auto mge_s4 = magic_enum::enum_to_sequence(); REQUIRE(mge_s4 == std::array{number::one, number::two, number::three}); } @@ -134,7 +142,7 @@ TEST_CASE("magic_enum::enum_to_string_sequence()") { REQUIRE(mge_s1 == std::array{"RED", "GREEN", "BLUE"}); constexpr auto mge_s2 = magic_enum::enum_to_string_sequence(); - REQUIRE(mge_s2 == std::array{"one", "two","three"}); + REQUIRE(mge_s2 == std::array{"one", "two", "three"}); constexpr auto mge_s3 = magic_enum::enum_to_string_sequence(); REQUIRE(mge_s3 == std::array{"Left", "Down", "Up", "Right"});