mirror of
https://github.com/Neargye/magic_enum.git
synced 2026-01-09 23:34:23 +00:00
New enum-flags API (#120)
This commit is contained in:
parent
6a1db3b8b6
commit
aecf0db795
7 changed files with 336 additions and 477 deletions
|
|
@ -197,8 +197,6 @@ enum class Color { RED = 2, BLUE = 4, GREEN = 8 };
|
|||
|
||||
* Before use, read the [limitations](doc/limitations.md) of functionality.
|
||||
|
||||
* For the small enum use the API from the namespace `magic_enum`, and for enum-flags use the API from the namespace `magic_enum::flags`.
|
||||
|
||||
## Integration
|
||||
|
||||
* You should add the required file [magic_enum.hpp](include/magic_enum.hpp).
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@
|
|||
|
||||
* Enum can't reflect if the enum is a forward declaration.
|
||||
|
||||
* For enum-flags add specialization `is_flags_enum` for necessary enum type. Specialization of `is_flags_enum` must be injected in `namespace magic_enum::customize`.
|
||||
```cpp
|
||||
enum class Directions { Up = 1 << 1, Down = 1 << 2, Right = 1 << 3, Left = 1 << 4 };
|
||||
template <>
|
||||
struct magic_enum::customize::is_flags_enum<Directions> : std::true_type {};
|
||||
```
|
||||
|
||||
* Enum value must be in range `[MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]`.
|
||||
|
||||
* By default `MAGIC_ENUM_RANGE_MIN = -128`, `MAGIC_ENUM_RANGE_MAX = 128`.
|
||||
|
|
@ -28,14 +35,12 @@
|
|||
|
||||
enum class number { one = 100, two = 200, three = 300 };
|
||||
|
||||
namespace magic_enum::customize {
|
||||
template <>
|
||||
struct enum_range<number> {
|
||||
struct magic_enum::customize::enum_range<number> {
|
||||
static constexpr int min = 100;
|
||||
static constexpr int max = 300;
|
||||
// (max - min) must be less than UINT16_MIN.
|
||||
};
|
||||
} // namespace magic_enum
|
||||
```
|
||||
|
||||
* `magic_enum` [won't work if a value is aliased](https://github.com/Neargye/magic_enum/issues/68). Work with enum-aliases is compiler-implementation-defined.
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@
|
|||
* To check is magic_enum supported compiler use macro `MAGIC_ENUM_SUPPORTED` or constexpr constant `magic_enum::is_magic_enum_supported`.</br>
|
||||
If magic_enum used on unsupported compiler, occurs the compilation error. To suppress error define macro `MAGIC_ENUM_NO_CHECK_SUPPORT`.
|
||||
|
||||
* For the small enum use the API from the namespace `magic_enum`, and for enum-flags use the API from the namespace `magic_enum::flags`.
|
||||
|
||||
* To add custom enum or type names see the [example](../example/example_custom_name.cpp).
|
||||
|
||||
* To change the type of strings or ortional, use special macros:
|
||||
|
|
|
|||
|
|
@ -26,15 +26,18 @@
|
|||
#include <magic_enum.hpp>
|
||||
|
||||
enum class AnimalFlags : std::uint64_t { HasClaws = 1 << 10, CanFly = 1 << 20, EatsFish = 1 << 30, Endangered = std::uint64_t{1} << 40 };
|
||||
// Add specialization `is_flags_enum` to force define that enum are flags.
|
||||
// template <>
|
||||
// struct magic_enum::customize::is_flags_enum<AnimalFlags> : std::true_type {};
|
||||
|
||||
int main() {
|
||||
// Enum-flags variable to string name.
|
||||
AnimalFlags f1 = AnimalFlags::Endangered;
|
||||
auto f1_name = magic_enum::flags::enum_name(f1);
|
||||
auto f1_name = magic_enum::enum_name(f1);
|
||||
std::cout << f1_name << std::endl; // Endangered
|
||||
|
||||
// String enum-flags name sequence.
|
||||
constexpr auto names = magic_enum::flags::enum_names<AnimalFlags>();
|
||||
constexpr auto names = magic_enum::enum_names<AnimalFlags>();
|
||||
std::cout << "AnimalFlags names:";
|
||||
for (const auto& n : names) {
|
||||
std::cout << " " << n;
|
||||
|
|
@ -43,33 +46,33 @@ int main() {
|
|||
// AnimalFlags names: HasClaws CanFly EatsFish Endangered
|
||||
|
||||
// String name to enum-flags value.
|
||||
auto f2 = magic_enum::flags::enum_cast<AnimalFlags>("EatsFish|CanFly");
|
||||
auto f2 = magic_enum::enum_cast<AnimalFlags>("EatsFish|CanFly");
|
||||
if (f2.has_value()) {
|
||||
std::cout << "EatsFish = " << magic_enum::flags::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400
|
||||
std::cout << "EatsFish|CanFly = " << magic_enum::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400
|
||||
}
|
||||
|
||||
// Integer value to enum-flags value.
|
||||
auto f3 = magic_enum::flags::enum_cast<AnimalFlags>(1073742848);
|
||||
auto f3 = magic_enum::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
|
||||
std::cout << magic_enum::enum_flags_name(f3.value()) << " = " << magic_enum::enum_integer(f3.value()) << std::endl; // HasClaws|EatsFish = 1073742848
|
||||
}
|
||||
|
||||
// Enum-flags value to integer value.
|
||||
auto f4_integer = magic_enum::flags::enum_integer(AnimalFlags::HasClaws);
|
||||
auto f4_integer = magic_enum::enum_integer(AnimalFlags::HasClaws);
|
||||
std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 1024
|
||||
|
||||
using namespace magic_enum::flags::ostream_operators; // out-of-the-box ostream operator for enum-flags.
|
||||
using namespace magic_enum::ostream_operators; // out-of-the-box ostream operator for enum-flags.
|
||||
// Ostream operator for enum-flags.
|
||||
std::cout << f1 << " " << f2 << " " << f3 << std::endl; // Endangered CanFly|EatsFish HasClaws|EatsFish
|
||||
|
||||
// Number of enum-flags values.
|
||||
std::cout << "AnimalFlags enum size: " << magic_enum::flags::enum_count<AnimalFlags>() << std::endl; // AnimalFlags enum size: 4
|
||||
std::cout << "AnimalFlags enum size: " << magic_enum::enum_count<AnimalFlags>() << std::endl; // AnimalFlags enum size: 4
|
||||
|
||||
// Indexed access to enum-flags value.
|
||||
std::cout << "AnimalFlags[0] = " << magic_enum::flags::enum_value<AnimalFlags>(0) << std::endl; // AnimalFlags[0] = HasClaws
|
||||
std::cout << "AnimalFlags[0] = " << magic_enum::enum_value<AnimalFlags>(0) << std::endl; // AnimalFlags[0] = HasClaws
|
||||
|
||||
// Enum-flags value sequence.
|
||||
constexpr auto values = magic_enum::flags::enum_values<AnimalFlags>();
|
||||
constexpr auto values = magic_enum::enum_values<AnimalFlags>();
|
||||
std::cout << "AnimalFlags values:";
|
||||
for (const auto f : values) {
|
||||
std::cout << " " << f; // Ostream operator for enum-flags.
|
||||
|
|
@ -77,16 +80,16 @@ int main() {
|
|||
std::cout << std::endl;
|
||||
// AnimalFlags sequence: HasClaws CanFly EatsFish Endangered
|
||||
|
||||
using namespace magic_enum::flags::bitwise_operators; // out-of-the-box bitwise operators for all enums.
|
||||
using namespace magic_enum::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-flags pair (value, string name) sequence.
|
||||
constexpr auto entries = magic_enum::flags::enum_entries<AnimalFlags>();
|
||||
constexpr auto entries = magic_enum::enum_entries<AnimalFlags>();
|
||||
std::cout << "AnimalFlags entries:";
|
||||
for (const auto& e : entries) {
|
||||
std::cout << " " << e.second << " = " << magic_enum::flags::enum_integer(e.first);
|
||||
std::cout << " " << e.second << " = " << magic_enum::enum_integer(e.first);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
// AnimalFlags entries: AnimalFlags entries: HasClaws = 1024 CanFly = 1048576 EatsFish = 1073741824 Endangered = 1099511627776
|
||||
|
|
|
|||
|
|
@ -142,6 +142,9 @@ constexpr string_view enum_name(E) noexcept {
|
|||
return {};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
struct is_flags_enum : std::false_type {};
|
||||
|
||||
} // namespace magic_enum::customize
|
||||
|
||||
namespace detail {
|
||||
|
|
@ -283,9 +286,9 @@ constexpr bool cmp_less(L lhs, R rhs) noexcept {
|
|||
if constexpr (std::is_signed_v<L> == std::is_signed_v<R>) {
|
||||
// If same signedness (both signed or both unsigned).
|
||||
return lhs < rhs;
|
||||
} else if constexpr (std::is_same_v<L, bool>) { // bool special case due to msvc's C4804, C4018
|
||||
} else if constexpr (std::is_same_v<L, bool>) { // bool special case
|
||||
return static_cast<R>(lhs) < rhs;
|
||||
} else if constexpr (std::is_same_v<R, bool>) { // bool special case due to msvc's C4804, C4018
|
||||
} else if constexpr (std::is_same_v<R, bool>) { // bool special case
|
||||
return lhs < static_cast<L>(rhs);
|
||||
} else if constexpr (std::is_signed_v<R>) {
|
||||
// If 'right' is negative, then result is 'false', otherwise cast & compare.
|
||||
|
|
@ -306,13 +309,6 @@ constexpr I log2(I value) noexcept {
|
|||
return ret;
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
constexpr bool is_pow2(I x) noexcept {
|
||||
static_assert(std::is_integral_v<I>, "magic_enum::detail::is_pow2 requires integral type.");
|
||||
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_enum_v = std::is_enum_v<T> && std::is_same_v<T, std::decay_t<T>>;
|
||||
|
||||
|
|
@ -368,11 +364,15 @@ constexpr bool is_valid() noexcept {
|
|||
return n<E, static_cast<E>(V)>().size() != 0;
|
||||
}
|
||||
|
||||
template <typename E, int O, bool IsFlags = false, typename U = std::underlying_type_t<E>>
|
||||
template <typename E, int O, bool IsFlags, typename U = std::underlying_type_t<E>>
|
||||
constexpr E value(std::size_t i) noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::value requires enum type.");
|
||||
|
||||
if constexpr (IsFlags) {
|
||||
if constexpr (std::is_same_v<U, bool>) { // bool special case
|
||||
static_assert(O == 0, "magic_enum::detail::value requires valid offset.");
|
||||
|
||||
return static_cast<E>(i);
|
||||
} else if constexpr (IsFlags) {
|
||||
return static_cast<E>(U{1} << static_cast<U>(static_cast<int>(i) + O));
|
||||
} else {
|
||||
return static_cast<E>(static_cast<int>(i) + O);
|
||||
|
|
@ -417,10 +417,10 @@ constexpr int reflected_max() noexcept {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
template <typename E, bool IsFlags>
|
||||
inline constexpr auto reflected_min_v = reflected_min<E, IsFlags>();
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
template <typename E, bool IsFlags>
|
||||
inline constexpr auto reflected_max_v = reflected_max<E, IsFlags>();
|
||||
|
||||
template <std::size_t N>
|
||||
|
|
@ -467,124 +467,101 @@ constexpr auto values() noexcept {
|
|||
return values<E, IsFlags, reflected_min_v<E, IsFlags>>(std::make_index_sequence<range_size>{});
|
||||
}
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr auto values_v = values<E, IsFlags>();
|
||||
template <typename E, typename U = std::underlying_type_t<E>>
|
||||
constexpr bool is_flags_enum() noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::is_flags_enum requires enum type.");
|
||||
|
||||
template <typename E, bool IsFlags = false, typename D = std::decay_t<E>>
|
||||
using values_t = decltype((values_v<D, IsFlags>));
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr auto count_v = values_v<E, IsFlags>.size();
|
||||
|
||||
template <typename E, bool IsFlags = false, typename U = std::underlying_type_t<E>>
|
||||
inline constexpr auto min_v = (count_v<E, IsFlags> > 0) ? static_cast<U>(values_v<E, IsFlags>.front()) : U{0};
|
||||
|
||||
template <typename E, bool IsFlags = false, typename U = std::underlying_type_t<E>>
|
||||
inline constexpr auto max_v = (count_v<E, IsFlags> > 0) ? static_cast<U>(values_v<E, IsFlags>.back()) : U{0};
|
||||
|
||||
template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>>
|
||||
constexpr std::size_t range_size() noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::range_size requires enum type.");
|
||||
constexpr auto max = IsFlags ? log2(max_v<E, IsFlags>) : max_v<E, IsFlags>;
|
||||
constexpr auto min = IsFlags ? log2(min_v<E, IsFlags>) : min_v<E, IsFlags>;
|
||||
constexpr auto range_size = max - min + 1;
|
||||
static_assert(range_size > 0, "magic_enum::enum_range requires valid size.");
|
||||
static_assert(range_size < (std::numeric_limits<std::uint16_t>::max)(), "magic_enum::enum_range requires valid size.");
|
||||
|
||||
return static_cast<std::size_t>(range_size);
|
||||
#if defined(MAGIC_ENUM_NO_CHECK_FLAGS)
|
||||
return customize::is_flags_enum<E>::value;
|
||||
#else
|
||||
if constexpr (std::is_same_v<U, bool>) { // bool special case
|
||||
return false;
|
||||
} else if constexpr (customize::is_flags_enum<E>::value) {
|
||||
return true;
|
||||
} else {
|
||||
constexpr auto flags_values = values<E, true>();
|
||||
constexpr auto default_values = values<E, false>();
|
||||
if (flags_values.size() == 0 || default_values.size() > flags_values.size()) {
|
||||
return false;
|
||||
}
|
||||
for (std::size_t i = 0; i < default_values.size(); ++i) {
|
||||
const auto v = static_cast<U>(default_values[i]);
|
||||
if (v != 0 && (v & (v - 1)) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return flags_values.size() > 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr auto range_size_v = range_size<E, IsFlags>();
|
||||
template <typename E>
|
||||
inline constexpr bool is_flags_v = is_flags_enum<E>();
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
using index_t = std::conditional_t<range_size_v<E, IsFlags> < (std::numeric_limits<std::uint8_t>::max)(), std::uint8_t, std::uint16_t>;
|
||||
template <typename E>
|
||||
inline constexpr auto values_v = values<E, is_flags_v<E>>();
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr auto invalid_index_v = (std::numeric_limits<index_t<E, IsFlags>>::max)();
|
||||
template <typename E, typename D = std::decay_t<E>>
|
||||
using values_t = decltype((values_v<D>));
|
||||
|
||||
template <typename E, bool IsFlags, std::size_t... I>
|
||||
constexpr auto indexes(std::index_sequence<I...>) noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::indexes requires enum type.");
|
||||
constexpr auto min = IsFlags ? log2(min_v<E, IsFlags>) : min_v<E, IsFlags>;
|
||||
[[maybe_unused]] auto i = index_t<E, IsFlags>{0};
|
||||
template <typename E>
|
||||
inline constexpr auto count_v = values_v<E>.size();
|
||||
|
||||
return std::array<decltype(i), sizeof...(I)>{{(is_valid<E, value<E, min, IsFlags>(I)>() ? i++ : invalid_index_v<E, IsFlags>)...}};
|
||||
}
|
||||
template <typename E, typename U = std::underlying_type_t<E>>
|
||||
inline constexpr auto min_v = (count_v<E> > 0) ? static_cast<U>(values_v<E>.front()) : U{0};
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr auto indexes_v = indexes<E, IsFlags>(std::make_index_sequence<range_size_v<E, IsFlags>>{});
|
||||
template <typename E, typename U = std::underlying_type_t<E>>
|
||||
inline constexpr auto max_v = (count_v<E> > 0) ? static_cast<U>(values_v<E>.back()) : U{0};
|
||||
|
||||
template <typename E, bool IsFlags, std::size_t... I>
|
||||
template <typename E, std::size_t... I>
|
||||
constexpr auto names(std::index_sequence<I...>) noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::names requires enum type.");
|
||||
|
||||
return std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E, IsFlags>[I]>...}};
|
||||
return std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E>[I]>...}};
|
||||
}
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr auto names_v = names<E, IsFlags>(std::make_index_sequence<count_v<E, IsFlags>>{});
|
||||
template <typename E>
|
||||
inline constexpr auto names_v = names<E>(std::make_index_sequence<count_v<E>>{});
|
||||
|
||||
template <typename E, bool IsFlags = false, typename D = std::decay_t<E>>
|
||||
using names_t = decltype((names_v<D, IsFlags>));
|
||||
template <typename E, typename D = std::decay_t<E>>
|
||||
using names_t = decltype((names_v<D>));
|
||||
|
||||
template <typename E, bool IsFlags, std::size_t... I>
|
||||
template <typename E, std::size_t... I>
|
||||
constexpr auto entries(std::index_sequence<I...>) noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::entries requires enum type.");
|
||||
|
||||
return std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E, IsFlags>[I], enum_name_v<E, values_v<E, IsFlags>[I]>}...}};
|
||||
return std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E>[I], enum_name_v<E, values_v<E>[I]>}...}};
|
||||
}
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr auto entries_v = entries<E, IsFlags>(std::make_index_sequence<count_v<E, IsFlags>>{});
|
||||
template <typename E>
|
||||
inline constexpr auto entries_v = entries<E>(std::make_index_sequence<count_v<E>>{});
|
||||
|
||||
template <typename E, bool IsFlags = false, typename D = std::decay_t<E>>
|
||||
using entries_t = decltype((entries_v<D, IsFlags>));
|
||||
template <typename E, typename D = std::decay_t<E>>
|
||||
using entries_t = decltype((entries_v<D>));
|
||||
|
||||
template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>>
|
||||
template <typename E, typename U = std::underlying_type_t<E>>
|
||||
constexpr bool is_sparse() noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::is_sparse requires enum type.");
|
||||
constexpr auto max = is_flags_v<E> ? log2(max_v<E>) : max_v<E>;
|
||||
constexpr auto min = is_flags_v<E> ? log2(min_v<E>) : min_v<E>;
|
||||
constexpr auto range_size = max - min + 1;
|
||||
|
||||
return range_size_v<E, IsFlags> != count_v<E, IsFlags>;
|
||||
return range_size != count_v<E>;
|
||||
}
|
||||
|
||||
template <typename E, bool IsFlags = false>
|
||||
inline constexpr bool is_sparse_v = is_sparse<E, IsFlags>();
|
||||
template <typename E>
|
||||
inline constexpr bool is_sparse_v = is_sparse<E>();
|
||||
|
||||
template <typename E, typename U = std::underlying_type_t<E>>
|
||||
constexpr std::size_t undex(U value) noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::undex requires enum type.");
|
||||
constexpr U values_ors() noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::values_ors requires enum type.");
|
||||
|
||||
if (const auto i = static_cast<std::size_t>(value - min_v<E>); value >= min_v<E> && value <= max_v<E>) {
|
||||
if constexpr (is_sparse_v<E>) {
|
||||
if (const auto idx = indexes_v<E>[i]; idx != invalid_index_v<E>) {
|
||||
return idx;
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
auto ors = U{0};
|
||||
for (std::size_t i = 0; i < count_v<E>; ++i) {
|
||||
ors |= static_cast<U>(values_v<E>[i]);
|
||||
}
|
||||
|
||||
return invalid_index_v<E>; // Value out of range.
|
||||
}
|
||||
|
||||
template <typename E, typename U = std::underlying_type_t<E>>
|
||||
constexpr std::size_t endex(E value) noexcept {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::endex requires enum type.");
|
||||
|
||||
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;
|
||||
return ors;
|
||||
}
|
||||
|
||||
template <bool, typename T, typename R>
|
||||
|
|
@ -656,8 +633,7 @@ using underlying_type_t = typename underlying_type<T>::type;
|
|||
// Returns type name of enum.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_enum_t<E, string_view> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr string_view name = detail::type_name_v<D>;
|
||||
constexpr string_view name = detail::type_name_v<std::decay_t<E>>;
|
||||
static_assert(name.size() > 0, "Enum type does not have a name.");
|
||||
|
||||
return name;
|
||||
|
|
@ -666,9 +642,7 @@ template <typename E>
|
|||
// Returns number of enum values.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t<E, std::size_t> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return detail::count_v<D>;
|
||||
return detail::count_v<std::decay_t<E>>;
|
||||
}
|
||||
|
||||
// Returns enum value at specified index.
|
||||
|
|
@ -676,40 +650,34 @@ 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>;
|
||||
static_assert(detail::count_v<D> > 0, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
if constexpr (detail::is_sparse_v<D>) {
|
||||
return assert((index < detail::count_v<D>)), detail::values_v<D>[index];
|
||||
} else {
|
||||
return assert((index < detail::count_v<D>)), detail::value<D, detail::min_v<D>>(index);
|
||||
constexpr bool is_flag = detail::is_flags_v<D>;
|
||||
constexpr auto min = is_flag ? detail::log2(detail::min_v<D>) : detail::min_v<D>;
|
||||
|
||||
return assert((index < detail::count_v<D>)), detail::value<D, min, is_flag>(index);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns enum value at specified index.
|
||||
template <typename E, std::size_t I>
|
||||
[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_enum_t<E, std::decay_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D> > 0, "magic_enum requires enum implementation and valid max and min.");
|
||||
static_assert(I < detail::count_v<D>, "magic_enum::enum_value out of range.");
|
||||
|
||||
return detail::values_v<D>[I];
|
||||
return enum_value<std::decay_t<E>>(I);
|
||||
}
|
||||
|
||||
// Returns std::array with enum values, sorted by enum value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t<E, detail::values_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D> > 0, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
return detail::values_v<D>;
|
||||
return detail::values_v<std::decay_t<E>>;
|
||||
}
|
||||
|
||||
// Returns name from static storage enum variable.
|
||||
// This version is much lighter on the compile times and is not restricted to the enum_range limitation.
|
||||
template <auto V>
|
||||
[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_enum_t<decltype(V), string_view> {
|
||||
using D = std::decay_t<decltype(V)>;
|
||||
constexpr string_view name = detail::enum_name_v<D, V>;
|
||||
constexpr string_view name = detail::enum_name_v<std::decay_t<decltype(V)>, V>;
|
||||
static_assert(name.size() > 0, "Enum value does not have a name.");
|
||||
|
||||
return name;
|
||||
|
|
@ -720,30 +688,65 @@ template <auto V>
|
|||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t<E, string_view> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if (const auto i = detail::endex<D>(value); i != detail::invalid_index_v<D>) {
|
||||
if constexpr (detail::is_sparse_v<D> || detail::is_flags_v<D>) {
|
||||
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
|
||||
if (enum_value<D>(i) == value) {
|
||||
return detail::names_v<D>[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto v = static_cast<U>(value);
|
||||
if (v >= detail::min_v<D> && v <= detail::max_v<D>) {
|
||||
return detail::names_v<D>[static_cast<std::size_t>(v - detail::min_v<E>)];
|
||||
}
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
||||
// Returns name from enum-flags value.
|
||||
// If enum-flags value does not have name or value out of range, returns empty string.
|
||||
template <typename E>
|
||||
[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_enum_t<E, string> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if constexpr (detail::is_flags_v<D>) {
|
||||
string name;
|
||||
auto check_value = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
|
||||
if (const auto v = static_cast<U>(enum_value<D>(i)); (static_cast<U>(value) & v) != 0) {
|
||||
check_value |= v;
|
||||
const auto n = detail::names_v<D>[i];
|
||||
if (!name.empty()) {
|
||||
name.append(1, '|');
|
||||
}
|
||||
name.append(n.data(), n.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (check_value != 0 && check_value == static_cast<U>(value)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
} else {
|
||||
return string{enum_name(value)};
|
||||
}
|
||||
}
|
||||
|
||||
// Returns std::array with names, sorted by enum value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t<E, detail::names_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D> > 0, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
return detail::names_v<D>;
|
||||
return detail::names_v<std::decay_t<E>>;
|
||||
}
|
||||
|
||||
// Returns std::array with pairs (value, name), sorted by enum value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t<E, detail::entries_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D> > 0, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
return detail::entries_v<D>;
|
||||
return detail::entries_v<std::decay_t<E>>;
|
||||
}
|
||||
|
||||
// Obtains enum value from integer value.
|
||||
|
|
@ -751,10 +754,36 @@ template <typename E>
|
|||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if (detail::undex<D>(value) != detail::invalid_index_v<D>) {
|
||||
if constexpr (detail::is_sparse_v<D>) {
|
||||
constexpr auto count = detail::count_v<D>;
|
||||
if constexpr (detail::is_flags_v<D>) {
|
||||
auto check_value = U{0};
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
if (const auto v = static_cast<U>(enum_value<D>(i)); (value & v) != 0) {
|
||||
check_value |= v;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_value != 0 && check_value == value) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
} else {
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
if (value == static_cast<U>(enum_value<D>(i))) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
constexpr auto min = detail::min_v<D>;
|
||||
constexpr auto max = detail::is_flags_v<D> ? detail::values_ors<D>() : detail::max_v<D>;
|
||||
|
||||
if (value >= min && value <= max) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
|
@ -765,12 +794,37 @@ template <typename E, typename BinaryPredicate>
|
|||
[[nodiscard]] constexpr auto enum_cast(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, optional<std::decay_t<E>>> {
|
||||
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_cast requires bool(char, char) invocable predicate.");
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if constexpr (detail::is_flags_v<D>) {
|
||||
auto result = U{0};
|
||||
while (!value.empty()) {
|
||||
const auto d = detail::find(value, '|');
|
||||
const auto s = (d == string_view::npos) ? value : value.substr(0, d);
|
||||
auto f = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
|
||||
if (detail::cmp_equal(s, detail::names_v<D>[i], p)) {
|
||||
f = static_cast<U>(enum_value<D>(i));
|
||||
result |= f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f == U{0}) {
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
value.remove_prefix((d == string_view::npos) ? value.size() : d + 1);
|
||||
}
|
||||
|
||||
if (result != U{0}) {
|
||||
return static_cast<D>(result);
|
||||
}
|
||||
} else {
|
||||
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
|
||||
if (detail::cmp_equal(value, detail::names_v<D>[i], p)) {
|
||||
return enum_value<D>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
|
@ -795,10 +849,20 @@ template <typename E>
|
|||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t<E, optional<std::size_t>> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if (const auto i = detail::endex<D>(value); i != detail::invalid_index_v<D>) {
|
||||
if constexpr (detail::is_sparse_v<D> || detail::is_flags_v<D>) {
|
||||
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
|
||||
if (enum_value<D>(i) == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto v = static_cast<U>(value);
|
||||
if (v >= detail::min_v<D> && v <= detail::max_v<D>) {
|
||||
return static_cast<std::size_t>(v - detail::min_v<E>);
|
||||
}
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
|
@ -807,43 +871,39 @@ template <typename E>
|
|||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
return detail::endex<D>(value) != detail::invalid_index_v<D>;
|
||||
return enum_cast<D>(static_cast<U>(value)).has_value();
|
||||
}
|
||||
|
||||
// Checks whether enum contains enumerator with such integer value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return detail::undex<D>(value) != detail::invalid_index_v<D>;
|
||||
return enum_cast<std::decay_t<E>>(value).has_value();
|
||||
}
|
||||
|
||||
// Checks whether enum contains enumerator with such name.
|
||||
template <typename E, typename BinaryPredicate>
|
||||
[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, bool> {
|
||||
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_contains requires bool(char, char) invocable predicate.");
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return enum_cast<D>(value, std::move_if_noexcept(p)).has_value();
|
||||
return enum_cast<std::decay_t<E>>(value, std::move_if_noexcept(p)).has_value();
|
||||
}
|
||||
|
||||
// Checks whether enum contains enumerator with such name.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_contains(string_view value) noexcept -> detail::enable_if_enum_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return enum_cast<D>(value).has_value();
|
||||
return enum_cast<std::decay_t<E>>(value).has_value();
|
||||
}
|
||||
|
||||
namespace ostream_operators {
|
||||
|
||||
template <typename Char, typename Traits, typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_enum_t<E, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED
|
||||
if (const auto name = magic_enum::enum_name<D>(value); !name.empty()) {
|
||||
if (const auto name = magic_enum::enum_flags_name<D>(value); !name.empty()) {
|
||||
for (const auto c : name) {
|
||||
os.put(c);
|
||||
}
|
||||
|
|
@ -853,7 +913,7 @@ std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& o
|
|||
return (os << static_cast<U>(value));
|
||||
}
|
||||
|
||||
template <typename Char, typename Traits, typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_enum_t<E, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
|
||||
return value.has_value() ? (os << value.value()) : os;
|
||||
}
|
||||
|
|
@ -862,295 +922,43 @@ std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& o
|
|||
|
||||
namespace bitwise_operators {
|
||||
|
||||
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename E, detail::enable_if_enum_t<E, int> = 0>
|
||||
constexpr E operator~(E rhs) noexcept {
|
||||
return static_cast<E>(~static_cast<underlying_type_t<E>>(rhs));
|
||||
}
|
||||
|
||||
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename E, detail::enable_if_enum_t<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, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename E, detail::enable_if_enum_t<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, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename E, detail::enable_if_enum_t<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, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename E, detail::enable_if_enum_t<E, int> = 0>
|
||||
constexpr E& operator|=(E& lhs, E rhs) noexcept {
|
||||
return lhs = (lhs | rhs);
|
||||
}
|
||||
|
||||
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename E, detail::enable_if_enum_t<E, int> = 0>
|
||||
constexpr E& operator&=(E& lhs, E rhs) noexcept {
|
||||
return lhs = (lhs & rhs);
|
||||
}
|
||||
|
||||
template <typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
template <typename E, detail::enable_if_enum_t<E, int> = 0>
|
||||
constexpr E& operator^=(E& lhs, E rhs) noexcept {
|
||||
return lhs = (lhs ^ rhs);
|
||||
}
|
||||
|
||||
} // namespace magic_enum::bitwise_operators
|
||||
|
||||
namespace flags {
|
||||
|
||||
// Returns type name of enum.
|
||||
using magic_enum::enum_type_name;
|
||||
|
||||
// Returns number of enum-flags values.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t<E, std::size_t> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return detail::count_v<D, true>;
|
||||
}
|
||||
|
||||
// Returns enum-flags value at specified index.
|
||||
// No bounds checking is performed: the behavior is undefined if index >= number of enum-flags values.
|
||||
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>;
|
||||
static_assert(detail::count_v<D, true> > 0, "magic_enum::flags requires enum-flags implementation.");
|
||||
|
||||
if constexpr (detail::is_sparse_v<D, true>) {
|
||||
return assert((index < detail::count_v<D, true>)), detail::values_v<D, true>[index];
|
||||
} else {
|
||||
constexpr auto min = detail::log2(detail::min_v<D, true>);
|
||||
|
||||
return assert((index < detail::count_v<D, true>)), detail::value<D, min, true>(index);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns enum-flags value at specified index.
|
||||
template <typename E, std::size_t I>
|
||||
[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_enum_t<E, std::decay_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D, true> > 0, "magic_enum::flags requires enum implementation and valid max and min.");
|
||||
static_assert(I < detail::count_v<D, true>, "magic_enum::flags::enum_value out of range.");
|
||||
|
||||
return detail::values_v<D, true>[I];
|
||||
}
|
||||
|
||||
// Returns std::array with enum-flags values, sorted by enum-flags value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t<E, detail::values_t<E, true>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D, true> > 0, "magic_enum::flags requires enum-flags implementation.");
|
||||
|
||||
return detail::values_v<D, true>;
|
||||
}
|
||||
|
||||
// Returns name from enum-flags value.
|
||||
// If enum-flags value does not have name or value out of range, returns empty string.
|
||||
template <typename E>
|
||||
[[nodiscard]] auto enum_name(E value) -> detail::enable_if_enum_t<E, string> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
string name;
|
||||
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;
|
||||
const auto n = detail::names_v<D, true>[i];
|
||||
if (!name.empty()) {
|
||||
name.append(1, '|');
|
||||
}
|
||||
name.append(n.data(), n.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (check_value != 0 && check_value == static_cast<U>(value)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
||||
// Returns std::array with string names, sorted by enum-flags value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t<E, detail::names_t<E, true>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D, true> > 0, "magic_enum::flags requires enum-flags implementation.");
|
||||
|
||||
return detail::names_v<D, true>;
|
||||
}
|
||||
|
||||
// Returns std::array with pairs (value, name), sorted by enum-flags value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t<E, detail::entries_t<E, true>> {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(detail::count_v<D, true> > 0, "magic_enum::flags requires enum-flags implementation.");
|
||||
|
||||
return detail::entries_v<D, true>;
|
||||
}
|
||||
|
||||
// Obtains enum-flags value from integer value.
|
||||
// Returns optional with enum-flags value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if constexpr (detail::is_sparse_v<D, true>) {
|
||||
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)); (value & v) != 0) {
|
||||
check_value |= v;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_value != 0 && check_value == value) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
} else {
|
||||
constexpr auto min = detail::min_v<D, true>;
|
||||
constexpr auto max = detail::value_ors<D>();
|
||||
|
||||
if (value >= min && value <= max) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
||||
// Obtains enum-flags value from name.
|
||||
// Returns optional with enum-flags value.
|
||||
template <typename E, typename BinaryPredicate>
|
||||
[[nodiscard]] constexpr auto enum_cast(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, optional<std::decay_t<E>>> {
|
||||
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::flags::enum_cast requires bool(char, char) invocable predicate.");
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
auto result = U{0};
|
||||
while (!value.empty()) {
|
||||
const auto d = detail::find(value, '|');
|
||||
const auto s = (d == string_view::npos) ? value : value.substr(0, d);
|
||||
auto f = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D, true>; ++i) {
|
||||
if (detail::cmp_equal(s, detail::names_v<D, true>[i], p)) {
|
||||
f = static_cast<U>(enum_value<D>(i));
|
||||
result |= f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f == U{0}) {
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
value.remove_prefix((d == string_view::npos) ? value.size() : d + 1);
|
||||
}
|
||||
|
||||
if (result == U{0}) {
|
||||
return {}; // Invalid value or out of range.
|
||||
} else {
|
||||
return static_cast<D>(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Obtains enum-flags value from name.
|
||||
// Returns optional with enum-flags value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_cast(string_view value) noexcept -> detail::enable_if_enum_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return enum_cast<D>(value, detail::char_equal_to{});
|
||||
}
|
||||
|
||||
// Returns integer value from enum value.
|
||||
using magic_enum::enum_integer;
|
||||
|
||||
// Obtains index in enum-flags values from enum-flags value.
|
||||
// Returns optional with index.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t<E, optional<std::size_t>> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if (detail::is_pow2(static_cast<U>(value))) {
|
||||
for (std::size_t i = 0; i < detail::count_v<D, true>; ++i) {
|
||||
if (enum_value<D>(i) == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
||||
// 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_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
return enum_cast<D>(static_cast<U>(value)).has_value();
|
||||
}
|
||||
|
||||
// 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_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return enum_cast<D>(value).has_value();
|
||||
}
|
||||
|
||||
// Checks whether enum-flags contains enumerator with such name.
|
||||
template <typename E, typename BinaryPredicate>
|
||||
[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, bool> {
|
||||
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::flags::enum_contains requires bool(char, char) invocable predicate.");
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return enum_cast<D>(value, std::move_if_noexcept(p)).has_value();
|
||||
}
|
||||
|
||||
// Checks whether enum-flags contains enumerator with such name.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_contains(string_view value) noexcept -> detail::enable_if_enum_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return enum_cast<D>(value).has_value();
|
||||
}
|
||||
|
||||
} // namespace magic_enum::flags
|
||||
|
||||
namespace flags::ostream_operators {
|
||||
|
||||
template <typename Char, typename Traits, typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED
|
||||
if (const auto name = magic_enum::flags::enum_name<D>(value); !name.empty()) {
|
||||
for (const auto c : name) {
|
||||
os.put(c);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
return (os << static_cast<U>(value));
|
||||
}
|
||||
|
||||
template <typename Char, typename Traits, typename E, std::enable_if_t<std::is_enum_v<E>, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
|
||||
return value.has_value() ? (os << value.value()) : os;
|
||||
}
|
||||
|
||||
} // namespace magic_enum::flags::ostream_operators
|
||||
|
||||
namespace flags::bitwise_operators {
|
||||
|
||||
using namespace magic_enum::bitwise_operators;
|
||||
|
||||
} // namespace magic_enum::flags::bitwise_operators
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#if defined(__clang__)
|
||||
|
|
|
|||
|
|
@ -57,38 +57,33 @@ enum number : unsigned long {
|
|||
_4 = four
|
||||
#endif
|
||||
};
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<number> {
|
||||
static constexpr int min = 100;
|
||||
static constexpr int max = 300;
|
||||
};
|
||||
|
||||
enum class MaxUsedAsInvalid : std::uint8_t {
|
||||
ONE,
|
||||
TWO,
|
||||
INVALID = std::numeric_limits<std::uint8_t>::max()
|
||||
};
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<MaxUsedAsInvalid> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 64;
|
||||
};
|
||||
|
||||
enum class Binary : bool {
|
||||
ONE,
|
||||
TWO
|
||||
};
|
||||
|
||||
namespace magic_enum::customize {
|
||||
template <>
|
||||
struct enum_range<MaxUsedAsInvalid> {
|
||||
struct magic_enum::customize::enum_range<Binary> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 64;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct enum_range<Binary> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 64;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct enum_range<number> {
|
||||
static constexpr int min = 100;
|
||||
static constexpr int max = 300;
|
||||
};
|
||||
} // namespace magic_enum
|
||||
|
||||
template <>
|
||||
constexpr std::string_view magic_enum::customize::enum_name<Color>(Color value) noexcept {
|
||||
switch (value) {
|
||||
|
|
@ -847,69 +842,69 @@ TEST_CASE("extrema") {
|
|||
|
||||
SECTION("min") {
|
||||
REQUIRE(magic_enum::customize::enum_range<BadColor>::min == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<BadColor> == 0);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<BadColor, false> == 0);
|
||||
REQUIRE(magic_enum::detail::min_v<BadColor> == 0);
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<Color>::min == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Color> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Color, false> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::min_v<Color> == -12);
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<Numbers>::min == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Numbers> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Numbers, false> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::min_v<Numbers> == 1);
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<Directions>::min == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Directions> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Directions, false> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::min_v<Directions> == -120);
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_NONASCII)
|
||||
REQUIRE(magic_enum::customize::enum_range<Language>::min == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Language> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Language, false> == MAGIC_ENUM_RANGE_MIN);
|
||||
REQUIRE(magic_enum::detail::min_v<Language> == 10);
|
||||
#endif
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<number>::min == 100);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<number> == 100);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<number, false> == 100);
|
||||
REQUIRE(magic_enum::detail::min_v<number> == 100);
|
||||
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Binary> == 0);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<Binary, false> == 0);
|
||||
REQUIRE(magic_enum::detail::min_v<Binary> == false);
|
||||
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<MaxUsedAsInvalid> == 0);
|
||||
REQUIRE(magic_enum::detail::reflected_min_v<MaxUsedAsInvalid,false> == 0);
|
||||
REQUIRE(magic_enum::detail::min_v<MaxUsedAsInvalid> == 0);
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
REQUIRE(magic_enum::customize::enum_range<BadColor>::max == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<BadColor> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<BadColor, false> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::max_v<BadColor> == 2);
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<Color>::max == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Color> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Color, false> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::max_v<Color> == 15);
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<Numbers>::max == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Numbers> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Numbers, false> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::max_v<Numbers> == 3);
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<Directions>::max == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Directions> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Directions, false> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::max_v<Directions> == 120);
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_NONASCII)
|
||||
REQUIRE(magic_enum::customize::enum_range<Language>::max == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Language> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Language, false> == MAGIC_ENUM_RANGE_MAX);
|
||||
REQUIRE(magic_enum::detail::max_v<Language> == 40);
|
||||
#endif
|
||||
|
||||
REQUIRE(magic_enum::customize::enum_range<number>::max == 300);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<number> == 300);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<number, false> == 300);
|
||||
REQUIRE(magic_enum::detail::max_v<number> == 300);
|
||||
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Binary> == 1);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<Binary, false> == 1);
|
||||
REQUIRE(magic_enum::detail::max_v<Binary> == true);
|
||||
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<MaxUsedAsInvalid> == 64);
|
||||
REQUIRE(magic_enum::detail::reflected_max_v<MaxUsedAsInvalid, false> == 64);
|
||||
REQUIRE(magic_enum::detail::max_v<MaxUsedAsInvalid> == 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@
|
|||
#include <sstream>
|
||||
|
||||
enum class Color { RED = 1, GREEN = 2, BLUE = 4 };
|
||||
template <>
|
||||
struct magic_enum::customize::is_flags_enum<Color> : std::true_type {};
|
||||
|
||||
enum class Numbers : int {
|
||||
one = 1 << 1,
|
||||
|
|
@ -77,16 +79,13 @@ enum number : unsigned long {
|
|||
_4 = four
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace magic_enum::customize {
|
||||
template <>
|
||||
struct enum_range<number> {
|
||||
struct magic_enum::customize::enum_range<number> {
|
||||
static constexpr int min = 100;
|
||||
static constexpr int max = 300;
|
||||
};
|
||||
} // namespace magic_enum
|
||||
|
||||
using namespace magic_enum::flags;
|
||||
using namespace magic_enum;
|
||||
using namespace magic_enum::bitwise_operators;
|
||||
|
||||
TEST_CASE("enum_cast") {
|
||||
|
|
@ -453,39 +452,39 @@ TEST_CASE("enum_count") {
|
|||
TEST_CASE("enum_name") {
|
||||
SECTION("automatic storage") {
|
||||
constexpr Color cr = Color::RED;
|
||||
auto cr_name = enum_name(cr);
|
||||
constexpr auto cr_name = enum_name(cr);
|
||||
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
|
||||
Color cb = Color::BLUE;
|
||||
REQUIRE(cr_name == "RED");
|
||||
REQUIRE(enum_name<Color&>(cb) == "BLUE");
|
||||
REQUIRE(enum_name(cm[1]) == "GREEN");
|
||||
REQUIRE(enum_name(Color::RED | Color{0}) == "RED");
|
||||
REQUIRE(enum_name(Color::RED | Color::GREEN) == "RED|GREEN");
|
||||
REQUIRE(enum_name(Color::RED | Color::GREEN).empty());
|
||||
REQUIRE(enum_name(Color::RED | Color{8}).empty());
|
||||
REQUIRE(enum_name(static_cast<Color>(0)).empty());
|
||||
|
||||
constexpr Numbers no = Numbers::one;
|
||||
auto no_name = enum_name(no);
|
||||
constexpr auto no_name = enum_name(no);
|
||||
REQUIRE(no_name == "one");
|
||||
REQUIRE(enum_name(Numbers::two) == "two");
|
||||
REQUIRE(enum_name(Numbers::three) == "three");
|
||||
REQUIRE(enum_name(Numbers::many) == "many");
|
||||
REQUIRE(enum_name(Numbers::many | Numbers::two) == "two|many");
|
||||
REQUIRE(enum_name(Numbers::many | Numbers::two).empty());
|
||||
REQUIRE(enum_name(static_cast<Numbers>(0)).empty());
|
||||
|
||||
constexpr Directions dr = Directions::Right;
|
||||
auto dr_name = enum_name(dr);
|
||||
constexpr auto dr_name = enum_name(dr);
|
||||
Directions du = Directions::Up;
|
||||
REQUIRE(enum_name<Directions&>(du) == "Up");
|
||||
REQUIRE(enum_name<const Directions>(Directions::Down) == "Down");
|
||||
REQUIRE(dr_name == "Right");
|
||||
REQUIRE(enum_name(Directions::Left) == "Left");
|
||||
REQUIRE(enum_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down) == "Left|Down|Up|Right");
|
||||
REQUIRE(enum_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down).empty());
|
||||
REQUIRE(enum_name(static_cast<Directions>(0)).empty());
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_NONASCII)
|
||||
constexpr Language lang = Language::日本語;
|
||||
auto lang_name = enum_name(lang);
|
||||
constexpr auto lang_name = enum_name(lang);
|
||||
Language lk = Language::한국어;
|
||||
REQUIRE(enum_name<Language&>(lk) == "한국어");
|
||||
REQUIRE(enum_name<const Language>(Language::English) == "English");
|
||||
|
|
@ -495,16 +494,69 @@ TEST_CASE("enum_name") {
|
|||
#endif
|
||||
|
||||
constexpr number nto = number::three | number::one;
|
||||
auto nto_name = enum_name(nto);
|
||||
constexpr auto nto_name = enum_name(nto);
|
||||
REQUIRE(enum_name(number::one) == "one");
|
||||
REQUIRE(enum_name(number::two) == "two");
|
||||
REQUIRE(enum_name(number::three) == "three");
|
||||
REQUIRE(enum_name(number::four) == "four");
|
||||
REQUIRE(nto_name == "one|three");
|
||||
REQUIRE(nto_name.empty());
|
||||
REQUIRE(enum_name(static_cast<number>(0)).empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("enum_flags_name") {
|
||||
constexpr Color cr = Color::RED;
|
||||
auto cr_name = enum_flags_name(cr);
|
||||
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
|
||||
Color cb = Color::BLUE;
|
||||
REQUIRE(cr_name == "RED");
|
||||
REQUIRE(enum_flags_name<Color&>(cb) == "BLUE");
|
||||
REQUIRE(enum_flags_name(cm[1]) == "GREEN");
|
||||
REQUIRE(enum_flags_name(Color::RED | Color{0}) == "RED");
|
||||
REQUIRE(enum_flags_name(Color::RED | Color::GREEN) == "RED|GREEN");
|
||||
REQUIRE(enum_flags_name(Color::RED | Color{8}).empty());
|
||||
REQUIRE(enum_flags_name(static_cast<Color>(0)).empty());
|
||||
|
||||
constexpr Numbers no = Numbers::one;
|
||||
auto no_name = enum_flags_name(no);
|
||||
REQUIRE(no_name == "one");
|
||||
REQUIRE(enum_flags_name(Numbers::two) == "two");
|
||||
REQUIRE(enum_flags_name(Numbers::three) == "three");
|
||||
REQUIRE(enum_flags_name(Numbers::many) == "many");
|
||||
REQUIRE(enum_flags_name(Numbers::many | Numbers::two) == "two|many");
|
||||
REQUIRE(enum_flags_name(static_cast<Numbers>(0)).empty());
|
||||
|
||||
constexpr Directions dr = Directions::Right;
|
||||
auto dr_name = enum_flags_name(dr);
|
||||
Directions du = Directions::Up;
|
||||
REQUIRE(enum_flags_name<Directions&>(du) == "Up");
|
||||
REQUIRE(enum_flags_name<const Directions>(Directions::Down) == "Down");
|
||||
REQUIRE(dr_name == "Right");
|
||||
REQUIRE(enum_flags_name(Directions::Left) == "Left");
|
||||
REQUIRE(enum_flags_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down) == "Left|Down|Up|Right");
|
||||
REQUIRE(enum_flags_name(static_cast<Directions>(0)).empty());
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_NONASCII)
|
||||
constexpr Language lang = Language::日本語;
|
||||
auto lang_name = enum_flags_name(lang);
|
||||
Language lk = Language::한국어;
|
||||
REQUIRE(enum_flags_name<Language&>(lk) == "한국어");
|
||||
REQUIRE(enum_flags_name<const Language>(Language::English) == "English");
|
||||
REQUIRE(lang_name == "日本語");
|
||||
REQUIRE(enum_flags_name(Language::😃) == "😃");
|
||||
REQUIRE(enum_flags_name(static_cast<Language>(0)).empty());
|
||||
#endif
|
||||
|
||||
constexpr number nto = number::three | number::one;
|
||||
auto nto_name = enum_flags_name(nto);
|
||||
REQUIRE(enum_flags_name(number::one) == "one");
|
||||
REQUIRE(enum_flags_name(number::two) == "two");
|
||||
REQUIRE(enum_flags_name(number::three) == "three");
|
||||
REQUIRE(enum_flags_name(number::four) == "four");
|
||||
REQUIRE(nto_name == "one|three");
|
||||
REQUIRE(enum_flags_name(static_cast<number>(0)).empty());
|
||||
}
|
||||
|
||||
TEST_CASE("enum_names") {
|
||||
REQUIRE(std::is_same_v<decltype(magic_enum::enum_names<Color>()), const std::array<std::string_view, 3>&>);
|
||||
|
||||
|
|
@ -549,7 +601,7 @@ TEST_CASE("enum_entries") {
|
|||
|
||||
TEST_CASE("ostream_operators") {
|
||||
auto test_ostream = [](auto e, std::string_view name) {
|
||||
using namespace magic_enum::flags::ostream_operators;
|
||||
using namespace magic_enum::ostream_operators;
|
||||
std::stringstream ss;
|
||||
ss << e;
|
||||
REQUIRE(ss.str() == name);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue