diff --git a/.gitignore b/.gitignore index bb2e110..c33d2aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ +.vscode/ ### C++ gitignore ### # Prerequisites diff --git a/README.md b/README.md index 797f944..ce40fb3 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,12 @@ ## 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 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. +* `magic_enum::enum_cast` obtains enum value from string or integer. +* `magic_enum::enum_value` obtains indexed access to enum value. +* `magic_enum::enum_values` obtains enum value sequence. +* `magic_enum::enum_count` obtains number of enum values. +* `magic_enum::enum_name` obtains string name from enum value. +* `magic_enum::enum_names` obtains string enum name sequence. ## Features @@ -34,56 +36,84 @@ Header-only C++17 library provides Enum-to-String and String-to-Enum functions. * Compile-time * Enum to string * String to enum +* Works with any enum type ## [Examples](example/example.cpp) -* Enum variable to string enum name +* Enum value to string ```cpp - auto color = Color::RED; - auto color_name = magic_enum::enum_to_string(color); + Color color = Color::RED; + auto color_name = magic_enum::enum_name(color); if (color_name.has_value()) { // color_name.value() -> "RED" } ``` -* Static storage enum variable to string enum name +* Static storage enum variable to string ```cpp - constexpr auto color = Color::BLUE; - constexpr auto color_name = magic_enum::enum_to_string(); + constexpr Color color = Color::BLUE; + constexpr auto color_name = magic_enum::enum_name(color); if (color_name.has_value()) { // color_name.value() -> "BLUE" } ``` -* String enum name to enum value +* String to enum value ```cpp - constexpr auto color = magic_enum::enum_from_string("GREEN"); + constexpr auto color = magic_enum::enum_cast("GREEN"); if (color.has_value()) { // color.value() -> Color::GREEN } ``` -* Enum to value sequence +* Integer to enum value ```cpp - constexpr auto colors = magic_enum::enum_to_sequence(); + constexpr auto color = magic_enum::enum_cast(0); + if (color.has_value()) { + // color.value() -> Color::RED + } + ``` + +* Indexed access to enum value + ```cpp + constexpr Color color_value = magic_enum::enum_value(0); + // color_element -> Color::RED + ``` + +* Enum value sequence + ```cpp + constexpr auto colors = magic_enum::enum_values(); // colors -> {Color::RED, Color::BLUE, Color::GREEN} ``` -* Enum to string enum name sequence +* Number of enum elements ```cpp - constexpr auto color_names = magic_enum::enum_to_string_sequence(); + constexpr std::size_t color_size = magic_enum::enum_count(); + // color_size -> 3 + ``` + +* Enum string sequence + ```cpp + constexpr auto color_names = magic_enum::enum_names(); // color_names -> {"RED", "BLUE", "GREEN"} ``` +* Stream operator for enum + ```cpp + using namespace magic_enum::ops; // out-of-the-box stream operator for enums. + Color color = Color::BLUE; + std::cout << color << std::endl; // "BLUE" + ``` + ## 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 value does not have name or out of range `MAGIC_ENUM_RANGE`, returns `std::nullopt`. +* `magic_enum::enum_cast` returns `std::optional`, using `has_value()` to check contains enum value and `value()` to get the enum value. -* `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`. +* `magic_enum::enum_values` returns `std::array` with all enum value, sorted by enum value. -* `magic_enum::enum_to_sequence` returns `std::array` with all value enum, sorted by enum value. +* `magic_enum::enum_name` returns `std::optional`, using `has_value()` to check contains enum name and `value()` to get the enum name. -* `magic_enum::enum_to_string_sequence` returns `std::array` with all string enum name, sorted by enum value. +* `magic_enum::enum_names` 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 diff --git a/example/example.cpp b/example/example.cpp index 5ecf1d3..f63a930 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -20,45 +20,57 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include - #include +#include + +#include enum Color { RED = -10, BLUE = 0, GREEN = 10 }; int main() { - // Enum variable to string enum name. - auto c1 = Color::RED; - auto c1_name = magic_enum::enum_to_string(c1); + // Enum variable to string name. + Color c1 = Color::RED; + auto c1_name = magic_enum::enum_name(c1); if (c1_name.has_value()) { std::cout << c1_name.value() << std::endl; // RED } - // String enum name to enum variable. - auto c2 = magic_enum::enum_from_string("GREEN"); - if (c2.has_value() && c2.value() == Color::GREEN) { - std::cout << "GREEN = " << c2.value() << std::endl; // GREEN = 10 - } - - // Static storage enum variable to string enum name. - constexpr auto c3 = Color::BLUE; - constexpr auto c3_name = magic_enum::enum_to_string(); - if (c3_name.has_value()) { - std::cout << c3_name.value() << std::endl; // BLUE - } - - constexpr auto colors = magic_enum::enum_to_sequence(); - std::cout << "Colors:"; - for (auto e : colors) { - std::cout << " " << magic_enum::enum_to_string(e).value(); + // String enum name sequence. + constexpr auto color_names = magic_enum::enum_names(); + std::cout << "Color names:"; + for (auto n : color_names) { + std::cout << " " << n; } std::cout << std::endl; - // Color sequence: RED BLUE GREEN + // Color names: RED BLUE GREEN - constexpr auto color_names = magic_enum::enum_to_string_sequence(); - std::cout << "Color names:"; - for (auto e : color_names) { - std::cout << " " << e; + // String name to enum value. + auto c2 = magic_enum::enum_cast("BLUE"); + if (c2.has_value() && c2.value() == Color::BLUE) { + std::cout << "BLUE = " << c2.value() << std::endl; // BLUE = 0 + } + + // Integer value to enum value. + auto c3 = magic_enum::enum_cast(10); + if (c3.has_value() && c3.value() == Color::GREEN) { + std::cout << "GREEN = " << c3.value() << std::endl; // GREEN = 10 + } + + using namespace magic_enum::ops; // out-of-the-box stream operator for enums. + // ostream operator for enum. + std::cout << "Color: " << c1 << " " << c2 << " " << c3 << std::endl; // Color: RED BLUE GREEN + + // Number of enum values. + std::cout << "Color enum size: " << magic_enum::enum_count() << std::endl; // Color enum size: 3 + + // Indexed access to enum value. + std::cout << "Color[0] = " << magic_enum::enum_value(0) << std::endl; // Color[0] = RED + + // Enum value sequence. + constexpr auto colors = magic_enum::enum_values(); + std::cout << "Colors sequence:"; + for (Color c : colors) { + std::cout << " " << c; // ostream operator for enum. } std::cout << std::endl; // Color sequence: RED BLUE GREEN diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 80caa8e..8e94575 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -5,7 +5,7 @@ // | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| // |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| // __/ | https://github.com/Neargye/magic_enum -// |___/ vesion 0.2.0 +// |___/ vesion 0.3.0-dev // // Licensed under the MIT License . // SPDX-License-Identifier: MIT @@ -31,13 +31,16 @@ #pragma once +#include +#include +#include #include -#include +#include #include #include #include -#include -#include +#include +#include // 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) @@ -53,13 +56,28 @@ static_assert(MAGIC_ENUM_RANGE < std::numeric_limits::max(), namespace detail { +template +struct enum_range final { + using D = std::decay_t; + static_assert(std::is_enum_v, "magic_enum::detail::enum_range require enum type."); + using U = std::underlying_type_t; + using C = std::common_type_t; + static constexpr int min = std::max(std::is_signed_v ? -MAGIC_ENUM_RANGE : 0, std::numeric_limits::min()); + static constexpr int max = std::min(MAGIC_ENUM_RANGE, std::numeric_limits::max()); + static constexpr auto sequence = std::make_integer_sequence{}; + + static constexpr bool contains(int value) noexcept { + return static_cast(value) <= enum_range::max && static_cast(value) >= enum_range::min; + } +}; + [[nodiscard]] constexpr bool is_name_char(char c, bool front) noexcept { return (!front && c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } 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."); +[[nodiscard]] constexpr std::string_view name_impl() noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::name_impl require enum type."); #if defined(__clang__) std::string_view name{__PRETTY_FUNCTION__}; constexpr auto suffix = sizeof("]") - 1; @@ -90,128 +108,167 @@ template #endif } -template -[[nodiscard]] constexpr std::string_view enum_to_string_impl(int value, std::integer_sequence) noexcept { - 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)>()...}}; +template +[[nodiscard]] constexpr decltype(auto) strings_impl(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::strings_impl require enum type."); + constexpr std::array names{{name_impl(I + enum_range::min)>()...}}; - return names[value - O]; + return names; } -template -[[nodiscard]] constexpr std::optional enum_from_string_impl(std::string_view name, std::integer_sequence) noexcept { - 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) && ...); +template +[[nodiscard]] constexpr std::string_view name_impl(int value) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::name_impl require enum type."); + constexpr auto names = strings_impl(enum_range::sequence); - return value; + return enum_range::contains(value) ? names[value - enum_range::min] : std::string_view{}; } -template -[[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."); +template +[[nodiscard]] constexpr decltype(auto) values_impl(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::values_impl require enum type."); constexpr int n = sizeof...(I); - constexpr std::array valid{{!enum_to_string_impl(I + O)>().empty()...}}; + constexpr std::array valid{{!name_impl(I + enum_range::min)>().empty()...}}; constexpr int num_valid = ((valid[I] ? 1 : 0) + ...); - std::array sequence{}; + std::array enums{}; for (int i = 0, v = 0; i < n && v < num_valid; ++i) { if (valid[i]) { - sequence[v++] = static_cast(i + O); + enums[v++] = static_cast(i + enum_range::min); } } - return sequence; + return enums; } -template -[[nodiscard]] constexpr decltype(auto) enum_to_string_sequence_impl(std::integer_sequence s) noexcept { - 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 int num_valid = ((valid[I] ? 1 : 0) + ...); +template +[[nodiscard]] constexpr decltype(auto) names_impl(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::names_impl require enum type."); + constexpr auto enums = values_impl(enum_range::sequence); + constexpr std::array names{{name_impl()...}}; - std::array sequence{}; - for (int i = 0, v = 0; i < n && v < num_valid; ++i) { - if (valid[i]) { - sequence[v++] = enum_to_string_impl(i + O, s); + return names; +} + +template +[[nodiscard]] constexpr std::optional enum_cast_impl(std::string_view value) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::enum_cast_impl require enum type."); + constexpr auto values = values_impl(enum_range::sequence); + constexpr auto count = values.size(); + constexpr auto names = names_impl(std::make_index_sequence{}); + + for (std::size_t i = 0; i < count; ++i) { + if (names[i] == value) { + return values[i]; } } - return sequence; + return std::nullopt; // Invalid value or out of range. } -} // namespace detail +template +using enable_if_enum_t = typename std::enable_if>>::type; -// enum_to_string(enum) obtains string enum name from enum variable. -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 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 + 1; +} // namespace magic_enum::detail - if (static_cast(value) > max || static_cast(value) < min) { - return std::nullopt; // Enum value out of range. - } +// Obtains enum value from enum string name. +template > +[[nodiscard]] constexpr std::optional enum_cast(std::string_view value) noexcept { + using D = std::decay_t; - const auto name = detail::enum_to_string_impl(static_cast(value), std::make_integer_sequence{}); - if (name.empty()) { - return std::nullopt; + return detail::enum_cast_impl(value); +} + +// Obtains enum value from integer value. +template > +[[nodiscard]] constexpr std::optional enum_cast(std::underlying_type_t value) noexcept { + using D = std::decay_t; + + if (detail::name_impl(static_cast(value)).empty()) { + return std::nullopt; // Invalid value or out of range. } else { - return name; // Enum value does not have name. + return static_cast(value); } } -// enum_to_string() obtains string enum name from static storage enum variable. -template >>> -[[nodiscard]] constexpr std::optional enum_to_string() noexcept { - constexpr auto name = detail::enum_to_string_impl(); +// Obtains indexed access to enum element. +template> +[[nodiscard]] constexpr E enum_value(std::size_t i) noexcept { + using D = std::decay_t; + constexpr auto values = detail::values_impl(detail::enum_range::sequence); + + return assert(i < values.size()), values[i]; +} + +// Obtains value enum sequence. +template > +[[nodiscard]] constexpr decltype(auto) enum_values() noexcept { + using D = std::decay_t; + constexpr auto values = detail::values_impl(detail::enum_range::sequence); + + return values; +} + +// Obtains number of enum elements. +template > +[[nodiscard]] constexpr std::size_t enum_count() noexcept { + using D = std::decay_t; + constexpr auto count = detail::values_impl(detail::enum_range::sequence).size(); + + return count; +} + +// Obtains string enum name from enum value. +template > +[[nodiscard]] constexpr std::optional enum_name(E value) noexcept { + using D = std::decay_t; + const auto name = detail::name_impl(static_cast(value)); if (name.empty()) { return std::nullopt; } else { - return name; // Enum value does not have name. + return name; // Enum value does not have name or out of range. } } -// 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 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 + 1; +// Obtains string enum name sequence. +template > +[[nodiscard]] constexpr decltype(auto) enum_names() noexcept { + using D = std::decay_t; + constexpr auto count = detail::values_impl(detail::enum_range::sequence).size(); + constexpr auto names = detail::names_impl(std::make_index_sequence{}); - return detail::enum_from_string_impl(name, std::make_integer_sequence{}); + return names; } -// enum_to_sequence() obtains value enum sequence. -template >> -[[nodiscard]] constexpr decltype(auto) enum_to_sequence() noexcept { - using U = std::underlying_type_t; - 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 + 1; +namespace ops { - return detail::enum_to_sequence_impl(std::make_integer_sequence{}); +template > +std::ostream& operator<<(std::ostream& os, E value) { + using D = std::decay_t; + const auto name = detail::name_impl(static_cast(value)); + + if (!name.empty()) { + os << name; + } + + return os; } -// 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 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 + 1; +template > +std::ostream& operator<<(std::ostream& os, std::optional value) { + using D = std::decay_t; - return detail::enum_to_string_sequence_impl(std::make_integer_sequence{}); + if (value.has_value()) { + const auto name = detail::name_impl(static_cast(value.value())); + if (!name.empty()) { + os << name; + } + } + + return os; } +} // namespace magic_enum::ops + } // namespace magic_enum diff --git a/test/test.cpp b/test/test.cpp index daac01b..ae43821 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include enum class Color { RED = -12, GREEN = 7, BLUE = 15 }; @@ -36,117 +38,195 @@ enum Directions { Up = 85, Down = -42, Right = 119, Left = -119 }; enum number : unsigned long { one = 10, two = 20, three = 30 }; -TEST_CASE("magic_enum::enum_to_string(enum)") { - Color cr = Color::RED; - Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; - 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_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("enum_cast") { + SECTION("string") { +#if defined(_MSC_VER) && _MSC_VER < 1920 +# define constexpr // Visual Studio 2017 have bug with string_view constexpr compare. +#endif - 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_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()); + constexpr auto cr = magic_enum::enum_cast("RED"); + REQUIRE(cr.value() == Color::RED); + REQUIRE(magic_enum::enum_cast("GREEN").value() == Color::GREEN); + REQUIRE(magic_enum::enum_cast("BLUE").value() == Color::BLUE); + REQUIRE_FALSE(magic_enum::enum_cast("None").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_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()); + constexpr auto no = magic_enum::enum_cast("one"); + REQUIRE(no.value() == Numbers::one); + REQUIRE(magic_enum::enum_cast("two").value() == Numbers::two); + REQUIRE(magic_enum::enum_cast("three").value() == Numbers::three); + REQUIRE_FALSE(magic_enum::enum_cast("None").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_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()); + constexpr auto dr = magic_enum::enum_cast("Right"); + REQUIRE(magic_enum::enum_cast("Up").value() == Directions::Up); + REQUIRE(magic_enum::enum_cast("Down").value() == Directions::Down); + REQUIRE(dr.value() == Directions::Right); + REQUIRE(magic_enum::enum_cast("Left").value() == Directions::Left); + REQUIRE_FALSE(magic_enum::enum_cast("None").has_value()); + + constexpr auto nt = magic_enum::enum_cast("three"); + REQUIRE(magic_enum::enum_cast("one").value() == number::one); + REQUIRE(magic_enum::enum_cast("two").value() == number::two); + REQUIRE(nt.value() == number::three); + REQUIRE_FALSE(magic_enum::enum_cast("None").has_value()); + +#undef constexpr + } + + SECTION("string") { + constexpr auto cr = magic_enum::enum_cast(-12); + REQUIRE(cr.value() == Color::RED); + REQUIRE(magic_enum::enum_cast(7).value() == Color::GREEN); + REQUIRE(magic_enum::enum_cast(15).value() == Color::BLUE); + REQUIRE_FALSE(magic_enum::enum_cast(0).has_value()); + + constexpr auto no = magic_enum::enum_cast(10); + REQUIRE(no.value() == Numbers::one); + REQUIRE(magic_enum::enum_cast(20).value() == Numbers::two); + REQUIRE(magic_enum::enum_cast(30).value() == Numbers::three); + REQUIRE_FALSE(magic_enum::enum_cast(0).has_value()); + + constexpr auto dr = magic_enum::enum_cast(119); + REQUIRE(magic_enum::enum_cast(85).value() == Directions::Up); + REQUIRE(magic_enum::enum_cast(-42).value() == Directions::Down); + REQUIRE(dr.value() == Directions::Right); + REQUIRE(magic_enum::enum_cast(-119).value() == Directions::Left); + REQUIRE_FALSE(magic_enum::enum_cast(0).has_value()); + + constexpr auto nt = magic_enum::enum_cast(30); + REQUIRE(magic_enum::enum_cast(10).value() == number::one); + REQUIRE(magic_enum::enum_cast(20).value() == number::two); + REQUIRE(nt.value() == number::three); + REQUIRE_FALSE(magic_enum::enum_cast(0).has_value()); + } } -TEST_CASE("magic_enum::enum_to_string()") { - constexpr Color cr = Color::RED; - constexpr Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; - 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_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("enum_value") { + constexpr auto cr = magic_enum::enum_value(0); + REQUIRE(cr == Color::RED); + REQUIRE(magic_enum::enum_value(1) == Color::GREEN); + REQUIRE(magic_enum::enum_value(2) == Color::BLUE); - 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_FALSE(magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); - REQUIRE_FALSE(magic_enum::enum_to_string(-MAGIC_ENUM_RANGE)>().has_value()); + constexpr auto no = magic_enum::enum_value(0); + REQUIRE(no == Numbers::one); + REQUIRE(magic_enum::enum_value(1) == Numbers::two); + REQUIRE(magic_enum::enum_value(2) == Numbers::three); - 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_FALSE(magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); - REQUIRE_FALSE(magic_enum::enum_to_string(-MAGIC_ENUM_RANGE)>().has_value()); + constexpr auto dr = magic_enum::enum_value(3); + REQUIRE(magic_enum::enum_value(0) == Directions::Left); + REQUIRE(magic_enum::enum_value(1) == Directions::Down); + REQUIRE(magic_enum::enum_value(2) == Directions::Up); + REQUIRE(dr == Directions::Right); - 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_FALSE(magic_enum::enum_to_string(MAGIC_ENUM_RANGE)>().has_value()); - REQUIRE_FALSE(magic_enum::enum_to_string(-MAGIC_ENUM_RANGE)>().has_value()); + constexpr auto nt = magic_enum::enum_value(2); + REQUIRE(magic_enum::enum_value(0) == number::one); + REQUIRE(magic_enum::enum_value(1) == number::two); + REQUIRE(nt == number::three); } -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_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_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_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_FALSE(magic_enum::enum_from_string("None").has_value()); -} - -TEST_CASE("magic_enum::enum_sequence()") { - constexpr auto mge_s1 = magic_enum::enum_to_sequence(); +TEST_CASE("enum_values") { + constexpr auto mge_s1 = magic_enum::enum_values(); REQUIRE(mge_s1 == std::array{Color::RED, Color::GREEN, Color::BLUE}); - constexpr auto mge_s2 = magic_enum::enum_to_sequence(); + constexpr auto mge_s2 = magic_enum::enum_values(); REQUIRE(mge_s2 == std::array{Numbers::one, Numbers::two, Numbers::three}); - constexpr auto mge_s3 = magic_enum::enum_to_sequence(); + constexpr auto mge_s3 = magic_enum::enum_values(); REQUIRE(mge_s3 == std::array{Directions::Left, Directions::Down, Directions::Up, Directions::Right}); - constexpr auto mge_s4 = magic_enum::enum_to_sequence(); + constexpr auto mge_s4 = magic_enum::enum_values(); REQUIRE(mge_s4 == std::array{number::one, number::two, number::three}); } -TEST_CASE("magic_enum::enum_to_string_sequence()") { - constexpr auto mge_s1 = magic_enum::enum_to_string_sequence(); +TEST_CASE("enum_count") { + constexpr auto mge_s1 = magic_enum::enum_count(); + REQUIRE(mge_s1 == 3); + + constexpr auto mge_s2 = magic_enum::enum_count(); + REQUIRE(mge_s2 == 3); + + constexpr auto mge_s3 = magic_enum::enum_count(); + REQUIRE(mge_s3 == 4); + + constexpr auto mge_s4 = magic_enum::enum_count(); + REQUIRE(mge_s4 == 3); +} + +TEST_CASE("enum_name") { + constexpr Color cr = Color::RED; + constexpr auto cr_name = magic_enum::enum_name(cr); + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + REQUIRE(cr_name.value() == "RED"); + REQUIRE(magic_enum::enum_name(Color::BLUE).value() == "BLUE"); + REQUIRE(magic_enum::enum_name(cm[1]).value() == "GREEN"); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(-MAGIC_ENUM_RANGE)).has_value()); + + constexpr Numbers no = Numbers::one; + constexpr auto no_name = magic_enum::enum_name(no); + REQUIRE(no_name.value() == "one"); + REQUIRE(magic_enum::enum_name(Numbers::two).value() == "two"); + REQUIRE(magic_enum::enum_name(Numbers::three).value() == "three"); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(-MAGIC_ENUM_RANGE)).has_value()); + + constexpr Directions dr = Directions::Right; + constexpr auto dr_name = magic_enum::enum_name(dr); + REQUIRE(magic_enum::enum_name(Directions::Up).value() == "Up"); + REQUIRE(magic_enum::enum_name(Directions::Down).value() == "Down"); + REQUIRE(dr_name.value() == "Right"); + REQUIRE(magic_enum::enum_name(Directions::Left).value() == "Left"); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(-MAGIC_ENUM_RANGE)).has_value()); + + constexpr number nt = number::three; + constexpr auto nt_name = magic_enum::enum_name(nt); + REQUIRE(magic_enum::enum_name(number::one).value() == "one"); + REQUIRE(magic_enum::enum_name(number::two).value() == "two"); + REQUIRE(nt_name.value() == "three"); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(MAGIC_ENUM_RANGE)).has_value()); + REQUIRE_FALSE(magic_enum::enum_name(static_cast(-MAGIC_ENUM_RANGE)).has_value()); +} + +TEST_CASE("enum_names") { + constexpr auto mge_s1 = magic_enum::enum_names(); REQUIRE(mge_s1 == std::array{"RED", "GREEN", "BLUE"}); - constexpr auto mge_s2 = magic_enum::enum_to_string_sequence(); + constexpr auto mge_s2 = magic_enum::enum_names(); REQUIRE(mge_s2 == std::array{"one", "two", "three"}); - constexpr auto mge_s3 = magic_enum::enum_to_string_sequence(); + constexpr auto mge_s3 = magic_enum::enum_names(); REQUIRE(mge_s3 == std::array{"Left", "Down", "Up", "Right"}); - constexpr auto mge_s4 = magic_enum::enum_to_string_sequence(); + constexpr auto mge_s4 = magic_enum::enum_names(); REQUIRE(mge_s4 == std::array{"one", "two", "three"}); } + +TEST_CASE("operator<<") { + auto test_ostream = [](auto e, std::string_view name) { + using namespace magic_enum::ops; + std::stringstream ss; + ss << e; + REQUIRE(ss.str() == name); + }; + + test_ostream(Color::RED, "RED"); + test_ostream(Color::GREEN, "GREEN"); + test_ostream(Color::BLUE, "BLUE"); + test_ostream((Color)0, ""); + + test_ostream(Numbers::one, "one"); + test_ostream(Numbers::two, "two"); + test_ostream(Numbers::three, "three"); + test_ostream((Numbers)0, ""); + + test_ostream(Directions::Up, "Up"); + test_ostream(Directions::Down, "Down"); + test_ostream(Directions::Right, "Right"); + test_ostream(Directions::Left, "Left"); + test_ostream((Directions)0, ""); + + test_ostream(number::one, "one"); + test_ostream(number::two, "two"); + test_ostream(number::three, "three"); + test_ostream((number)0, ""); +}