1
0
Fork 0
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:
ZXShady 2025-06-12 02:54:42 +02:00
parent a733a2ea66
commit 4110b46847
5 changed files with 180 additions and 110 deletions

View file

@ -23,29 +23,23 @@
* 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>` * 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 { ... };
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 ```cpp
namespace Deeply::Nested::Namespace { 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 // - magic_enum will find this function by ADL
// - no need to ever define this function // - uses builder pattern
magic_enum::customize::adl_info<true> adl_magic_enum_define_range(my_enum_type); // - 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 ## 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.

View file

@ -8,6 +8,7 @@
* [`enum_name` returns name from enum value.](#enum_name) * [`enum_name` returns name from enum value.](#enum_name)
* [`enum_names` obtains string enum name sequence.](#enum_names) * [`enum_names` obtains string enum name sequence.](#enum_names)
* [`enum_entries` obtains pair (value enum, string enum name) sequence.](#enum_entries) * [`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_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_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) * [`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 #define MAGIC_ENUM_RANGE_MAX 255
``` ```
## `enum_cast` ## `enum_cast`
```cpp ```cpp
@ -276,6 +278,62 @@ constexpr array<pair<E, string_view>, N> enum_entries() noexcept;
// color_entries[0].second -> "RED" // 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` ## `enum_index`
```cpp ```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" 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>` * 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 { ```cpp
enum class my_enum_type { ... }; namespace Deeply::Nested::Namespace {
struct my_adl_info_struct { enum class my_enum_type { my_enum_value1,my_enum_value2 };
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 // - magic_enum will find this function by ADL
// - no need to ever define this function // - uses builder pattern
magic_enum::customize::adl_info<true> adl_magic_enum_define_range(my_enum_type); // - 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` ## `is_unscoped_enum`

View file

@ -163,58 +163,55 @@ 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 customize {
template <typename E, typename = void>
struct enum_range;
}
namespace detail { namespace detail {
template<typename E, typename = void> template<typename E,typename = void>
constexpr inline bool has_is_flags_adl = false; constexpr inline std::size_t prefix_length_or_zero = 0;
template<typename E> 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; 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 };
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> template<bool IsFlags = false,int Min = MAGIC_ENUM_RANGE_MIN,int Max = MAGIC_ENUM_RANGE_MAX,std::size_t PrefixLength = 0>
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"); }; struct adl_info_holder {
constexpr static int max = Max;
template<int Min, int Max> constexpr static int min = Min;
struct adl_info<Min, Max> { constexpr static bool is_flags =IsFlags;
static constexpr int min = Min; constexpr static std::size_t prefix_length = PrefixLength;
static constexpr int max = Max;
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> adl_info_holder<> adl_info()
struct adl_info<IsFlags> { {
static constexpr bool is_flags = IsFlags; 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. // 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, typename = void> 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> template <typename E>
struct enum_range < E, decltype(void(adl_magic_enum_define_range(E{}))) > { struct enum_range<E, decltype(void(adl_magic_enum_define_range(E{}))) >
static constexpr int min = detail::has_minmax_adl<E>.first; : decltype(adl_magic_enum_define_range(E{})) {};
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.");
@ -302,6 +299,9 @@ class static_str {
MAGIC_ENUM_ASSERT(str.size_ == N); 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>{}} { 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); MAGIC_ENUM_ASSERT(str.size() == N);
} }
@ -650,7 +650,7 @@ constexpr auto enum_name() noexcept {
#else #else
constexpr auto name = n<V>(); constexpr auto name = n<V>();
#endif #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 { } else {
static_assert(always_false_v<E>, "magic_enum::customize invalid."); static_assert(always_false_v<E>, "magic_enum::customize invalid.");
} }

View file

@ -105,23 +105,37 @@ namespace We::Need::To::Go::Deeper {
auto adl_magic_enum_define_range(Dimension) auto adl_magic_enum_define_range(Dimension)
{ {
enum { return magic_enum::customize::adl_info().minmax<1000,1000+128>();
min = 1000,
max = 1000 + 128
} e{};
return e;
} }
struct FlaggyData {
static constexpr bool is_flags = true;
};
// not defined! // 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::Dimension;
using We::Need::To::Go::Deeper::Flaggy; 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 }; enum class BoolTest : bool { Yay, Nay };
using namespace magic_enum; 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(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()); 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"); 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);

View file

@ -57,7 +57,10 @@ namespace Namespace {
three = 1 << 3, three = 1 << 3,
many = 1 << 30, 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; using Namespace::Numbers;
@ -91,6 +94,24 @@ struct magic_enum::customize::enum_range<number> {
static constexpr bool is_flags = true; 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.hpp>
#include <magic_enum/magic_enum_fuse.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&>("GREEN|RED|None").has_value());
REQUIRE_FALSE(enum_flags_cast<Color>("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"); 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);
@ -504,6 +532,8 @@ TEST_CASE("enum_flags_name") {
REQUIRE(enum_flags_name(number::four) == "four"); REQUIRE(enum_flags_name(number::four) == "four");
REQUIRE(nto_name == "one|three"); REQUIRE(nto_name == "one|three");
REQUIRE(enum_flags_name(static_cast<number>(0)).empty()); 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") { TEST_CASE("enum_names") {