diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c9dae9d..5306bc9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -9,7 +9,6 @@ jobs: fail-fast: false matrix: config: - - { os: windows-2016, vs: "Visual Studio 2017" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md#visual-studio-enterprise-2017 - { os: windows-2019, vs: "Visual Studio 2019" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#visual-studio-enterprise-2019 - { os: windows-2022, vs: "Visual Studio 2022" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2022-Readme.md#visual-studio-enterprise-2022 build: [Debug, Release] diff --git a/README.md b/README.md index 9d30373..5670807 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,7 @@ enum class Color { RED = 2, BLUE = 4, GREEN = 8 }; ## Compiler compatibility -* Clang/LLVM >= 5 +* Clang/LLVM >= 6 * MSVC++ >= 14.11 / Visual Studio >= 2017 * Xcode >= 10 * GCC >= 9 diff --git a/example/example_custom_name.cpp b/example/example_custom_name.cpp index 6d27b74..0e7451b 100644 --- a/example/example_custom_name.cpp +++ b/example/example_custom_name.cpp @@ -29,16 +29,16 @@ enum class Color : int { RED = -10, BLUE = 0, GREEN = 10 }; // С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(Color value) noexcept { +constexpr magic_enum::customize::customize_t 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 invalid_tag; } - return {}; // Empty string for unknow value. + return default_tag; } enum class Numbers : int { One, Two, Three }; @@ -46,26 +46,26 @@ 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 { +constexpr magic_enum::customize::customize_t 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. + return default_tag; } } 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 << 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; // '' 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 + 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 5439e03..7b88bd8 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #if defined(MAGIC_ENUM_CONFIG_FILE) #include MAGIC_ENUM_CONFIG_FILE @@ -134,10 +135,28 @@ struct enum_range { static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); static_assert((MAGIC_ENUM_RANGE_MAX - MAGIC_ENUM_RANGE_MIN) < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE must be less than UINT16_MAX."); +namespace detail { +enum class default_customize_tag {}; +enum class invalid_customize_tag {}; +} // namespace magic_enum::customize::detail + +using customize_t = std::variant; + +// Default customize. +inline constexpr auto default_tag = detail::default_customize_tag{}; +// Invalid customize. +inline constexpr auto invalid_tag = detail::invalid_customize_tag{}; + // If need custom names for enum, add specialization enum_name for necessary enum type. template -constexpr string_view enum_name(E) noexcept { - return {}; +constexpr customize_t enum_name(E) noexcept { + return default_tag; +} + +// If need custom type name for enum, add specialization enum_type_name for necessary enum type. +template +constexpr customize_t enum_type_name() noexcept { + return default_tag; } } // namespace magic_enum::customize @@ -196,6 +215,8 @@ class static_string { template <> class static_string<0> { public: + constexpr explicit static_string() = default; + constexpr explicit static_string(string_view) noexcept {} constexpr const char* data() const noexcept { return nullptr; } @@ -343,7 +364,13 @@ template constexpr auto n() noexcept { static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); - if constexpr (supported::value) { + [[maybe_unused]] constexpr auto custom = customize::enum_type_name(); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.index() == 0) { + constexpr auto name = std::get(custom); + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return static_string{name}; + } else if constexpr (custom.index() == 1 && supported::value) { #if defined(__clang__) || defined(__GNUC__) constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); #elif defined(_MSC_VER) @@ -353,7 +380,7 @@ constexpr auto n() noexcept { #endif return static_string{name}; } else { - return string_view{}; // Unsupported compiler. + return static_string<0>{}; // Unsupported compiler or Invalid customize. } } @@ -363,23 +390,24 @@ inline constexpr auto type_name_v = n(); template constexpr auto n() noexcept { static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); - [[maybe_unused]] constexpr auto custom_name = customize::enum_name(V); - if constexpr (custom_name.empty()) { - if constexpr (supported::value) { + [[maybe_unused]] constexpr auto custom = customize::enum_name(V); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.index() == 0) { + constexpr auto name = std::get(custom); + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return static_string{name}; + } else if constexpr (custom.index() == 1 && supported::value) { #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}); #else - constexpr auto name = string_view{}; + constexpr auto name = string_view{}; #endif - return static_string{name}; - } else { - return string_view{}; // Unsupported compiler. - } + return static_string{name}; } else { - return static_string{custom_name}; + return static_string<0>{}; // Unsupported compiler or Invalid customize. } } @@ -943,7 +971,7 @@ template template [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - if (const auto index = enum_index(value); index.has_value()) { + if (const auto index = enum_index(value)) { return detail::names_v[*index]; } return {}; @@ -1089,7 +1117,7 @@ template > template [[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_enum_t { constexpr auto index = enum_index>(V); - static_assert(index.has_value(), "magic_enum::enum_index enum value does not have a index."); + static_assert(index, "magic_enum::enum_index enum value does not have a index."); return *index; } @@ -1100,13 +1128,13 @@ template using D = std::decay_t; using U = underlying_type_t; - return enum_cast(static_cast(value)).has_value(); + return static_cast(enum_cast(static_cast(value))); } // Checks whether enum contains enumerator with such integer value. template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_t { - return enum_cast>(value).has_value(); + return static_cast(enum_cast>(value)); } // Checks whether enum contains enumerator with such name. @@ -1114,15 +1142,15 @@ template > [[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t { static_assert(std::is_invocable_r_v, "magic_enum::enum_contains requires bool(char, char) invocable predicate."); - return enum_cast>(value, std::move_if_noexcept(p)).has_value(); + return static_cast(enum_cast>(value, std::move_if_noexcept(p))); } namespace detail { template constexpr optional fuse_one_enum(optional hash, E value) noexcept { - if (hash.has_value()) { - if (const auto index = enum_index(value); index.has_value()) { + if (hash) { + if (const auto index = enum_index(value)) { return (*hash << log2(enum_count() + 1)) | *index; } } @@ -1143,7 +1171,7 @@ template constexpr auto typesafe_fuse_enum(Es... values) noexcept { enum class enum_fuse_t : std::uintmax_t; const auto fuse = fuse_enum(values...); - if (fuse.has_value()) { + if (fuse) { return optional{static_cast(*fuse)}; } return optional{}; @@ -1162,7 +1190,7 @@ template #else const auto fuse = detail::typesafe_fuse_enum...>(values...); #endif - return assert(fuse.has_value()), fuse; + return assert(fuse), fuse; } namespace ostream_operators { @@ -1185,7 +1213,7 @@ std::basic_ostream& operator<<(std::basic_ostream& o template = 0> std::basic_ostream& operator<<(std::basic_ostream& os, optional value) { - return value.has_value() ? (os << *value) : os; + return value ? (os << *value) : os; } } // namespace magic_enum::ostream_operators diff --git a/test/test.cpp b/test/test.cpp index a3d1d49..1b9f219 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -35,6 +35,15 @@ #include enum class Color { RED = -12, GREEN = 7, BLUE = 15 }; +template <> +constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name(Color value) noexcept { + switch (value) { + case Color::RED: + return "red"; + default: + return default_tag; + } +} enum class Numbers : int { one = 1, two, three, many = 127 }; @@ -94,16 +103,6 @@ struct magic_enum::customize::enum_range { static constexpr int max = 64; }; -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).");