1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-09 23:34:23 +00:00
This commit is contained in:
neargye 2020-07-05 15:12:05 +05:00
parent 604054df5a
commit 8e343c47b8
3 changed files with 150 additions and 102 deletions

View file

@ -24,16 +24,16 @@
#include <magic_enum.hpp>
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<AnimalFlags>();
constexpr auto names = magic_enum::flags::enum_names<AnimalFlags>();
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<AnimalFlags>("EatsFish");
auto f2 = magic_enum::flags::enum_cast<AnimalFlags>("EatsFish|CanFly");
#else
auto f2 = magic_enum::flags::enum_cast<AnimalFlags>(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<AnimalFlags>(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<AnimalFlags>(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<std::underlying_type_t<AnimalFlags>>(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<AnimalFlags>() << std::endl; // AnimalFlags enum size: 4
std::cout << "AnimalFlags enum size: " << magic_enum::flags::enum_count<AnimalFlags>() << std::endl; // AnimalFlags enum size: 4
// Indexed access to enum value.
std::cout << "AnimalFlags[0] = " << magic_enum::flag::enum_value<AnimalFlags>(0) << std::endl; // AnimalFlags[0] = HasClaws
std::cout << "AnimalFlags[0] = " << magic_enum::flags::enum_value<AnimalFlags>(0) << std::endl; // AnimalFlags[0] = HasClaws
// Enum value sequence.
constexpr auto values = magic_enum::flag::enum_values<AnimalFlags>();
constexpr auto values = magic_enum::flags::enum_values<AnimalFlags>();
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
// Enum pair (value enum, string enum name) sequence.
constexpr auto entries = magic_enum::flag::enum_entries<AnimalFlags>();
constexpr auto entries = magic_enum::flags::enum_entries<AnimalFlags>();
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;
}

View file

@ -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<Color>() << std::endl; // Color enum size: 3
std::cout << "Color enum size: " << magic_enum::enum_count<Color>() << std::endl; // Color size: 3
// Indexed access to enum value.
std::cout << "Color[0] = " << magic_enum::enum_value<Color>(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 };

View file

@ -282,7 +282,7 @@ constexpr bool is_valid() noexcept {
}
template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>>
constexpr auto reflected_min() noexcept {
constexpr int reflected_min() noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::reflected_min requires enum type.");
if constexpr (IsFlags) {
@ -298,7 +298,7 @@ constexpr auto reflected_min() noexcept {
}
template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>>
constexpr auto reflected_max() noexcept {
constexpr int reflected_max() noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::reflected_max requires enum type.");
if constexpr (IsFlags) {
@ -450,7 +450,7 @@ constexpr bool is_sparse() noexcept {
}
template <typename E, bool IsFlags = false>
inline constexpr bool is_sparse_v = range_size_v<E, IsFlags> != count_v<E, IsFlags>;
inline constexpr bool is_sparse_v = is_sparse<E, IsFlags>();
template <typename E, typename U = std::underlying_type_t<E>>
constexpr int undex(U value) noexcept {
@ -476,6 +476,18 @@ constexpr int endex(E value) noexcept {
return undex<E>(static_cast<U>(value));
}
template <typename E, typename U = std::underlying_type_t<E>>
constexpr U value_ors() noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::endex requires enum type.");
auto value = U{0};
for (std::size_t i = 0; i < count_v<E, true>; ++i) {
value |= static_cast<U>(values_v<E, true>[i]);
}
return value;
}
template <bool, bool, typename T, typename R>
struct enable_if_enum {};
@ -492,13 +504,13 @@ struct enable_if_enum<true, true, T, R> {
using type = R;
using D = std::decay_t<T>;
static_assert(supported<D>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(count_v<D, true> > 0, "magic_enum::flag requires enum-flags implementation.");
static_assert(count_v<D, true> > 0, "magic_enum::flags requires enum-flags implementation.");
};
template <typename T, typename R = void>
using enable_if_enum_t = typename enable_if_enum<std::is_enum_v<std::decay_t<T>>, false, T, R>::type;
template <typename T, typename R>
template <typename T, typename R = void>
using enable_if_enum_flags_t = typename enable_if_enum<std::is_enum_v<std::decay_t<T>>, true, T, R>::type;
template <typename T, typename Enable = std::enable_if_t<std::is_enum_v<std::decay_t<T>>>>
@ -578,11 +590,12 @@ template <typename E>
template <typename E>
[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
constexpr auto count = detail::count_v<D>;
if constexpr (detail::is_sparse_v<D>) {
return assert(index < detail::count_v<D>), detail::values_v<D>[index];
return assert(index < count), detail::values_v<D>[index];
} else {
return assert(index < detail::count_v<D>), detail::value<D, detail::min_v<D>>(index);
return assert(index < count), detail::value<D, detail::min_v<D>>(index);
}
}
@ -681,7 +694,7 @@ template <typename E>
// Returns integer value from enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t<E, underlying_type_t<E>> {
[[nodiscard]] constexpr underlying_type_t<E> enum_integer(E value) noexcept {
return static_cast<underlying_type_t<E>>(value);
}
@ -724,21 +737,26 @@ template <typename E>
namespace ostream_operators {
template <typename Char, typename Traits, typename E>
auto operator<<(std::basic_ostream<Char, Traits>& os, E value) -> detail::enable_if_enum_t<E, std::basic_ostream<Char, Traits>&> {
if (const auto name = enum_name(value); !name.empty()) {
template <typename Char, typename Traits, typename E, typename = detail::enable_if_enum_t<E>>
auto& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
using namespace magic_enum;
using D = std::decay_t<E>;
if (const auto name = enum_name<D>(value); !name.empty()) {
for (const auto c : name) {
os.put(c);
}
} else {
os << enum_integer(value);
os << enum_integer<D>(value);
}
return os;
}
template <typename Char, typename Traits, typename E>
auto operator<<(std::basic_ostream<Char, Traits>& os, std::optional<E> value) -> detail::enable_if_enum_t<E, std::basic_ostream<Char, Traits>&> {
template <typename Char, typename Traits, typename E, typename = detail::enable_if_enum_t<E>>
auto& operator<<(std::basic_ostream<Char, Traits>& os, std::optional<E> value) {
using namespace magic_enum;
if (value.has_value()) {
os << value.value();
}
@ -750,44 +768,44 @@ auto operator<<(std::basic_ostream<Char, Traits>& os, std::optional<E> value) ->
namespace bitwise_operators {
template <typename E>
constexpr auto operator~(E rhs) noexcept -> detail::enable_if_enum_t<E, E> {
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
constexpr E operator~(E rhs) noexcept {
return static_cast<E>(~static_cast<underlying_type_t<E>>(rhs));
}
template <typename E>
constexpr auto operator|(E lhs, E rhs) noexcept -> detail::enable_if_enum_t<E, E> {
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
constexpr E operator|(E lhs, E rhs) noexcept {
return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) | static_cast<underlying_type_t<E>>(rhs));
}
template <typename E>
constexpr auto operator&(E lhs, E rhs) noexcept -> detail::enable_if_enum_t<E, E> {
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
constexpr E operator&(E lhs, E rhs) noexcept {
return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) & static_cast<underlying_type_t<E>>(rhs));
}
template <typename E>
constexpr auto operator^(E lhs, E rhs) noexcept -> detail::enable_if_enum_t<E, E> {
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
constexpr E operator^(E lhs, E rhs) noexcept {
return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) ^ static_cast<underlying_type_t<E>>(rhs));
}
template <typename E>
constexpr auto operator|=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t<E, E&> {
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
constexpr E& operator|=(E& lhs, E rhs) noexcept {
return lhs = lhs | rhs;
}
template <typename E>
constexpr auto operator&=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t<E, E&> {
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
constexpr E& operator&=(E& lhs, E rhs) noexcept {
return lhs = lhs & rhs;
}
template <typename E>
constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t<E, E&> {
template <typename E, std::enable_if_t<std::is_enum_v<E>, 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 <typename E>
@ -802,13 +820,14 @@ template <typename E>
template <typename E>
[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_flags_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
constexpr auto count = detail::count_v<D, true>;
if constexpr (detail::is_sparse_v<D>) {
return assert((index < detail::count_v<D, true>)), detail::values_v<D, true>[index];
if constexpr (detail::is_sparse_v<D, true>) {
return assert(index < count), detail::values_v<D, true>[index];
} else {
constexpr auto min = detail::log2(detail::min_v<D, true>) - 1;
return assert((index < detail::count_v<D, true>)), detail::value<D, min, true>(index);
return assert(index < count), detail::value<D, min, true>(index);
}
}
@ -857,23 +876,53 @@ template <typename E>
return detail::entries_v<D, true>;
}
// TODO: enum_cast
// Checks whether enum-flags contains enumerator with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_flags_t<E, bool> {
using D = std::decay_t<E>;
using U = std::underlying_type_t<D>;
if constexpr (detail::is_sparse_v<D, true>) {
constexpr auto min = detail::min_v<D, true>;
constexpr auto max = detail::value_ors<D>();
return value >= min && value <= max;
} else {
auto check_value = U{0};
for (std::size_t i = 0; i < detail::count_v<D, true>; ++i) {
if (const auto v = static_cast<U>(enum_value<D>(i)); (static_cast<U>(value) & v) != 0) {
check_value |= v;
}
}
return check_value == value;
}
}
// Checks whether enum-flags contains enumerator with such enum-flags value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t<E, bool> {
using D = std::decay_t<E>;
using U = std::underlying_type_t<D>;
return enum_contains<D>(static_cast<U>(value));
}
// Checks whether enum-flags contains enumerator with such string name.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t<E, bool> {
// 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 <typename E>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_flags_t<E, std::optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
using U = std::underlying_type_t<D>;
auto check_value = U{0};
for (std::size_t i = 0; i < detail::count_v<D, true>; ++i) {
if (const auto v = enum_value<D>(i); (static_cast<U>(value) & static_cast<U>(v)) != 0) {
check_value |= static_cast<U>(v);
}
}
if (check_value == value) {
if (enum_contains<D>(value)) {
return static_cast<D>(value);
}
@ -884,7 +933,8 @@ template <typename E>
// Returns std::optional with enum-flags value.
template <typename E, typename BinaryPredicate>
[[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_flags_t<E, std::optional<std::decay_t<E>>> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::flag::enum_cast requires bool(char, char) invocable predicate.");
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "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 <typename E, typename BinaryPredicate>
// Returns std::optional with enum-flags value.
template <typename E>
[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t<E, std::optional<std::decay_t<E>>> {
// TODO: impl
static_assert(sizeof(E) == 0, "not implemented");
return {};
}
// Returns integer value from enum-flags value.
template <typename E>
[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_flags_t<E, underlying_type_t<E>> {
return static_cast<underlying_type_t<E>>(value);
}
// Obtains index in enum-flags values from enum-flags value.
// Returns std::optional with index.
template <typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::size_t>> {
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_flags_t<E, std::optional<std::size_t>> {
using D = std::decay_t<E>;
if (detail::is_pow2<D>(value)) {
@ -920,34 +965,37 @@ template <typename E>
return std::nullopt; // Value out of range.
}
// Checks whether enum-flags contains enumerator with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_flags_t<E, bool> {
using D = std::decay_t<E>;
return enum_cast<D>(value).has_value();
}
// Checks whether enum-flags contains enumerator with such enum-flags value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t<E, bool> {
using D = std::decay_t<E>;
using U = std::underlying_type_t<D>;
return enum_cast<D>(static_cast<U>(value)).has_value();
}
// Checks whether enum-flags contains enumerator with such string name.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t<E, bool> {
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 <typename Char, typename Traits, typename E, typename = detail::enable_if_enum_flags_t<E>>
auto& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
using namespace magic_enum::flags;
using D = std::decay_t<E>;
if (const auto name = enum_name<D>(value); !name.empty()) {
for (const auto c : name) {
os.put(c);
}
} else {
os << enum_integer<D>(value);
}
return os;
}
template <typename Char, typename Traits, typename E, typename = detail::enable_if_enum_flags_t<E>>
auto& operator<<(std::basic_ostream<Char, Traits>& os, std::optional<E> value) {
using namespace magic_enum::flags;
if (value.has_value()) {
os << value.value();
}
return os;
}
} // namespace magic_enum::flags::ostream_operators