From 690486e7f2d3879277942e4a1b02f52579a9c981 Mon Sep 17 00:00:00 2001 From: Daniil Goncharov Date: Mon, 7 Sep 2020 12:55:59 +0300 Subject: [PATCH] improve customize --- doc/limitations.md | 4 +- example/example_custom_name.cpp | 35 +++++++++++++++-- include/magic_enum.hpp | 69 +++++++++++++++++++++------------ test/test.cpp | 46 +++++++++++++--------- test/test_flags.cpp | 2 +- 5 files changed, 107 insertions(+), 49 deletions(-) diff --git a/doc/limitations.md b/doc/limitations.md index 253c6c8..1d10e0f 100644 --- a/doc/limitations.md +++ b/doc/limitations.md @@ -22,14 +22,14 @@ #include ``` - * If need another range for specific enum type, add specialization `enum_range` for necessary enum type. Specialization of `enum_range` must be injected in `namespace magic_enum`. + * If need another range for specific enum type, add specialization `enum_range` for necessary enum type. Specialization of `enum_range` must be injected in `namespace magic_enum::customize`. ```cpp #include enum class number { one = 100, two = 200, three = 300 }; - namespace magic_enum { + namespace magic_enum::customize { template <> struct enum_range { static constexpr int min = 100; // Must be greater than `INT16_MIN`. diff --git a/example/example_custom_name.cpp b/example/example_custom_name.cpp index a1d5f99..5e71720 100644 --- a/example/example_custom_name.cpp +++ b/example/example_custom_name.cpp @@ -26,17 +26,46 @@ enum class Color : int { RED = -10, BLUE = 0, GREEN = 10 }; -// Сustom definitions of name for enum value. +// Сustom definitions of names for enum. +// Specialization of `enum_name` must be injected in `namespace magic_enum::customize`. template <> -constexpr auto magic_enum::detail::n() noexcept { - return std::string_view{"the red color"}; +constexpr std::string_view magic_enum::customize::enum_name(Color value) noexcept { + switch (value) { + case Color::RED: + return "the red color"; + case Color::BLUE: + return "The BLUE"; + case Color::GREEN: + return {}; // "Empty string for default value." + } + return {}; // "Empty string for unknow value." +} + +enum class Numbers : int { One, Two, Three }; + +// Сustom definitions of names for enum. +// Specialization of `enum_name` must be injected in `namespace magic_enum::customize`. +template <> +constexpr std::string_view magic_enum::customize::enum_name(Numbers value) noexcept { + switch (value) { + case Numbers::One: + return "the one"; + default: + return {}; // "Empty string for default or unknow value." + } } int main() { std::cout << magic_enum::enum_name(Color::RED) << std::endl; // "the red color" + std::cout << magic_enum::enum_name(Color::BLUE) << std::endl; // "The BLUE" + std::cout << magic_enum::enum_name(Color::GREEN) << std::endl; // "GREEN" std::cout << std::boolalpha; std::cout << (magic_enum::enum_cast("the red color").value() == Color::RED) << std::endl; // "true" + std::cout << magic_enum::enum_name(Numbers::One) << std::endl; // "the one" + std::cout << magic_enum::enum_name(Numbers::Two) << std::endl; // "Two" + std::cout << magic_enum::enum_name(Numbers::Three) << std::endl; // "Three" + return 0; } diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 7e23c2d..fa8e5a1 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -91,25 +91,6 @@ namespace magic_enum { -// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. -// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. -// If need another range for specific enum type, add specialization enum_range for necessary enum type. -template -struct enum_range { - static_assert(std::is_enum_v, "magic_enum::enum_range requires enum type."); - inline static constexpr int min = MAGIC_ENUM_RANGE_MIN; - inline static constexpr int max = MAGIC_ENUM_RANGE_MAX; - static_assert(max > min, "magic_enum::enum_range requires max > min."); -}; - -static_assert(MAGIC_ENUM_RANGE_MIN <= 0, "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); -static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "MAGIC_ENUM_RANGE_MIN must be greater than INT16_MIN."); - -static_assert(MAGIC_ENUM_RANGE_MAX > 0, "MAGIC_ENUM_RANGE_MAX must be greater than 0."); -static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE_MAX must be less than INT16_MAX."); - -static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); - // If need another optional type, define the macro MAGIC_ENUM_USING_ALIAS_OPTIONAL. #if defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) MAGIC_ENUM_USING_ALIAS_OPTIONAL @@ -132,6 +113,37 @@ MAGIC_ENUM_USING_ALIAS_STRING using string = std::string; #endif +namespace customize { + +// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. +// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. +// If need another range for specific enum type, add specialization enum_range for necessary enum type. +template +struct enum_range { + static_assert(std::is_enum_v, "magic_enum::customize::enum_range requires enum type."); + inline static constexpr int min = MAGIC_ENUM_RANGE_MIN; + inline static constexpr int max = MAGIC_ENUM_RANGE_MAX; + static_assert(max > min, "magic_enum::customize::enum_range requires max > min."); +}; + +static_assert(MAGIC_ENUM_RANGE_MIN <= 0, "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); +static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "MAGIC_ENUM_RANGE_MIN must be greater than INT16_MIN."); + +static_assert(MAGIC_ENUM_RANGE_MAX > 0, "MAGIC_ENUM_RANGE_MAX must be greater than 0."); +static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE_MAX must be less than INT16_MAX."); + +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); + +// If need cunstom names for enum type, add specialization enum_name for necessary enum type. +template +constexpr string_view enum_name(E) noexcept { + static_assert(std::is_enum_v, "magic_enum::customize::enum_name requires enum type."); + + return {}; +} + +} // namespace magic_enum::customize + namespace detail { template @@ -306,16 +318,23 @@ inline constexpr auto type_name_v = n(); template constexpr auto n() noexcept { static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + constexpr auto custom_name = customize::enum_name(V); + + if constexpr (custom_name.empty()) { + static_cast(custom_name); #if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED # if defined(__clang__) || defined(__GNUC__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); # elif defined(_MSC_VER) - constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); # endif - return static_string{name}; + return static_string{name}; #else - return string_view{}; // Unsupported compiler. + return string_view{}; // Unsupported compiler. #endif + } else { + return static_string{custom_name}; + } } template @@ -335,7 +354,7 @@ constexpr int reflected_min() noexcept { if constexpr (IsFlags) { return 0; } else { - constexpr auto lhs = enum_range::min; + constexpr auto lhs = customize::enum_range::min; static_assert(lhs > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); constexpr auto rhs = (std::numeric_limits::min)(); @@ -354,7 +373,7 @@ constexpr int reflected_max() noexcept { if constexpr (IsFlags) { return std::numeric_limits::digits - 1; } else { - constexpr auto lhs = enum_range::max; + constexpr auto lhs = customize::enum_range::max; static_assert(lhs < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); constexpr auto rhs = (std::numeric_limits::max)(); diff --git a/test/test.cpp b/test/test.cpp index 0cc8aab..c88b6ca 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -54,7 +54,7 @@ enum number : unsigned long { #endif }; -namespace magic_enum { +namespace magic_enum::customize { template <> struct enum_range { static constexpr int min = 100; @@ -62,13 +62,23 @@ struct enum_range { }; } // namespace magic_enum +template <> +constexpr std::string_view magic_enum::customize::enum_name(Color value) noexcept { + switch (value) { + case Color::RED: + return "red"; + default: + return {}; + } +} + using namespace magic_enum; static_assert(is_magic_enum_supported, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); TEST_CASE("enum_cast") { SECTION("string") { - constexpr auto cr = enum_cast("RED"); + constexpr auto cr = enum_cast("red"); REQUIRE(cr.value() == Color::RED); REQUIRE(enum_cast("GREEN").value() == Color::GREEN); REQUIRE(enum_cast("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE); @@ -257,7 +267,7 @@ TEST_CASE("enum_contains") { } SECTION("string") { - constexpr auto cr = "RED"; + constexpr auto cr = "red"; REQUIRE(enum_contains(cr)); REQUIRE(enum_contains("GREEN")); REQUIRE(enum_contains("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); @@ -345,7 +355,7 @@ TEST_CASE("enum_name") { constexpr auto cr_name = enum_name(cr); Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; Color cb = Color::BLUE; - REQUIRE(cr_name == "RED"); + REQUIRE(cr_name == "red"); REQUIRE(enum_name(cb) == "BLUE"); REQUIRE(enum_name(cm[1]) == "GREEN"); REQUIRE(enum_name(static_cast(0)).empty()); @@ -380,7 +390,7 @@ TEST_CASE("enum_name") { constexpr Color cr = Color::RED; constexpr auto cr_name = enum_name(); constexpr Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; - REQUIRE(cr_name == "RED"); + REQUIRE(cr_name == "red"); REQUIRE(enum_name() == "BLUE"); REQUIRE(enum_name() == "GREEN"); @@ -411,7 +421,7 @@ TEST_CASE("enum_names") { REQUIRE(std::is_same_v()), const std::array&>); constexpr auto& s1 = enum_names(); - REQUIRE(s1 == std::array{{"RED", "GREEN", "BLUE"}}); + REQUIRE(s1 == std::array{{"red", "GREEN", "BLUE"}}); constexpr auto& s2 = enum_names(); REQUIRE(s2 == std::array{{"one", "two", "three"}}); @@ -427,7 +437,7 @@ TEST_CASE("enum_entries") { REQUIRE(std::is_same_v()), const std::array, 3>&>); constexpr auto& s1 = enum_entries(); - REQUIRE(s1 == std::array, 3>{{{Color::RED, "RED"}, {Color::GREEN, "GREEN"}, {Color::BLUE, "BLUE"}}}); + REQUIRE(s1 == std::array, 3>{{{Color::RED, "red"}, {Color::GREEN, "GREEN"}, {Color::BLUE, "BLUE"}}}); constexpr auto& s2 = enum_entries(); REQUIRE(s2 == std::array, 3>{{{Numbers::one, "one"}, {Numbers::two, "two"}, {Numbers::three, "three"}}}); @@ -447,7 +457,7 @@ TEST_CASE("ostream_operators") { REQUIRE(ss.str() == name); }; - test_ostream(std::make_optional(Color::RED), "RED"); + test_ostream(std::make_optional(Color::RED), "red"); test_ostream(Color::GREEN, "GREEN"); test_ostream(Color::BLUE, "BLUE"); test_ostream(static_cast(0), "0"); @@ -619,45 +629,45 @@ TEST_CASE("extrema") { REQUIRE_FALSE(magic_enum::enum_contains(BadColor::NONE)); SECTION("min") { - REQUIRE(magic_enum::enum_range::min == MAGIC_ENUM_RANGE_MIN); + REQUIRE(magic_enum::customize::enum_range::min == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::reflected_min_v == 0); REQUIRE(magic_enum::detail::min_v == 0); - REQUIRE(magic_enum::enum_range::min == MAGIC_ENUM_RANGE_MIN); + REQUIRE(magic_enum::customize::enum_range::min == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::reflected_min_v == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::min_v == -12); - REQUIRE(magic_enum::enum_range::min == MAGIC_ENUM_RANGE_MIN); + REQUIRE(magic_enum::customize::enum_range::min == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::reflected_min_v == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::min_v == 1); - REQUIRE(magic_enum::enum_range::min == MAGIC_ENUM_RANGE_MIN); + REQUIRE(magic_enum::customize::enum_range::min == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::reflected_min_v == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::min_v == -120); - REQUIRE(magic_enum::enum_range::min == 100); + REQUIRE(magic_enum::customize::enum_range::min == 100); REQUIRE(magic_enum::detail::reflected_min_v == 100); REQUIRE(magic_enum::detail::min_v == 100); } SECTION("max") { - REQUIRE(magic_enum::enum_range::max == MAGIC_ENUM_RANGE_MAX); + REQUIRE(magic_enum::customize::enum_range::max == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::reflected_max_v == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::max_v == 2); - REQUIRE(magic_enum::enum_range::max == MAGIC_ENUM_RANGE_MAX); + REQUIRE(magic_enum::customize::enum_range::max == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::reflected_max_v == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::max_v == 15); - REQUIRE(magic_enum::enum_range::max == MAGIC_ENUM_RANGE_MAX); + REQUIRE(magic_enum::customize::enum_range::max == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::reflected_max_v == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::max_v == 3); - REQUIRE(magic_enum::enum_range::max == MAGIC_ENUM_RANGE_MAX); + REQUIRE(magic_enum::customize::enum_range::max == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::reflected_max_v == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::max_v == 120); - REQUIRE(magic_enum::enum_range::max == 300); + REQUIRE(magic_enum::customize::enum_range::max == 300); REQUIRE(magic_enum::detail::reflected_max_v == 300); REQUIRE(magic_enum::detail::max_v == 300); } diff --git a/test/test_flags.cpp b/test/test_flags.cpp index 6686697..9975041 100644 --- a/test/test_flags.cpp +++ b/test/test_flags.cpp @@ -69,7 +69,7 @@ enum number : unsigned long { #endif }; -namespace magic_enum { +namespace magic_enum::customize { template <> struct enum_range { static constexpr int min = 100;