From 74dfbd65283f58415ba9aa8fee0dfdf24d9020ad Mon Sep 17 00:00:00 2001 From: Daniil Goncharov Date: Fri, 3 Jul 2020 18:18:05 +0500 Subject: [PATCH] add nameof_enum_flag --- .travis.yml | 52 +++---------- README.md | 6 +- doc/reference.md | 62 ++++++++++++--- example/example.cpp | 7 ++ include/nameof.hpp | 185 +++++++++++++++++++++++++++++++++----------- test/test.cpp | 66 ++++++++++++++++ 6 files changed, 280 insertions(+), 98 deletions(-) diff --git a/.travis.yml b/.travis.yml index da87dee..38c5441 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ os: linux # Use linux unless specified otherwise. -dist: xenial +dist: bionic sudo: required language: cpp @@ -44,39 +44,9 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test - - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-5.0 main' + - sourceline: 'deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-7 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: - - g++-7 - - clang-5.0 - env: - - CXX_COMPILER=clang++-5.0 CC_COMPILER=clang-5.0 - - - os: linux - compiler: clang++ - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-6.0 main' - key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' - packages: - - g++-7 - - clang-6.0 - env: - - CXX_COMPILER=clang++-6.0 CC_COMPILER=clang-6.0 - - - os: linux - compiler: clang++ - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-7 main' - key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' - packages: - - g++-7 - clang-7 env: - CXX_COMPILER=clang++-7 CC_COMPILER=clang-7 @@ -86,11 +56,9 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test - - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main' + - sourceline: 'deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: - - g++-7 - clang-8 env: - CXX_COMPILER=clang++-8 CC_COMPILER=clang-8 @@ -100,11 +68,9 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test - - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' + - sourceline: 'deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: - - g++-7 - clang-9 env: - CXX_COMPILER=clang++-9 CC_COMPILER=clang-9 @@ -114,11 +80,9 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test - - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main' + - sourceline: 'deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: - - g++-7 - clang-10 env: - CXX_COMPILER=clang++-10 CC_COMPILER=clang-10 @@ -135,6 +99,12 @@ matrix: env: - CXX_COMPILER=clang++ CC_COMPILER=clang + - os: osx + compiler: clang++ + osx_image: xcode12 + env: + - CXX_COMPILER=clang++ CC_COMPILER=clang + install: - export CC=${CC_COMPILER} - export CXX=${CXX_COMPILER} diff --git a/README.md b/README.md index f3baffd..980a975 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Header-only C++17 library provides nameof macros and functions to simply obtain * Nameof enum ```cpp - enum class Color { RED = 2, BLUE = 4, GREEN = 8 }; + enum class Color { RED = 1, BLUE = 2, GREEN = 4 }; auto color = Color::RED; // Name of enum variable. @@ -77,6 +77,10 @@ Header-only C++17 library provides nameof macros and functions to simply obtain // This version is much lighter on the compile times and is not restricted to the enum_range limitation. NAMEOF_CONST_ENUM(Color::GREEN) -> "GREEN" nameof::nameof_enum() -> "GREEN" + + // Enum flag variable to string. + NAMEOF_ENUM_FLAG(Color::GREEN | Color::BLUE) -> "GREEN|BLUE" + nameof::nameof_enum_flag() -> "GREEN|BLUE" ``` * Nameof type diff --git a/doc/reference.md b/doc/reference.md index 61c5f3e..9e0507a 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -3,9 +3,11 @@ * [`NAMEOF` macro that obtains simple (unqualified) string name of variable, function, macro.](#nameof) * [`NAMEOF_FULL` macro that obtains simple (unqualified) full (with template suffix) string name of variable, function, macro.](#nameof_full) * [`NAMEOF_RAW` macro that obtains raw string name of variable, function, macro.](#nameof_raw) -* [`nameof_enum` function that obtains simple (unqualified) string enum name of enum variable.](#nameof_enum) -* [`NAMEOF_ENUM` macro that obtains simple (unqualified) string enum name of enum variable.](#nameof_enum-1) -* [`NAMEOF_CONST_ENUM` macro that obtains simple (unqualified) string enum name of static storage enum variable.](#NAMEOF_CONST_ENUM) +* [`nameof_enum` function that obtains simple (unqualified) string name of enum variable.](#nameof_enum) +* [`NAMEOF_ENUM` macro that obtains simple (unqualified) string name of enum variable.](#nameof_enum-1) +* [`NAMEOF_CONST_ENUM` macro that obtains simple (unqualified) string name of static storage enum variable.](#nameof_const_enum) +* [`nameof_enum_flag` function that obtains simple (unqualified) string name of enum variable.](#nameof_enum_flag) +* [`NAMEOF_ENUM_FLAG` function that obtains simple (unqualified) string name of enum variable.](#nameof_enum_flag-1) * [`nameof_type` function that obtains string name of type, reference and cv-qualifiers are ignored.](#nameof_type) * [`nameof_full_type` function that obtains string name of full type, with reference and cv-qualifiers.](#nameof_full_type) * [`NAMEOF_TYPE` macro that obtains string name of type, reference and cv-qualifiers are ignored.](#nameof_type-1) @@ -90,7 +92,7 @@ ## `nameof_enum` -* Function that obtains simple (unqualified) string enum name of enum variable. +* Function that obtains simple (unqualified) string name of enum variable. * Returns `std::string_view`. @@ -119,7 +121,7 @@ ## `NAMEOF_ENUM` -* Macro that obtains simple (unqualified) string enum name of enum variable. +* Macro that obtains simple (unqualified) string name of enum variable. * Returns `std::string_view`. @@ -134,7 +136,7 @@ ## `NAMEOF_CONST_ENUM` -* Macro macro that obtains simple (unqualified) string enum name of static storage enum variable. +* Macro that obtains simple (unqualified) string name of static storage enum variable. * Returns `std::string_view`. @@ -148,9 +150,51 @@ NAMEOF_CONST_ENUM(Color::GREEN) -> "GREEN" ``` +## `nameof_enum_flag` + +* Function that obtains simple (unqualified) string name of enum flag variable. + +* Returns `std::string`. + +* If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `std::string_view`. + +* Examples + + ```cpp + enum class AnimalFlags { HasClaws = 1 << 0, CanFly = 1 << 1, EatsFish = 1 << 2, Endangered = 1 << 3 }; + auto flag = AnimalFlags::Endangered; + nameof_enum_flag(flag) -> "Endangered" + flag = AnimalFlags::Endangered | AnimalFlags::CanFly; + nameof_enum_flag(flag) -> "CanFly|Endangered" + nameof_enum_flag(flag) -> "HasClaws|CanFly" + + nameof_enum(HasClaws | CanFly) -> "" + ``` + +## `NAMEOF_ENUM_FLAG` + +* Macro that obtains simple (unqualified) string name of enum flag variable. + +* Returns `std::string`. + +* If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `std::string_view`. + +* Examples + + ```cpp + enum class AnimalFlags { HasClaws = 1 << 0, CanFly = 1 << 1, EatsFish = 1 << 2, Endangered = 1 << 3 }; + auto flag = AnimalFlags::Endangered; + NAMEOF_ENUM_FLAG(flag) -> "Endangered" + flag = AnimalFlags::Endangered | AnimalFlags::CanFly; + NAMEOF_ENUM_FLAG(flag) -> "CanFly|Endangered" + NAMEOF_ENUM_FLAG(flag) -> "HasClaws|CanFly" + + NAMEOF_ENUM(HasClaws | CanFly) -> "" + ``` + ## `nameof_type` -* Function macro that obtains string name of type, reference and cv-qualifiers are ignored. +* Function that obtains string name of type, reference and cv-qualifiers are ignored. * Returns `nameof::cstring` - constexpr implementation of an string. @@ -186,7 +230,7 @@ ## `NAMEOF_TYPE` -* Macro macro that obtains string name of type, reference and cv-qualifiers are ignored. +* Macro that obtains string name of type, reference and cv-qualifiers are ignored. * Returns `nameof::cstring` - constexpr implementation of an string. @@ -260,7 +304,7 @@ ## `NAMEOF_TYPE_RTTI` -* Macro macro that obtains string name of type, using RTTI. +* Macro that obtains string name of type, using RTTI. * Returns demangled RTTI type name. diff --git a/example/example.cpp b/example/example.cpp index c67c3a1..53f694b 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -83,6 +83,8 @@ struct Long { enum class Color { RED, GREEN, BLUE }; +enum AnimalFlags { HasClaws = 1 << 0, CanFly = 1 << 1, EatsFish = 1 << 2, Endangered = 1 << 3 }; + SomeStruct structvar; Long othervar; SomeStruct* ptrvar = &structvar; @@ -124,6 +126,11 @@ int main() { std::cout << nameof::nameof_enum(color) << std::endl; // 'RED' std::cout << NAMEOF_ENUM(color) << std::endl; // 'RED' std::cout << nameof::nameof_enum() << std::endl; // 'GREEN' + + // 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' #endif // Nameof. diff --git a/include/nameof.hpp b/include/nameof.hpp index 7b4e83b..0c0e71d 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_NAMEOF_HPP #define NEARGYE_NAMEOF_HPP +#define NAMEOF_VERSION_MAJOR 0 +#define NAMEOF_VERSION_MINOR 9 +#define NAMEOF_VERSION_PATCH 4 + #include #include #include @@ -39,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +60,7 @@ #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) # pragma warning(disable : 26495) // Variable 'cstring::chars_' is uninitialized. @@ -283,36 +289,6 @@ std::basic_ostream& operator<<(std::basic_ostream& o namespace detail { -template -struct identity { - using type = T; -}; - -template -struct nameof_type_supported -#if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template -struct nameof_enum_supported -#if defined(NAMEOF_ENUM_SUPPORTED) && NAMEOF_ENUM_SUPPORTED || defined(NAMEOF_ENUM_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template -using remove_cvref_t = std::remove_cv_t>; - -template -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>; - constexpr std::string_view pretty_name(std::string_view name, bool remove_template_suffix = true) noexcept { if (name.size() >= 1 && (name[0] == '"' || name[0] == '\'')) { return {}; // Narrow multibyte string literal. @@ -392,6 +368,23 @@ constexpr std::string_view pretty_name(std::string_view name, bool remove_templa return {}; // Invalid name. } +template +struct nameof_enum_supported +#if defined(NAMEOF_ENUM_SUPPORTED) && NAMEOF_ENUM_SUPPORTED || defined(NAMEOF_ENUM_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +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."); @@ -413,6 +406,7 @@ constexpr auto n() noexcept { template inline constexpr auto enum_name_v = n(); +#endif template constexpr bool is_valid() noexcept { @@ -448,7 +442,7 @@ constexpr std::size_t range_size() noexcept { } template -constexpr int reflected_min() noexcept { +constexpr auto 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."); @@ -458,7 +452,7 @@ constexpr int reflected_min() noexcept { } template -constexpr int reflected_max() noexcept { +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."); @@ -468,10 +462,10 @@ constexpr int reflected_max() noexcept { } template -inline constexpr int reflected_min_v = reflected_min(); +inline constexpr auto reflected_min_v = reflected_min(); template -inline constexpr int reflected_max_v = reflected_max(); +inline constexpr auto reflected_max_v = reflected_max(); template constexpr auto values(std::integer_sequence) noexcept { @@ -493,16 +487,16 @@ template inline constexpr auto values_v = values(std::make_integer_sequence, reflected_max_v>()>{}); template -inline constexpr std::size_t count_v = values_v.size(); +inline constexpr auto count_v = values_v.size(); template -inline constexpr int min_v = static_cast(values_v.front()); +inline constexpr auto min_v = static_cast(values_v.front()); template -inline constexpr int max_v = static_cast(values_v.back()); +inline constexpr auto max_v = static_cast(values_v.back()); template -inline constexpr std::size_t range_size_v = range_size, max_v>(); +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>; @@ -552,6 +546,77 @@ constexpr auto strings() noexcept { template inline static constexpr auto strings_v = strings(); +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) {}; + + return ret; +} + +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."); + + std::array values{}; + for (std::size_t i = 0, v = 0; v < count; ++i) { + if (valid[i]) { + values[v++] = flag_value(i); + } + } + + return values; +} + +template > +inline constexpr auto flags_values_v = flags_values(std::make_integer_sequence::digits>{}); + +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()...}}; +} + +template > +inline constexpr auto flags_strings_v = flags_strings>(std::make_integer_sequence, flags_max_v>()>{}); + +template +struct nameof_type_supported +#if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +struct identity { + using type = T; +}; + +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 @@ -571,16 +636,14 @@ constexpr auto n() noexcept { template inline constexpr auto type_name_v = n(); +#endif #if __has_include() inline std::string demangle(const char* tn) { - if (tn == nullptr) { - return {}; - } - auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); - auto r = std::string{dmg != nullptr ? dmg : tn}; + auto r = std::string{dmg}; std::free(dmg); + return r; } #else @@ -597,7 +660,7 @@ inline constexpr bool is_nameof_type_supported = detail::nameof_type_supported::value; -// Obtains simple (unqualified) string enum name of enum variable. +// Obtains simple (unqualified) string name of enum variable. template [[nodiscard]] constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; @@ -619,7 +682,32 @@ template return {}; // Value out of range. } -// Obtains simple (unqualified) string enum name of static storage enum variable. +// Obtains simple (unqualified) string name of enum flag variable. +template +[[nodiscard]] auto nameof_enum_flag(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."); + + 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) { + if (!name.empty()) { + name.append(1, '|'); + } + name.append(n); + } else { + return {}; // Value out of range. + } + } + } + + return name; +} + +// Obtains simple (unqualified) string name of static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. template [[nodiscard]] constexpr auto nameof_enum() noexcept -> detail::enable_if_enum_t { @@ -690,13 +778,16 @@ template constexpr auto __nameof_raw = ::nameof::cstring<__size>{__name}; \ return __nameof_raw; }() -// Obtains simple (unqualified) string enum name of enum variable. +// Obtains simple (unqualified) string name of enum variable. #define NAMEOF_ENUM(...) ::nameof::nameof_enum<::std::decay_t>(__VA_ARGS__) -// Obtains simple (unqualified) string enum name of static storage enum variable. +// Obtains simple (unqualified) string name of static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. #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__) + // 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 fd7cb28..0d7b12e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -79,6 +79,20 @@ enum Directions { Up = 85, Down = -42, Right = 120, Left = -120 }; enum number : unsigned long { one = 100, two = 200, three = 300, four = 400 }; +enum AnimalFlags { + HasClaws = 1, + CanFly = 2, + EatsFish = 4, + Endangered = 8, +}; + +enum class BigFlags : std::uint64_t { + A = 1, + B = (static_cast(0x1) << 20), + C = (static_cast(0x1) << 40), + D = (static_cast(0x1) << 63), +}; + namespace nameof { template <> struct enum_range { @@ -359,6 +373,58 @@ TEST_CASE("nameof_enum") { } } +TEST_CASE("nameof_enum_flag") { + constexpr AnimalFlags af = AnimalFlags::HasClaws; + auto af_name = nameof::nameof_enum_flag(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"); + + constexpr BigFlags bf = BigFlags::A; + auto bf_name = nameof::nameof_enum_flag(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"); +} + +TEST_CASE("NAMEOF_ENUM_FLAG") { + constexpr AnimalFlags af = AnimalFlags::HasClaws; + auto af_name = NAMEOF_ENUM_FLAG(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"); + + constexpr BigFlags bf = BigFlags::A; + auto bf_name = NAMEOF_ENUM_FLAG(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"); +} + #endif #if defined(NAMEOF_TYPE_SUPPORTED)