mirror of
https://github.com/Neargye/magic_enum.git
synced 2026-01-09 23:34:23 +00:00
Add prefix trimming
This commit is contained in:
parent
a733a2ea66
commit
4110b46847
5 changed files with 180 additions and 110 deletions
|
|
@ -23,29 +23,23 @@
|
|||
|
||||
* 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);
|
||||
}
|
||||
```
|
||||
* Or, for enum types that are deeply nested in classes and/or namespaces, declare a function called `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 `magic_enum::customize::adl_info`.
|
||||
|
||||
* 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 { ... };
|
||||
enum class my_enum_type { my_enum_value1,my_enum_value2 };
|
||||
|
||||
// - 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);
|
||||
// - uses builder pattern
|
||||
// - use auto to not have to name the type yourself
|
||||
auto adl_magic_enum_define_range(my_enum_type)
|
||||
{
|
||||
return magic_enum::customize::adl_info()
|
||||
.minmax<10,10>() // the min max search range
|
||||
.flag<true>() // whether it is a flag enum
|
||||
.prefix<sizeof("my_enum_")-1>(); // how many characters to trim from the start of each enum entry.
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -78,32 +72,6 @@
|
|||
};
|
||||
```
|
||||
|
||||
* 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
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
* [`enum_name` returns name from enum value.](#enum_name)
|
||||
* [`enum_names` obtains string enum name sequence.](#enum_names)
|
||||
* [`enum_entries` obtains pair (value enum, string enum name) sequence.](#enum_entries)
|
||||
* [`customize::enum_range`](#customizeenum_range)
|
||||
* [`enum_index` obtains index in enum value sequence from enum value.](#enum_index)
|
||||
* [`enum_contains` checks whether enum contains enumerator with such value.](#enum_contains)
|
||||
* [`enum_reflected` returns true if the enum value is in the range of values that can be reflected..](#enum_reflected)
|
||||
|
|
@ -63,6 +64,7 @@
|
|||
#define MAGIC_ENUM_RANGE_MAX 255
|
||||
```
|
||||
|
||||
|
||||
## `enum_cast`
|
||||
|
||||
```cpp
|
||||
|
|
@ -276,6 +278,62 @@ constexpr array<pair<E, string_view>, N> enum_entries() noexcept;
|
|||
// color_entries[0].second -> "RED"
|
||||
```
|
||||
|
||||
## `customize::enum_range`
|
||||
|
||||
```cpp
|
||||
namespace customize {
|
||||
template <typename E,typename = void>
|
||||
struct enum_range {
|
||||
constexpr static std::size_t prefix_length = 0;
|
||||
constexpr static bool is_flags = false;
|
||||
constexpr static int min = MAGIC_ENUM_MIN_RANGE;
|
||||
constexpr static int max = MAGIC_ENUM_MAX_RANGE;
|
||||
};
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
* Defined in header `<magic_enum/magic_enum.hpp>`
|
||||
|
||||
* A customization point for controlling `magic_enum` defaults
|
||||
|
||||
* It has a defaulted second `void` typename template parameter for SFINAE.
|
||||
|
||||
* `is_flags` tells `magic_enum` whether this enum should be considered to be a bitflag enum. It is not required to be defined if not defined it will be assumed to be `false`
|
||||
|
||||
* `prefix_length` tells `magic_enum` how many characters to remove from the start of the names for all string functions. It is not required to be defined if not defined it will be assumed to be `0`
|
||||
|
||||
* `min` and `max` are not required to be defined if `is_flags` is defined because they are ignored for enum flags.
|
||||
otherwise they are required.
|
||||
|
||||
* Examples
|
||||
|
||||
* Controlling prefix length
|
||||
|
||||
```cpp
|
||||
enum CStyleAnimals {
|
||||
CStyleAnimals_Giraffe,
|
||||
CStyleAnimals_Elephant,
|
||||
CStyleAnimals_Lion,
|
||||
};
|
||||
|
||||
template<>
|
||||
struct magic_enum::customize::enum_range<CStyleAnimals> {
|
||||
// sizeof counts null terminator subtract 1 to get length
|
||||
constexpr static auto prefix_length = sizeof("CStyleAnimals_")-1;
|
||||
constexpr static int min = CStyleAnimals_Giraffe; // required
|
||||
constexpr static int max = CStyleAnimals_Lion; // required
|
||||
};
|
||||
|
||||
CStyleAnimals animal = CStyleAnimals_Giraffe;
|
||||
auto animal_name = magic_enum::enum_name(animal);
|
||||
// animal_name => "Giraffe"
|
||||
auto animal_from_string = magic_enum::enum_cast<CStyleAnimals>(animal_name);
|
||||
// animal_from_string.value() == CStyleAnimals_Giraffe
|
||||
```
|
||||
|
||||
|
||||
|
||||
## `enum_index`
|
||||
|
||||
```cpp
|
||||
|
|
@ -512,31 +570,24 @@ constexpr bool enum_flags_contains(string_view value, BinaryPredicate p) noexcep
|
|||
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);
|
||||
}
|
||||
```
|
||||
* Or, for enum types that are deeply nested in classes and/or namespaces, declare a function called `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 `magic_enum::customize::adl_info`.
|
||||
|
||||
```cpp
|
||||
namespace Deeply::Nested::Namespace {
|
||||
enum class my_enum_type { my_enum_value1,my_enum_value2 };
|
||||
|
||||
* 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);
|
||||
// - uses builder pattern
|
||||
// - use auto to not have to name the type yourself
|
||||
auto adl_magic_enum_define_range(my_enum_type)
|
||||
{
|
||||
return magic_enum::customize::adl_info()
|
||||
.minmax<10,10>() // the min max search range
|
||||
.flag<true>() // whether it is a flag enum
|
||||
.prefix<sizeof("my_enum_")-1>(); // how many characters to trim from the start of each enum entry.
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
## `is_unscoped_enum`
|
||||
|
||||
|
|
|
|||
|
|
@ -163,58 +163,55 @@ static_assert([] {
|
|||
return true;
|
||||
} (), "magic_enum::customize wchar_t is not compatible with ASCII.");
|
||||
|
||||
namespace customize {
|
||||
template <typename E, typename = void>
|
||||
struct enum_range;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename E, typename = void>
|
||||
constexpr inline bool has_is_flags_adl = false;
|
||||
template<typename E,typename = void>
|
||||
constexpr inline std::size_t prefix_length_or_zero = 0;
|
||||
|
||||
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);
|
||||
constexpr inline auto prefix_length_or_zero<E, std::void_t<decltype(customize::enum_range<E>::prefix_length)>> = std::size_t{ customize::enum_range<E>::prefix_length };
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 = false,int Min = MAGIC_ENUM_RANGE_MIN,int Max = MAGIC_ENUM_RANGE_MAX,std::size_t PrefixLength = 0>
|
||||
struct adl_info_holder {
|
||||
constexpr static int max = Max;
|
||||
constexpr static int min = Min;
|
||||
constexpr static bool is_flags =IsFlags;
|
||||
constexpr static std::size_t prefix_length = PrefixLength;
|
||||
|
||||
template<int min,int max>
|
||||
constexpr static adl_info_holder<IsFlags,min,max,PrefixLength> minmax() { return {};}
|
||||
template<bool is_flag>
|
||||
constexpr static adl_info_holder<is_flag,Min,Max,PrefixLength> flag() { return {};}
|
||||
template<std::size_t prefix_len>
|
||||
constexpr static adl_info_holder<IsFlags,Min,Max,prefix_len> prefix() { return {};}
|
||||
};
|
||||
|
||||
template<bool IsFlags>
|
||||
struct adl_info<IsFlags> {
|
||||
static constexpr bool is_flags = IsFlags;
|
||||
};
|
||||
adl_info_holder<> adl_info()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// 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 specific enum type, add specialization enum_range for necessary enum type.
|
||||
template <typename E, typename = void>
|
||||
template <typename E,typename /*= void*/>
|
||||
struct enum_range {
|
||||
static constexpr int min = MAGIC_ENUM_RANGE_MIN;
|
||||
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.");
|
||||
};
|
||||
struct enum_range<E, decltype(void(adl_magic_enum_define_range(E{}))) >
|
||||
: decltype(adl_magic_enum_define_range(E{})) {};
|
||||
|
||||
static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN.");
|
||||
|
||||
|
|
@ -302,6 +299,9 @@ class static_str {
|
|||
MAGIC_ENUM_ASSERT(str.size_ == N);
|
||||
}
|
||||
|
||||
constexpr explicit static_str(const char* const str) noexcept : static_str{ str, std::make_integer_sequence<std::uint16_t, N>{} } {
|
||||
}
|
||||
|
||||
constexpr explicit static_str(string_view str) noexcept : static_str{str.data(), std::make_integer_sequence<std::uint16_t, N>{}} {
|
||||
MAGIC_ENUM_ASSERT(str.size() == N);
|
||||
}
|
||||
|
|
@ -650,7 +650,7 @@ constexpr auto enum_name() noexcept {
|
|||
#else
|
||||
constexpr auto name = n<V>();
|
||||
#endif
|
||||
return static_str<name.size_>{name};
|
||||
return static_str<name.size_ - prefix_length_or_zero<E>>{name.str_ + prefix_length_or_zero<E>};
|
||||
} else {
|
||||
static_assert(always_false_v<E>, "magic_enum::customize invalid.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,23 +105,37 @@ namespace We::Need::To::Go::Deeper {
|
|||
|
||||
auto adl_magic_enum_define_range(Dimension)
|
||||
{
|
||||
enum {
|
||||
min = 1000,
|
||||
max = 1000 + 128
|
||||
} e{};
|
||||
return e;
|
||||
return magic_enum::customize::adl_info().minmax<1000,1000+128>();
|
||||
}
|
||||
|
||||
struct FlaggyData {
|
||||
static constexpr bool is_flags = true;
|
||||
};
|
||||
|
||||
// not defined!
|
||||
FlaggyData adl_magic_enum_define_range(Flaggy);
|
||||
auto adl_magic_enum_define_range(Flaggy)
|
||||
{
|
||||
return magic_enum::customize::adl_info().flag<true>();
|
||||
}
|
||||
}
|
||||
using We::Need::To::Go::Deeper::Dimension;
|
||||
using We::Need::To::Go::Deeper::Flaggy;
|
||||
|
||||
enum CStyleEnum {
|
||||
CStyleEnum_A = -36,
|
||||
CStyleEnum_B,
|
||||
CStyleEnum_C,
|
||||
CStyleEnum_D,
|
||||
CStyleEnum_F,
|
||||
CStyleEnum_G,
|
||||
CStyleEnum_H = 36
|
||||
};
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<CStyleEnum> {
|
||||
static constexpr auto prefix_length = sizeof("CStyleEnum_") - 1;
|
||||
static constexpr int min = -100;
|
||||
static constexpr int max = 100;
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum class BoolTest : bool { Yay, Nay };
|
||||
|
||||
using namespace magic_enum;
|
||||
|
|
@ -142,6 +156,13 @@ TEST_CASE("enum_cast") {
|
|||
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 cstyle = enum_cast<CStyleEnum>("A");
|
||||
REQUIRE(cstyle.value() == CStyleEnum_A);
|
||||
REQUIRE(enum_cast<const CStyleEnum&>("H").value() == CStyleEnum_H);
|
||||
REQUIRE_FALSE(enum_cast<CStyleEnum>("CStyleEnum_H").has_value());
|
||||
REQUIRE(enum_cast<CStyleEnum>("d", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }) == CStyleEnum_D);
|
||||
REQUIRE_FALSE(enum_cast<CStyleEnum>("Q").has_value());
|
||||
|
||||
constexpr auto no = enum_cast<Numbers>("one");
|
||||
REQUIRE(no.value() == Numbers::one);
|
||||
REQUIRE(enum_cast<Numbers>("two").value() == Numbers::two);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,10 @@ namespace Namespace {
|
|||
three = 1 << 3,
|
||||
many = 1 << 30,
|
||||
};
|
||||
magic_enum::customize::adl_info<true> adl_magic_enum_define_range(Numbers);
|
||||
auto adl_magic_enum_define_range(Numbers)
|
||||
{
|
||||
return magic_enum::customize::adl_info().flag<true>();
|
||||
}
|
||||
}
|
||||
using Namespace::Numbers;
|
||||
|
||||
|
|
@ -91,6 +94,24 @@ struct magic_enum::customize::enum_range<number> {
|
|||
static constexpr bool is_flags = true;
|
||||
};
|
||||
|
||||
enum CStyleFlags {
|
||||
CStyleFlags_A = 1 << 0,
|
||||
CStyleFlags_B = 1 << 1,
|
||||
CStyleFlags_C = 1 << 2,
|
||||
CStyleFlags_D = 1 << 3,
|
||||
CStyleFlags_E = 1 << 4,
|
||||
CStyleFlags_F = 1 << 5,
|
||||
CStyleFlags_G = 1 << 6,
|
||||
CStyleFlags_H = 1 << 7,
|
||||
CStyleFlags_I = 1 << 8,
|
||||
};
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<CStyleFlags> {
|
||||
static constexpr bool is_flags = true;
|
||||
static constexpr auto prefix_length = sizeof("CStyleFlags_")-1;
|
||||
};
|
||||
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum_fuse.hpp>
|
||||
|
||||
|
|
@ -117,6 +138,13 @@ TEST_CASE("enum_cast") {
|
|||
REQUIRE_FALSE(enum_flags_cast<Color&>("GREEN|RED|None").has_value());
|
||||
REQUIRE_FALSE(enum_flags_cast<Color>("None").has_value());
|
||||
|
||||
REQUIRE(enum_flags_cast<CStyleFlags>("A|B|C|D").value() == (CStyleFlags_A | CStyleFlags_B | CStyleFlags_C | CStyleFlags_D));
|
||||
REQUIRE(enum_flags_cast<CStyleFlags>("a|e|f", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (CStyleFlags_A | CStyleFlags_E | CStyleFlags_F));
|
||||
REQUIRE_FALSE(enum_flags_cast<CStyleFlags>("blue|E|F|C", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).has_value());
|
||||
REQUIRE(enum_flags_cast<CStyleFlags>("H|I|F|F|F").value() == (CStyleFlags_H | CStyleFlags_I | CStyleFlags_F));
|
||||
REQUIRE(enum_flags_cast<CStyleFlags>("E|B|C|A").value() == (CStyleFlags_A | CStyleFlags_B | CStyleFlags_C | CStyleFlags_E));
|
||||
|
||||
|
||||
constexpr auto no = enum_cast<Numbers>("one");
|
||||
REQUIRE(no.value() == Numbers::one);
|
||||
REQUIRE(enum_cast<Numbers>("two").value() == Numbers::two);
|
||||
|
|
@ -504,6 +532,8 @@ TEST_CASE("enum_flags_name") {
|
|||
REQUIRE(enum_flags_name(number::four) == "four");
|
||||
REQUIRE(nto_name == "one|three");
|
||||
REQUIRE(enum_flags_name(static_cast<number>(0)).empty());
|
||||
|
||||
REQUIRE(enum_flags_name(CStyleFlags_A | CStyleFlags_B | CStyleFlags_C | CStyleFlags_D) == "A|B|C|D");
|
||||
}
|
||||
|
||||
TEST_CASE("enum_names") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue