diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp index 61eefdf..1cedd93 100644 --- a/example/enum_flag_example.cpp +++ b/example/enum_flag_example.cpp @@ -24,16 +24,16 @@ #include -enum class AnimalFlags : std::uint64_t { HasClaws = 1 << 1, CanFly = 1 << 2, EatsFish = 1 << 20, Endangered = std::uint64_t{1} << 40 }; +enum class AnimalFlags : std::uint64_t { HasClaws = 1 << 10, CanFly = 1 << 20, EatsFish = 1 << 30, Endangered = std::uint64_t{1} << 40 }; int main() { // Enum variable to string name. - AnimalFlags f1 = AnimalFlags::CanFly; - auto f1_name = magic_enum::flag::enum_name(f1); - std::cout << f1_name << std::endl; // CanFly + AnimalFlags f1 = AnimalFlags::Endangered; + auto f1_name = magic_enum::flags::enum_name(f1); + std::cout << f1_name << std::endl; // Endangered // String enum name sequence. - constexpr auto names = magic_enum::flag::enum_names(); + constexpr auto names = magic_enum::flags::enum_names(); std::cout << "AnimalFlags names:"; for (auto n : names) { std::cout << " " << n; @@ -43,38 +43,38 @@ int main() { #if 0 // String name to enum value. - auto f2 = magic_enum::flag::enum_cast("EatsFish"); + auto f2 = magic_enum::flags::enum_cast("EatsFish|CanFly"); +#else +auto f2 = magic_enum::flags::enum_cast(1074790400); +#endif if (f2.has_value() && f2.value() == AnimalFlags::EatsFish) { - std::cout << "EatsFish = " << magic_enum::flag::enum_integer(f2.value()) << std::endl; // EatsFish = 4 + std::cout << "EatsFish = " << magic_enum::flags::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400 } // Integer value to enum value. - auto f3 = magic_enum::flag::enum_cast(8); - if (f3.has_value() && f3.value() == AnimalFlags::Endangered) { - std::cout << "Endangered = " << magic_enum::flag::enum_integer(f3.value()) << std::endl; // Endangered = 8 + auto f3 = magic_enum::flags::enum_cast(1073742848); + if (f3.has_value()) { + std::cout << magic_enum::flags::enum_name(f3.value()) << " = " << magic_enum::flags::enum_integer(f3.value()) << std::endl; // HasClaws|EatsFish = 1073742848 } -#endif // Enum value to integer value. - auto f4_integer = magic_enum::flag::enum_integer(AnimalFlags::HasClaws); + auto f4_integer = magic_enum::flags::enum_integer(AnimalFlags::HasClaws); if (f4_integer == static_cast>(AnimalFlags::HasClaws)) { - std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 2 + std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 1024 } - using namespace magic_enum::flag::ostream_operators; // out-of-the-box ostream operator for all enums. -#if 0 + using namespace magic_enum::flags::ostream_operators; // out-of-the-box ostream operator for all enums. // ostream operator for enum. - std::cout << f1 << " " << f2 << " " << f3 << std::endl; // CanFly EatsFish Endangered -#endif + std::cout << f1 << " " << f2 << " " << f3 << std::endl; // Endangered CanFly|EatsFish HasClaws|EatsFish // Number of enum values. - std::cout << "AnimalFlags enum size: " << magic_enum::flag::enum_count() << std::endl; // AnimalFlags enum size: 4 + std::cout << "AnimalFlags enum size: " << magic_enum::flags::enum_count() << std::endl; // AnimalFlags enum size: 4 // Indexed access to enum value. - std::cout << "AnimalFlags[0] = " << magic_enum::flag::enum_value(0) << std::endl; // AnimalFlags[0] = HasClaws + std::cout << "AnimalFlags[0] = " << magic_enum::flags::enum_value(0) << std::endl; // AnimalFlags[0] = HasClaws // Enum value sequence. - constexpr auto values = magic_enum::flag::enum_values(); + constexpr auto values = magic_enum::flags::enum_values(); std::cout << "AnimalFlags values:"; for (AnimalFlags f : values) { std::cout << " " << f; // ostream operator for enum. @@ -82,19 +82,19 @@ int main() { std::cout << std::endl; // AnimalFlags sequence: HasClaws CanFly EatsFish Endangered - using namespace magic_enum::flag::bitwise_operators; // out-of-the-box bitwise operators for all enums. + using namespace magic_enum::flags::bitwise_operators; // out-of-the-box bitwise operators for all enums. // Support operators: ~, |, &, ^, |=, &=, ^=. AnimalFlags flag = AnimalFlags::HasClaws | AnimalFlags::CanFly; - std::cout << flag << std::endl; // HasClaws | CanFly + std::cout << flag << std::endl; // HasClaws|CanFly // Enum pair (value enum, string enum name) sequence. - constexpr auto entries = magic_enum::flag::enum_entries(); + constexpr auto entries = magic_enum::flags::enum_entries(); std::cout << "AnimalFlags entries:"; for (auto e : entries) { - std::cout << " " << e.second << " = " << magic_enum::flag::enum_integer(e.first); + std::cout << " " << e.second << " = " << magic_enum::flags::enum_integer(e.first); } std::cout << std::endl; - // AnimalFlags entries: AnimalFlags entries: HasClaws = 2 CanFly = 4 EatsFish = 1048576 Endangered = 1099511627776 + // AnimalFlags entries: AnimalFlags entries: HasClaws = 1024 CanFly = 1048576 EatsFish = 1073741824 Endangered = 1099511627776 return 0; } diff --git a/example/example.cpp b/example/example.cpp index d92e87b..c321ca1 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -70,7 +70,7 @@ int main() { 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 + std::cout << "Color enum size: " << magic_enum::enum_count() << std::endl; // Color size: 3 // Indexed access to enum value. std::cout << "Color[0] = " << magic_enum::enum_value(0) << std::endl; // Color[0] = RED @@ -88,7 +88,7 @@ int main() { using namespace magic_enum::bitwise_operators; // out-of-the-box bitwise operators for all enums. // Support operators: ~, |, &, ^, |=, &=, ^=. Flags flag = Flags::A | Flags::C; - std::cout << flag << std::endl; + std::cout << flag << std::endl; // 5 enum color { red, green, blue }; diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 0c7da1e..0ba8ec4 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -282,7 +282,7 @@ constexpr bool is_valid() noexcept { } template > -constexpr auto reflected_min() noexcept { +constexpr int reflected_min() noexcept { static_assert(is_enum_v, "magic_enum::detail::reflected_min requires enum type."); if constexpr (IsFlags) { @@ -298,7 +298,7 @@ constexpr auto reflected_min() noexcept { } template > -constexpr auto reflected_max() noexcept { +constexpr int reflected_max() noexcept { static_assert(is_enum_v, "magic_enum::detail::reflected_max requires enum type."); if constexpr (IsFlags) { @@ -450,7 +450,7 @@ constexpr bool is_sparse() noexcept { } template -inline constexpr bool is_sparse_v = range_size_v != count_v; +inline constexpr bool is_sparse_v = is_sparse(); template > constexpr int undex(U value) noexcept { @@ -476,6 +476,18 @@ constexpr int endex(E value) noexcept { return undex(static_cast(value)); } +template > +constexpr U value_ors() noexcept { + static_assert(is_enum_v, "magic_enum::detail::endex requires enum type."); + + auto value = U{0}; + for (std::size_t i = 0; i < count_v; ++i) { + value |= static_cast(values_v[i]); + } + + return value; +} + template struct enable_if_enum {}; @@ -492,13 +504,13 @@ struct enable_if_enum { using type = R; using D = std::decay_t; static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(count_v > 0, "magic_enum::flag requires enum-flags implementation."); + static_assert(count_v > 0, "magic_enum::flags requires enum-flags implementation."); }; template using enable_if_enum_t = typename enable_if_enum>, false, T, R>::type; -template +template using enable_if_enum_flags_t = typename enable_if_enum>, true, T, R>::type; template >>> @@ -578,11 +590,12 @@ template template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; + constexpr auto count = detail::count_v; if constexpr (detail::is_sparse_v) { - return assert(index < detail::count_v), detail::values_v[index]; + return assert(index < count), detail::values_v[index]; } else { - return assert(index < detail::count_v), detail::value>(index); + return assert(index < count), detail::value>(index); } } @@ -681,7 +694,7 @@ template // Returns integer value from enum value. template -[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr underlying_type_t enum_integer(E value) noexcept { return static_cast>(value); } @@ -724,21 +737,26 @@ template namespace ostream_operators { -template -auto operator<<(std::basic_ostream& os, E value) -> detail::enable_if_enum_t&> { - if (const auto name = enum_name(value); !name.empty()) { +template > +auto& operator<<(std::basic_ostream& os, E value) { + using namespace magic_enum; + using D = std::decay_t; + + if (const auto name = enum_name(value); !name.empty()) { for (const auto c : name) { os.put(c); } } else { - os << enum_integer(value); + os << enum_integer(value); } return os; } -template -auto operator<<(std::basic_ostream& os, std::optional value) -> detail::enable_if_enum_t&> { +template > +auto& operator<<(std::basic_ostream& os, std::optional value) { + using namespace magic_enum; + if (value.has_value()) { os << value.value(); } @@ -750,44 +768,44 @@ auto operator<<(std::basic_ostream& os, std::optional value) -> namespace bitwise_operators { -template -constexpr auto operator~(E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator~(E rhs) noexcept { return static_cast(~static_cast>(rhs)); } -template -constexpr auto operator|(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator|(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) | static_cast>(rhs)); } -template -constexpr auto operator&(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator&(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) & static_cast>(rhs)); } -template -constexpr auto operator^(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator^(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } -template -constexpr auto operator|=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E& operator|=(E& lhs, E rhs) noexcept { return lhs = lhs | rhs; } -template -constexpr auto operator&=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E& operator&=(E& lhs, E rhs) noexcept { return lhs = lhs & rhs; } -template -constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E& operator^=(E& lhs, E rhs) noexcept { return lhs = lhs ^ rhs; } } // namespace magic_enum::bitwise_operators -namespace flag { +namespace flags { // Returns number of enum-flags values. template @@ -802,13 +820,14 @@ template template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; + constexpr auto count = detail::count_v; - if constexpr (detail::is_sparse_v) { - return assert((index < detail::count_v)), detail::values_v[index]; + if constexpr (detail::is_sparse_v) { + return assert(index < count), detail::values_v[index]; } else { constexpr auto min = detail::log2(detail::min_v) - 1; - return assert((index < detail::count_v)), detail::value(index); + return assert(index < count), detail::value(index); } } @@ -857,23 +876,53 @@ template return detail::entries_v; } -// TODO: enum_cast +// Checks whether enum-flags contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + using U = std::underlying_type_t; + + if constexpr (detail::is_sparse_v) { + constexpr auto min = detail::min_v; + constexpr auto max = detail::value_ors(); + + return value >= min && value <= max; + } else { + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { + check_value |= v; + } + } + + return check_value == value; + } +} + +// Checks whether enum-flags contains enumerator with such enum-flags value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + using U = std::underlying_type_t; + + return enum_contains(static_cast(value)); +} + +// Checks whether enum-flags contains enumerator with such string name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { + // TODO: impl + static_assert(sizeof(E) == 0, "not implemented"); + return {}; +} // Obtains enum-flags value from integer value. // Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t>> { using D = std::decay_t; - using U = std::underlying_type_t; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { - check_value |= static_cast(v); - } - } - - if (check_value == value) { + if (enum_contains(value)) { return static_cast(value); } @@ -884,7 +933,8 @@ template // Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t>> { - static_assert(std::is_invocable_r_v, "magic_enum::flag::enum_cast requires bool(char, char) invocable predicate."); + static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_cast requires bool(char, char) invocable predicate."); + // TODO: impl static_assert(sizeof(E) == 0, "not implemented"); return {}; } @@ -893,20 +943,15 @@ template // Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { + // TODO: impl static_assert(sizeof(E) == 0, "not implemented"); return {}; } -// Returns integer value from enum-flags value. -template -[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_flags_t> { - return static_cast>(value); -} - // Obtains index in enum-flags values from enum-flags value. // Returns std::optional with index. template -[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; if (detail::is_pow2(value)) { @@ -920,34 +965,37 @@ template return std::nullopt; // Value out of range. } -// Checks whether enum-flags contains enumerator with such integer value. -template -[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { - using D = std::decay_t; - - return enum_cast(value).has_value(); -} - -// Checks whether enum-flags contains enumerator with such enum-flags value. -template -[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { - using D = std::decay_t; - using U = std::underlying_type_t; - - return enum_cast(static_cast(value)).has_value(); -} - -// Checks whether enum-flags contains enumerator with such string name. -template -[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { - static_assert(sizeof(E) == 0, "not implemented"); - return {}; -} +using magic_enum::enum_type_name; // TODO: impl +using magic_enum::enum_integer; // TODO: impl namespace ostream_operators { -// TODO: operator<< -using namespace magic_enum::ostream_operators; +template > +auto& operator<<(std::basic_ostream& os, E value) { + using namespace magic_enum::flags; + using D = std::decay_t; + + if (const auto name = enum_name(value); !name.empty()) { + for (const auto c : name) { + os.put(c); + } + } else { + os << enum_integer(value); + } + + return os; +} + +template > +auto& operator<<(std::basic_ostream& os, std::optional value) { + using namespace magic_enum::flags; + + if (value.has_value()) { + os << value.value(); + } + + return os; +} } // namespace magic_enum::flags::ostream_operators