mirror of
https://github.com/Neargye/magic_enum.git
synced 2026-01-09 23:34:23 +00:00
Add adl_ranges (#413)
Co-authored-by: lsemprini <17140216+lsemprini@users.noreply.github.com>
This commit is contained in:
parent
513e606d7b
commit
d642b05dcb
5 changed files with 174 additions and 14 deletions
|
|
@ -23,6 +23,32 @@
|
||||||
|
|
||||||
* If an enum is declared as a flag enum, its zero value will not be reflected.
|
* If an enum is declared as a flag enum, its zero value will not be reflected.
|
||||||
|
|
||||||
|
* Or, for enum types that are deeply nested in classes and/or namespaces, declare a function called `my_adl_info_struct adl_magic_enum_define_range(my_enum_type)` in the same namespace as `my_enum_type`, which magic_enum will find by ADL (because the function is in the same class/namespace as `my_enum_type`), and whose return type is a struct with `static constexpr` data members containing the same parameters as `magic_enum::customize::enum_range<my_enum_type>`
|
||||||
|
```cpp
|
||||||
|
namespace Deeply::Nested::Namespace {
|
||||||
|
enum class my_enum_type { ... };
|
||||||
|
struct my_adl_info_struct {
|
||||||
|
static constexpr bool is_flags = true;
|
||||||
|
// you can also set min and max here (see Enum Range below)
|
||||||
|
// static constexpr int min = ...;
|
||||||
|
// static constexpr int max = ...;
|
||||||
|
};
|
||||||
|
// - magic_enum will find this function by ADL
|
||||||
|
// - no need to ever define this function
|
||||||
|
my_adl_info_struct adl_magic_enum_define_range(my_enum_type);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* As a shorthand, if you only want to set `is_flags` and not `min` or `max`, you can also use `magic_enum::customize::adl_info<is_flags_bool>` to avoid having to define `my_adl_info_struct` in your code:
|
||||||
|
```cpp
|
||||||
|
namespace Deeply::Nested::Namespace {
|
||||||
|
enum class my_enum_type { ... };
|
||||||
|
// - magic_enum will find this function by ADL
|
||||||
|
// - no need to ever define this function
|
||||||
|
magic_enum::customize::adl_info<true> adl_magic_enum_define_range(my_enum_type);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Enum Range
|
## Enum Range
|
||||||
|
|
||||||
* Enum values must be in the range `[MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]`.
|
* Enum values must be in the range `[MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]`.
|
||||||
|
|
@ -52,6 +78,32 @@
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Or, for enum types that are deeply nested in classes and/or namespaces, declare a function called `my_adl_info_struct adl_magic_enum_define_range(my_enum_type)` in the same namespace as `my_enum_type`, which magic_enum will find by ADL (because the function is in the same class/namespace as `my_enum_type`), and whose return type is a struct with `static constexpr` data members containing the same parameters as `magic_enum::customize::enum_range<my_enum_type>`
|
||||||
|
```cpp
|
||||||
|
namespace Deeply::Nested::Namespace {
|
||||||
|
enum class my_enum_type { ... };
|
||||||
|
struct my_adl_info_struct {
|
||||||
|
static constexpr int min = 100;
|
||||||
|
static constexpr int max = 300;
|
||||||
|
// you can also set is_flags here
|
||||||
|
// static constexpr bool is_flags = true;
|
||||||
|
};
|
||||||
|
// - magic_enum will find this function by ADL
|
||||||
|
// - no need to ever define this function
|
||||||
|
my_adl_info_struct adl_magic_enum_define_range(my_enum_type);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* As a shorthand, if you only want to set `min` and `max` and not `is_flags`, you can also use `magic_enum::customize::adl_info<min_int, max_int>` to avoid having to define `my_adl_info_struct` in your code:
|
||||||
|
```cpp
|
||||||
|
namespace Deeply::Nested::Namespace {
|
||||||
|
enum class my_enum_type { ... };
|
||||||
|
// - magic_enum will find this function by ADL
|
||||||
|
// - no need to ever define this function
|
||||||
|
magic_enum::customize::adl_info<100 /*min*/, 300 /*max*/> adl_magic_enum_define_range(my_enum_type);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Aliasing
|
## Aliasing
|
||||||
|
|
||||||
magic_enum [won't work if a value is aliased](https://github.com/Neargye/magic_enum/issues/68). How magic_enum works with aliases is compiler-implementation-defined.
|
magic_enum [won't work if a value is aliased](https://github.com/Neargye/magic_enum/issues/68). How magic_enum works with aliases is compiler-implementation-defined.
|
||||||
|
|
|
||||||
|
|
@ -512,6 +512,32 @@ constexpr bool enum_flags_contains(string_view value, BinaryPredicate p) noexcep
|
||||||
magic_enum::enum_flags_test_any(Left|Down|Right, Down|Right); // -> "true"
|
magic_enum::enum_flags_test_any(Left|Down|Right, Down|Right); // -> "true"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Or, for enum types that are deeply nested in classes and/or namespaces, declare a function called `my_adl_info_struct adl_magic_enum_define_range(my_enum_type)` in the same namespace as `my_enum_type`, which magic_enum will find by ADL (because the function is in the same class/namespace as `my_enum_type`), and whose return type is a struct with `static constexpr` data members containing the same parameters as `magic_enum::customize::enum_range<my_enum_type>`
|
||||||
|
```cpp
|
||||||
|
namespace Deeply::Nested::Namespace {
|
||||||
|
enum class my_enum_type { ... };
|
||||||
|
struct my_adl_info_struct {
|
||||||
|
static constexpr bool is_flags = true;
|
||||||
|
// you can also set min and max here (see Limitations document)
|
||||||
|
// static constexpr int min = ...;
|
||||||
|
// static constexpr int max = ...;
|
||||||
|
};
|
||||||
|
// - magic_enum will find this function by ADL
|
||||||
|
// - no need to ever define this function
|
||||||
|
my_adl_info_struct adl_magic_enum_define_range(my_enum_type);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* As a shorthand, if you only want to set `is_flags` and not `min` or `max`, you can also use `magic_enum::customize::adl_info<is_flags_bool>` to avoid having to define `my_adl_info_struct` in your code:
|
||||||
|
```cpp
|
||||||
|
namespace Deeply::Nested::Namespace {
|
||||||
|
enum class my_enum_type { ... };
|
||||||
|
// - magic_enum will find this function by ADL
|
||||||
|
// - no need to ever define this function
|
||||||
|
magic_enum::customize::adl_info<true> adl_magic_enum_define_range(my_enum_type);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `is_unscoped_enum`
|
## `is_unscoped_enum`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|
|
||||||
|
|
@ -163,15 +163,57 @@ static_assert([] {
|
||||||
return true;
|
return true;
|
||||||
} (), "magic_enum::customize wchar_t is not compatible with ASCII.");
|
} (), "magic_enum::customize wchar_t is not compatible with ASCII.");
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template<typename E, typename = void>
|
||||||
|
constexpr inline bool has_is_flags_adl = false;
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
constexpr inline bool has_is_flags_adl < E, std::void_t<decltype(decltype(adl_magic_enum_define_range(E{}))::is_flags) > > = decltype(adl_magic_enum_define_range(E{}))::is_flags;
|
||||||
|
|
||||||
|
template<typename E, typename = void>
|
||||||
|
constexpr inline auto has_minmax_adl = std::pair<int, int>(MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX);
|
||||||
|
|
||||||
|
template<typename E>
|
||||||
|
constexpr inline auto has_minmax_adl < E, std::void_t<decltype(decltype(adl_magic_enum_define_range(E{}))::max), decltype(decltype(adl_magic_enum_define_range(E{}))::max) >> =
|
||||||
|
std::pair<int, int>(decltype(adl_magic_enum_define_range(E{}))::min, decltype(adl_magic_enum_define_range(E{}))::max);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace customize {
|
namespace customize {
|
||||||
|
|
||||||
|
template<auto... Vs>
|
||||||
|
struct adl_info { static_assert(sizeof...(Vs) && !sizeof...(Vs), "adl_info parameter types must be either 2 ints exactly or 1 bool for the is_flgas"); };
|
||||||
|
|
||||||
|
template<int Min, int Max>
|
||||||
|
struct adl_info<Min, Max> {
|
||||||
|
static constexpr int min = Min;
|
||||||
|
static constexpr int max = Max;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<bool IsFlags>
|
||||||
|
struct adl_info<IsFlags> {
|
||||||
|
static constexpr bool is_flags = IsFlags;
|
||||||
|
};
|
||||||
|
|
||||||
// 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 = 127.
|
// 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 = 127.
|
||||||
// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX.
|
// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX.
|
||||||
// If need another range for specific enum type, add specialization enum_range for necessary enum type.
|
// If need another range for specific enum type, add specialization enum_range for necessary enum type.
|
||||||
template <typename E>
|
template <typename E, typename = void>
|
||||||
struct enum_range {
|
struct enum_range {
|
||||||
static constexpr int min = MAGIC_ENUM_RANGE_MIN;
|
static constexpr int min = MAGIC_ENUM_RANGE_MIN;
|
||||||
static constexpr int max = MAGIC_ENUM_RANGE_MAX;
|
static constexpr int max = MAGIC_ENUM_RANGE_MAX;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
struct enum_range < E, decltype(void(adl_magic_enum_define_range(E{}))) > {
|
||||||
|
static constexpr int min = detail::has_minmax_adl<E>.first;
|
||||||
|
static constexpr int max = detail::has_minmax_adl<E>.second;
|
||||||
|
static constexpr bool is_flags = detail::has_is_flags_adl<E>;
|
||||||
|
|
||||||
|
static_assert(is_flags || min != MAGIC_ENUM_RANGE_MIN || max != MAGIC_ENUM_RANGE_MAX,
|
||||||
|
"adl_magic_enum_define_range is declared for this enum but does not define any constants.\n"
|
||||||
|
"be sure that the member names are static and are not mispelled.");
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN.");
|
static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN.");
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,29 @@ struct magic_enum::customize::enum_range<Binary> {
|
||||||
static constexpr int max = 64;
|
static constexpr int max = 64;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace We::Need::To::Go::Deeper {
|
||||||
|
enum class Dimension : short { Overworld = 1000, Nether, TheEnd = Overworld + 128 };
|
||||||
|
enum class Flaggy : std::uint64_t { Flag0 = 1 << 0, Flag32 = std::uint64_t(1) << 32 };
|
||||||
|
|
||||||
|
auto adl_magic_enum_define_range(Dimension)
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
min = 1000,
|
||||||
|
max = 1000 + 128
|
||||||
|
} e{};
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FlaggyData {
|
||||||
|
static constexpr bool is_flags = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// not defined!
|
||||||
|
FlaggyData adl_magic_enum_define_range(Flaggy);
|
||||||
|
}
|
||||||
|
using We::Need::To::Go::Deeper::Dimension;
|
||||||
|
using We::Need::To::Go::Deeper::Flaggy;
|
||||||
|
|
||||||
enum class BoolTest : bool { Yay, Nay };
|
enum class BoolTest : bool { Yay, Nay };
|
||||||
|
|
||||||
using namespace magic_enum;
|
using namespace magic_enum;
|
||||||
|
|
@ -113,6 +136,12 @@ TEST_CASE("enum_cast") {
|
||||||
REQUIRE(enum_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
|
REQUIRE(enum_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
|
||||||
REQUIRE_FALSE(enum_cast<Color>("None").has_value());
|
REQUIRE_FALSE(enum_cast<Color>("None").has_value());
|
||||||
|
|
||||||
|
constexpr auto dim = enum_cast<Dimension>("Nether");
|
||||||
|
REQUIRE(dim.value() == Dimension::Nether);
|
||||||
|
REQUIRE(enum_cast<Dimension&>("Nether").value() == Dimension::Nether);
|
||||||
|
REQUIRE(enum_cast<Dimension>("theend", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Dimension::TheEnd);
|
||||||
|
REQUIRE_FALSE(enum_cast<Dimension>("Aether").has_value());
|
||||||
|
|
||||||
constexpr auto no = enum_cast<Numbers>("one");
|
constexpr auto no = enum_cast<Numbers>("one");
|
||||||
REQUIRE(no.value() == Numbers::one);
|
REQUIRE(no.value() == Numbers::one);
|
||||||
REQUIRE(enum_cast<Numbers>("two").value() == Numbers::two);
|
REQUIRE(enum_cast<Numbers>("two").value() == Numbers::two);
|
||||||
|
|
@ -427,6 +456,13 @@ TEST_CASE("enum_values") {
|
||||||
|
|
||||||
constexpr auto& s6 = enum_values<MaxUsedAsInvalid>();
|
constexpr auto& s6 = enum_values<MaxUsedAsInvalid>();
|
||||||
REQUIRE(s6 == std::array<MaxUsedAsInvalid, 2>{{MaxUsedAsInvalid::ONE, MaxUsedAsInvalid::TWO}});
|
REQUIRE(s6 == std::array<MaxUsedAsInvalid, 2>{{MaxUsedAsInvalid::ONE, MaxUsedAsInvalid::TWO}});
|
||||||
|
|
||||||
|
constexpr auto& s7 = enum_values<Dimension>();
|
||||||
|
REQUIRE(s7 == std::array<Dimension, 3>{{Dimension::Overworld, Dimension::Nether, Dimension::TheEnd}});
|
||||||
|
|
||||||
|
constexpr auto& s8 = enum_values<Flaggy>();
|
||||||
|
REQUIRE(s8 == std::array<Flaggy, 2>{{Flaggy::Flag0, Flaggy::Flag32}});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("enum_count") {
|
TEST_CASE("enum_count") {
|
||||||
|
|
@ -932,6 +968,10 @@ TEST_CASE("extrema") {
|
||||||
REQUIRE(magic_enum::detail::reflected_min<BadColor, as_common<>>() == 0);
|
REQUIRE(magic_enum::detail::reflected_min<BadColor, as_common<>>() == 0);
|
||||||
REQUIRE(magic_enum::detail::min_v<BadColor, as_common<>> == 0);
|
REQUIRE(magic_enum::detail::min_v<BadColor, as_common<>> == 0);
|
||||||
|
|
||||||
|
REQUIRE(magic_enum::customize::enum_range<Dimension>::min == 1000);
|
||||||
|
REQUIRE(magic_enum::customize::enum_range<Dimension>::max == 1000 + 128);
|
||||||
|
REQUIRE_FALSE(magic_enum::customize::enum_range<Dimension>::is_flags);
|
||||||
|
|
||||||
REQUIRE(magic_enum::customize::enum_range<Color>::min == MAGIC_ENUM_RANGE_MIN);
|
REQUIRE(magic_enum::customize::enum_range<Color>::min == MAGIC_ENUM_RANGE_MIN);
|
||||||
REQUIRE(magic_enum::detail::reflected_min<Color, as_common<>>() == MAGIC_ENUM_RANGE_MIN);
|
REQUIRE(magic_enum::detail::reflected_min<Color, as_common<>>() == MAGIC_ENUM_RANGE_MIN);
|
||||||
REQUIRE(magic_enum::detail::min_v<Color, as_common<>> == -12);
|
REQUIRE(magic_enum::detail::min_v<Color, as_common<>> == -12);
|
||||||
|
|
|
||||||
|
|
@ -49,17 +49,17 @@ struct magic_enum::customize::enum_range<Color> {
|
||||||
static constexpr bool is_flags = true;
|
static constexpr bool is_flags = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Numbers : int {
|
namespace Namespace {
|
||||||
none = 0,
|
enum class Numbers : int {
|
||||||
one = 1 << 1,
|
none = 0,
|
||||||
two = 1 << 2,
|
one = 1 << 1,
|
||||||
three = 1 << 3,
|
two = 1 << 2,
|
||||||
many = 1 << 30,
|
three = 1 << 3,
|
||||||
};
|
many = 1 << 30,
|
||||||
template <>
|
};
|
||||||
struct magic_enum::customize::enum_range<Numbers> {
|
magic_enum::customize::adl_info<true> adl_magic_enum_define_range(Numbers);
|
||||||
static constexpr bool is_flags = true;
|
}
|
||||||
};
|
using Namespace::Numbers;
|
||||||
|
|
||||||
enum Directions : std::uint64_t {
|
enum Directions : std::uint64_t {
|
||||||
NoDirection = 0,
|
NoDirection = 0,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue