From 0ccc3b4f3b701257f1a343cc1fd9f08848e16dc0 Mon Sep 17 00:00:00 2001 From: Daniil Goncharov Date: Tue, 1 Sep 2020 14:00:59 +0300 Subject: [PATCH] clean-up --- example/example.cpp | 4 +- include/nameof.hpp | 405 ++++++++++++++++++++++++-------------------- test/test.cpp | 76 +++++---- 3 files changed, 264 insertions(+), 221 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 53f694b..e005967 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -129,8 +129,8 @@ int main() { // Nameof enum flags. AnimalFlags flag = static_cast(AnimalFlags::CanFly | AnimalFlags::EatsFish); - std::cout << nameof::nameof_enum_flag(flag) << std::endl; // 'CanFly|EatsFish' - std::cout << NAMEOF_ENUM_FLAG(flag) << std::endl; // 'CanFly|EatsFish' + std::cout << nameof::nameof_enum_flags(flag) << std::endl; // 'CanFly|EatsFish' + std::cout << NAMEOF_ENUM_FLAGS(flag) << std::endl; // 'CanFly|EatsFish' #endif // Nameof. diff --git a/include/nameof.hpp b/include/nameof.hpp index 0c0e71d..f192e45 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -43,23 +43,25 @@ #include #include #include -#include -#include #include #include +#if !defined(NAMEOF_USING_ALIAS_STRING) +#include +#endif +#if !defined(NAMEOF_USING_ALIAS_STRING_VIEW) +#include +#endif + #if __has_include() #include #include -#include #endif #if defined(__clang__) # pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wsign-conversion" // Implicit conversion changes signedness: 'int' to 'size_t'. #elif defined(__GNUC__) # pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wsign-conversion" // Implicit conversion changes signedness: 'int' to 'size_t'. # pragma GCC diagnostic ignored "-Wstringop-overflow" // Missing terminating nul 'enum_name_v'. #elif defined(_MSC_VER) # pragma warning(push) @@ -96,6 +98,12 @@ # define NAMEOF_ENUM_SUPPORTED 1 #endif +// Checks nameof_enum compiler aliases compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 +# undef NAMEOF_ENUM_SUPPORTED_ALIASES +# define NAMEOF_ENUM_SUPPORTED_ALIASES 1 +#endif + // Enum value must be greater or equals than NAMEOF_ENUM_RANGE_MIN. By default NAMEOF_ENUM_RANGE_MIN = -128. // If need another min range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN. #if !defined(NAMEOF_ENUM_RANGE_MIN) @@ -280,13 +288,27 @@ template template std::basic_ostream& operator<<(std::basic_ostream& os, const cstring& srt) { - for (const auto c : std::string_view{srt}) { + for (const auto c : srt) { os.put(c); } return os; } +// If need another optional type, define the macro NAMEOF_USING_ALIAS_STRING_VIEW. +#if defined(NAMEOF_USING_ALIAS_STRING_VIEW) +NAMEOF_USING_ALIAS_STRING_VIEW +#else +using string_view = std::string_view; +#endif + +// If need another optional type, define the macro NAMEOF_USING_ALIAS_STRING. +#if defined(NAMEOF_USING_ALIAS_STRING) +NAMEOF_USING_ALIAS_STRING +#else +using string = std::string; +#endif + namespace detail { constexpr std::string_view pretty_name(std::string_view name, bool remove_template_suffix = true) noexcept { @@ -368,6 +390,32 @@ constexpr std::string_view pretty_name(std::string_view name, bool remove_templa return {}; // Invalid name. } +template +constexpr bool cmp_less(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::cmp_less requires integral type."); + + if constexpr (std::is_signed_v == std::is_signed_v) { + // If same signedness (both signed or both unsigned). + return lhs < rhs; + } else if constexpr (std::is_signed_v) { + // If 'right' is negative, then result is 'false', otherwise cast & compare. + return rhs > 0 && lhs < static_cast>(rhs); + } else { + // If 'left' is negative, then result is 'true', otherwise cast & compare. + return lhs < 0 || static_cast>(lhs) < rhs; + } +} + +template +constexpr I log2(I value) noexcept { + static_assert(std::is_integral_v, "nameof::detail::log2 requires integral type."); + + auto ret = I{0}; + for (; value > I{1}; value >>= I{1}, ++ret) {}; + + return ret; +} + template struct nameof_enum_supported #if defined(NAMEOF_ENUM_SUPPORTED) && NAMEOF_ENUM_SUPPORTED || defined(NAMEOF_ENUM_NO_CHECK_SUPPORT) @@ -382,9 +430,6 @@ using enable_if_enum_t = std::enable_if_t>, R>; template inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; -#if defined(NEARGYE_MAGIC_ENUM_HPP) -using ::magic_enum::detail::enum_name_v; -#else template constexpr auto n() noexcept { static_assert(is_enum_v, "nameof::detail::n requires enum type."); @@ -406,7 +451,6 @@ constexpr auto n() noexcept { template inline constexpr auto enum_name_v = n(); -#endif template constexpr bool is_valid() noexcept { @@ -415,188 +459,179 @@ constexpr bool is_valid() noexcept { return n(V)>().size() != 0; } -template -constexpr bool cmp_less(L lhs, R rhs) noexcept { - static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::cmp_less requires integral type."); - - if constexpr (std::is_signed_v == std::is_signed_v) { - // If same signedness (both signed or both unsigned). - return lhs < rhs; - } else if constexpr (std::is_signed_v) { - // If 'right' is negative, then result is 'false', otherwise cast & compare. - return rhs > 0 && lhs < static_cast>(rhs); - } else { - // If 'left' is negative, then result is 'true', otherwise cast & compare. - return lhs < 0 || static_cast>(lhs) < rhs; - } -} - -template -constexpr std::size_t range_size() noexcept { - static_assert(is_enum_v, "nameof::detail::range_size requires enum type."); - constexpr auto size = Max - Min + 1; - static_assert(size > 0, "nameof::enum_range requires valid size."); - static_assert(size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); - - return static_cast(size); -} - -template -constexpr auto reflected_min() noexcept { +template > +constexpr int reflected_min() noexcept { static_assert(is_enum_v, "nameof::detail::reflected_min requires enum type."); - constexpr auto lhs = enum_range::min; - static_assert(lhs > (std::numeric_limits::min)(), "nameof::enum_range requires min must be greater than INT16_MIN."); - constexpr auto rhs = (std::numeric_limits>::min)(); - return cmp_less(lhs, rhs) ? rhs : lhs; -} + if constexpr (IsFlags) { + return 0; + } else { + constexpr auto lhs = enum_range::min; + static_assert(lhs > (std::numeric_limits::min)(), "nameof::enum_range requires min must be greater than INT16_MIN."); + constexpr auto rhs = (std::numeric_limits::min)(); -template -constexpr auto reflected_max() noexcept { - static_assert(is_enum_v, "nameof::detail::reflected_max requires enum type."); - constexpr auto lhs = enum_range::max; - static_assert(lhs < (std::numeric_limits::max)(), "nameof::enum_range requires max must be less than INT16_MAX."); - constexpr auto rhs = (std::numeric_limits>::max)(); - - return cmp_less(lhs, rhs) ? lhs : rhs; -} - -template -inline constexpr auto reflected_min_v = reflected_min(); - -template -inline constexpr auto reflected_max_v = reflected_max(); - -template -constexpr auto values(std::integer_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::values requires enum type."); - constexpr std::array valid{{is_valid>()...}}; - constexpr int count = ((valid[I] ? 1 : 0) + ...); - - std::array values{}; - for (int i = 0, v = 0; v < count; ++i) { - if (valid[i]) { - values[v++] = static_cast(i + reflected_min_v); + if constexpr (cmp_less(lhs, rhs)) { + return rhs; + } else { + return lhs; } } - - return values; } -template -inline constexpr auto values_v = values(std::make_integer_sequence, reflected_max_v>()>{}); +template > +constexpr int reflected_max() noexcept { + static_assert(is_enum_v, "nameof::detail::reflected_max requires enum type."); -template -inline constexpr auto count_v = values_v.size(); - -template -inline constexpr auto min_v = static_cast(values_v.front()); - -template -inline constexpr auto max_v = static_cast(values_v.back()); - -template -inline constexpr auto range_size_v = range_size, max_v>(); - -template -using index_t = std::conditional_t < (std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; - -template -inline constexpr auto invalid_index_v = (std::numeric_limits>::max)(); - -template -constexpr auto indexes(std::integer_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::indexes requires enum type."); - [[maybe_unused]] index_t i = 0; - - return std::array, sizeof...(I)>{{(is_valid>() ? i++ : invalid_index_v)...}}; -} - -template -inline constexpr auto indexes_v = indexes(std::make_integer_sequence>{}); - -template -constexpr auto strings(std::integer_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::strings requires enum type."); - - return std::array{{enum_name_v(I + min_v)>.data()...}}; -} - -template -constexpr auto strings(std::index_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::strings requires enum type."); - - return std::array{{enum_name_v[I]>.data()...}}; -} - -template -inline constexpr bool sparsity_v = (sizeof(const char*) * range_size_v) > (sizeof(index_t) * range_size_v + sizeof(const char*) * count_v); - -template -constexpr auto strings() noexcept { - static_assert(is_enum_v, "nameof::detail::strings requires enum type."); - - if constexpr (sparsity_v) { - return strings(std::make_index_sequence>{}); + if constexpr (IsFlags) { + return std::numeric_limits::digits - 1; } else { - return strings(std::make_integer_sequence>{}); + constexpr auto lhs = enum_range::max; + static_assert(lhs < (std::numeric_limits::max)(), "nameof::enum_range requires max must be less than INT16_MAX."); + constexpr auto rhs = (std::numeric_limits::max)(); + + if constexpr (cmp_less(lhs, rhs)) { + return lhs; + } else { + return rhs; + } } } -template -inline static constexpr auto strings_v = strings(); +template +inline constexpr auto reflected_min_v = reflected_min(); -template > -constexpr std::uint8_t log2(E value) noexcept { - auto ret = std::uint8_t{0}; - for (auto x = static_cast(value); x > static_cast(1U); x >>= static_cast(1U), ++ret) {}; +template +inline constexpr auto reflected_max_v = reflected_max(); - return ret; +template > +constexpr E value(std::size_t i) noexcept { + static_assert(is_enum_v, "nameof::detail::value requires enum type."); + + if constexpr (IsFlags) { + return static_cast(U{1} << static_cast(static_cast(i) + O)); + } else { + return static_cast(static_cast(i) + O); + } } -template > -constexpr E flag_value(std::size_t v) noexcept { - return static_cast(static_cast(1U) << static_cast(v)); -} - -template , U... I> -constexpr auto flags_values(std::integer_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::flags_values requires enum type."); - constexpr std::array valid{{is_valid(I)>()...}}; - constexpr std::size_t count = (valid[I] + ...); - static_assert(count <= std::numeric_limits::digits, "nameof::detail::flags_values requires valid count."); +template +constexpr auto values(std::index_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::values requires enum type."); + constexpr std::array valid{{is_valid(I)>()...}}; + constexpr std::size_t count = ((valid[I] ? std::size_t{1} : std::size_t{0}) + ...); std::array values{}; for (std::size_t i = 0, v = 0; v < count; ++i) { if (valid[i]) { - values[v++] = flag_value(i); + values[v++] = value(i); } } return values; } -template > -inline constexpr auto flags_values_v = flags_values(std::make_integer_sequence::digits>{}); +template > +constexpr auto values() noexcept { + static_assert(is_enum_v, "nameof::detail::values requires enum type."); + constexpr auto range_size = reflected_max_v - reflected_min_v + 1; + static_assert(range_size > 0, "nameof::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); -template -inline constexpr auto flags_count_v = flags_values_v.size(); - -template -inline constexpr auto flags_min_v = log2(flags_values_v.front()); - -template -inline constexpr auto flags_max_v = log2(flags_values_v.back()); - -template , U... I> -constexpr auto flags_strings(std::integer_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::flags_strings requires enum type."); - - return std::array{{enum_name_v(I + Min)>.data()...}}; + return values>(std::make_index_sequence{}); } -template > -inline constexpr auto flags_strings_v = flags_strings>(std::make_integer_sequence, flags_max_v>()>{}); +template +inline constexpr auto values_v = values(); + +template > +using values_t = decltype((values_v)); + +template +inline constexpr auto count_v = values_v.size(); + +template > +inline constexpr auto min_v = static_cast(values_v.front()); + +template > +inline constexpr auto max_v = static_cast(values_v.back()); + +template > +constexpr std::size_t range_size() noexcept { + static_assert(is_enum_v, "nameof::detail::range_size requires enum type."); + constexpr auto max = IsFlags ? log2(max_v) : max_v; + constexpr auto min = IsFlags ? log2(min_v) : min_v; + constexpr auto range_size = max - min + U{1}; + static_assert(range_size > 0, "nameof::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); + + return static_cast(range_size); +} + +template +inline constexpr auto range_size_v = range_size(); + +template +using index_t = std::conditional_t < (std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; + +template +inline constexpr auto invalid_index_v = (std::numeric_limits>::max)(); + +template +constexpr auto indexes(std::index_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::indexes requires enum type."); + constexpr auto min = IsFlags ? log2(min_v) : min_v; + [[maybe_unused]] auto i = index_t{0}; + + return std::array{{(is_valid(I)>() ? i++ : invalid_index_v)...}}; +} + +template +inline constexpr auto indexes_v = indexes(std::make_index_sequence>{}); + +template > +constexpr bool is_sparse() noexcept { + static_assert(is_enum_v, "nameof::detail::is_sparse requires enum type."); + + if constexpr (IsFlags) { + return (sizeof(const char*) * range_size_v) > (sizeof(E) * count_v + sizeof(const char*) * count_v); + } else { + return (sizeof(const char*) * range_size_v) > (sizeof(index_t) * range_size_v + sizeof(const char*) * count_v); + } +} + +template +inline constexpr bool is_sparse_v = is_sparse(); + +template > +[[nodiscard]] constexpr E get_value(std::size_t i) noexcept { + static_assert(is_enum_v, "nameof::detail::strings requires enum type."); + + if constexpr (is_sparse_v) { + return values_v[i]; + } else { + constexpr auto min = IsFlags ? log2(min_v) : min_v; + + return value(i); + } +} + +template +constexpr auto strings(std::index_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::strings requires enum type."); + + return std::array{{enum_name_v(I)>.data()...}}; +} + +template +constexpr auto strings() noexcept { + static_assert(is_enum_v, "nameof::detail::strings requires enum type."); + constexpr auto count = is_sparse_v ? count_v : range_size_v; + + return strings(std::make_index_sequence{}); +} + +template +inline static constexpr auto strings_v = strings(); template struct nameof_type_supported @@ -606,17 +641,19 @@ struct nameof_type_supported : std::false_type {}; #endif +#if defined(_MSC_VER) template struct identity { using type = T; }; +#else +template +using identity = T; +#endif template using remove_cvref_t = std::remove_cv_t>; -#if defined(NEARGYE_MAGIC_ENUM_HPP) -using ::magic_enum::detail::type_name_v; -#else template constexpr auto n() noexcept { #if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED @@ -636,7 +673,6 @@ constexpr auto n() noexcept { template inline constexpr auto type_name_v = n(); -#endif #if __has_include() inline std::string demangle(const char* tn) { @@ -670,7 +706,7 @@ template if (const auto i = static_cast(value) - detail::min_v; static_cast(value) >= static_cast(detail::min_v) && static_cast(value) <= static_cast(detail::max_v)) { - if constexpr (detail::sparsity_v) { + if constexpr (detail::is_sparse_v) { if (const auto idx = detail::indexes_v[i]; idx != detail::invalid_index_v) { return detail::strings_v[idx]; } @@ -682,18 +718,21 @@ template return {}; // Value out of range. } -// Obtains simple (unqualified) string name of enum flag variable. +// Obtains simple (unqualified) string name of enum-flags variable. template -[[nodiscard]] auto nameof_enum_flag(E value) -> detail::enable_if_enum_t { +[[nodiscard]] auto nameof_enum_flags(E value) -> detail::enable_if_enum_t { using D = std::decay_t; using U = std::underlying_type_t; - static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_flag unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - static_assert(detail::flags_count_v > 0, "nameof::nameof_enum_flag requires enum flag implementation."); + static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_flags unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + static_assert(detail::count_v > 0, "nameof::nameof_enum_flags requires enum flag implementation."); + constexpr auto size = detail::is_sparse_v ? detail::count_v : detail::range_size_v; std::string name; - for (auto i = detail::flags_min_v; i <= detail::flags_max_v; ++i) { - if (const auto v = (static_cast(1U) << static_cast(i)); (static_cast(value) & v) != 0) { - if (const auto n = detail::flags_strings_v[i]; n != nullptr) { + auto check_value = U{0}; + for (std::size_t i = 0; i < size; ++i) { + if (const auto v = static_cast(detail::get_value(i)); (static_cast(value) & v) != 0) { + if (const auto n = detail::strings_v[i]; n != nullptr) { + check_value |= v; if (!name.empty()) { name.append(1, '|'); } @@ -704,7 +743,11 @@ template } } - return name; + if (check_value != 0 && check_value == static_cast(value)) { + return name; + } + + return {}; // Invalid value or out of range. } // Obtains simple (unqualified) string name of static storage enum variable. @@ -723,11 +766,7 @@ template template [[nodiscard]] constexpr std::string_view nameof_type() noexcept { static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); -#if defined(_MSC_VER) using U = detail::identity>; -#else - using U = detail::remove_cvref_t; -#endif constexpr std::string_view name = detail::type_name_v; static_assert(name.size() > 0, "Type does not have a name."); @@ -738,11 +777,7 @@ template template [[nodiscard]] constexpr std::string_view nameof_full_type() noexcept { static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); -#if defined(_MSC_VER) using U = detail::identity; -#else - using U = T; -#endif constexpr std::string_view name = detail::type_name_v; static_assert(name.size() > 0, "Type does not have a name."); @@ -786,7 +821,7 @@ template #define NAMEOF_CONST_ENUM(...) ::nameof::nameof_enum<__VA_ARGS__>() // Obtains simple (unqualified) string name of enum flag variable. -#define NAMEOF_ENUM_FLAG(...) ::nameof::nameof_enum_flag<::std::decay_t>(__VA_ARGS__) +#define NAMEOF_ENUM_FLAGS(...) ::nameof::nameof_enum_flags<::std::decay_t>(__VA_ARGS__) // Obtains string name of type, reference and cv-qualifiers are ignored. #define NAMEOF_TYPE(...) ::nameof::nameof_type<__VA_ARGS__>() diff --git a/test/test.cpp b/test/test.cpp index 0d7b12e..dc0f472 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -373,56 +373,64 @@ TEST_CASE("nameof_enum") { } } -TEST_CASE("nameof_enum_flag") { +TEST_CASE("nameof_enum_flags") { constexpr AnimalFlags af = AnimalFlags::HasClaws; - auto af_name = nameof::nameof_enum_flag(af); + auto af_name = nameof::nameof_enum_flags(af); AnimalFlags afm[3] = {AnimalFlags::HasClaws, AnimalFlags::CanFly, AnimalFlags::EatsFish}; REQUIRE(af_name == "HasClaws"); - REQUIRE(nameof::nameof_enum_flag(AnimalFlags::EatsFish) == "EatsFish"); - REQUIRE(nameof::nameof_enum_flag(afm[1]) == "CanFly"); - REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2)) == "HasClaws|CanFly"); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); + REQUIRE(nameof::nameof_enum_flags(AnimalFlags::EatsFish) == "EatsFish"); + REQUIRE(nameof::nameof_enum_flags(afm[1]) == "CanFly"); + REQUIRE(nameof::nameof_enum_flags(static_cast(0)).empty()); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | 2)) == "HasClaws|CanFly"); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); + REQUIRE(nameof::nameof_enum_flags(static_cast(0)).empty()); constexpr BigFlags bf = BigFlags::A; - auto bf_name = nameof::nameof_enum_flag(bf); + auto bf_name = nameof::nameof_enum_flags(bf); BigFlags bfm[3] = {BigFlags::A, BigFlags::B, BigFlags::C}; REQUIRE(bf_name == "A"); - REQUIRE(nameof::nameof_enum_flag(BigFlags::C) == "C"); - REQUIRE(nameof::nameof_enum_flag(bfm[1]) == "B"); - REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2)).empty()); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); + REQUIRE(nameof::nameof_enum_flags(BigFlags::C) == "C"); + REQUIRE(nameof::nameof_enum_flags(bfm[1]) == "B"); + REQUIRE(nameof::nameof_enum_flags(static_cast(0)).empty()); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | 2)).empty()); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); + REQUIRE(nameof::nameof_enum_flags(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); + REQUIRE(nameof::nameof_enum_flags(static_cast((static_cast(0x1) << 63) | 1)) == "A|D"); + REQUIRE(nameof::nameof_enum_flags(static_cast(2)).empty()); + REQUIRE(nameof::nameof_enum_flags(static_cast((static_cast(0x1) << 63) | 2)).empty()); } -TEST_CASE("NAMEOF_ENUM_FLAG") { +TEST_CASE("NAMEOF_ENUM_FLAGS") { constexpr AnimalFlags af = AnimalFlags::HasClaws; - auto af_name = NAMEOF_ENUM_FLAG(af); + auto af_name = NAMEOF_ENUM_FLAGS(af); AnimalFlags afm[3] = {AnimalFlags::HasClaws, AnimalFlags::CanFly, AnimalFlags::EatsFish}; REQUIRE(af_name == "HasClaws"); - REQUIRE(NAMEOF_ENUM_FLAG(afm[1]) == "CanFly"); - REQUIRE(NAMEOF_ENUM_FLAG(AnimalFlags::EatsFish) == "EatsFish"); - REQUIRE(NAMEOF_ENUM_FLAG(AnimalFlags::Endangered) == "Endangered"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2)) == "HasClaws|CanFly"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); + REQUIRE(NAMEOF_ENUM_FLAGS(afm[1]) == "CanFly"); + REQUIRE(NAMEOF_ENUM_FLAGS(AnimalFlags::EatsFish) == "EatsFish"); + REQUIRE(NAMEOF_ENUM_FLAGS(AnimalFlags::Endangered) == "Endangered"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(0)).empty()); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(1 | 2)) == "HasClaws|CanFly"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(0)).empty()); constexpr BigFlags bf = BigFlags::A; - auto bf_name = NAMEOF_ENUM_FLAG(bf); + auto bf_name = NAMEOF_ENUM_FLAGS(bf); BigFlags bfm[3] = {BigFlags::A, BigFlags::B, BigFlags::C}; REQUIRE(bf_name == "A"); - REQUIRE(NAMEOF_ENUM_FLAG(bfm[1]) == "B"); - REQUIRE(NAMEOF_ENUM_FLAG(BigFlags::C) == "C"); - REQUIRE(NAMEOF_ENUM_FLAG(BigFlags::D) == "D"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2)).empty()); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); + REQUIRE(NAMEOF_ENUM_FLAGS(bfm[1]) == "B"); + REQUIRE(NAMEOF_ENUM_FLAGS(BigFlags::C) == "C"); + REQUIRE(NAMEOF_ENUM_FLAGS(BigFlags::D) == "D"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(0)).empty()); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(1 | 2)).empty()); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast((static_cast(0x1) << 63) | 1)) == "A|D"); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast(2)).empty()); + REQUIRE(NAMEOF_ENUM_FLAGS(static_cast((static_cast(0x1) << 63) | 2)).empty()); } #endif