1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-09 23:34:23 +00:00
This commit is contained in:
neargye 2022-11-07 21:00:16 +04:00
parent 596f00c0db
commit 8bd403f888
10 changed files with 502 additions and 298 deletions

View file

@ -6,7 +6,7 @@ package(default_visibility = ["//visibility:public"])
cc_library( cc_library(
name = "magic_enum", name = "magic_enum",
hdrs = ["include/magic_enum.hpp", "include/magic_enum_fuse.hpp"], hdrs = ["include/magic_enum.hpp", "include/magic_enum_format.hpp", "include/magic_enum_fuse.hpp", "include/magic_enum_switch.hpp"],
includes = ["include"], includes = ["include"],
) )

View file

@ -154,7 +154,6 @@ Header-only C++17 library provides static reflection for enums, work with any en
* Enum switch runtime value as constexpr constant * Enum switch runtime value as constexpr constant
```cpp ```cpp
Color color = Color::RED; Color color = Color::RED;
magic_enum::enum_switch([] (auto val) { magic_enum::enum_switch([] (auto val) {
constexpr Color c_color = val; constexpr Color c_color = val;
// ... // ...

View file

@ -6,7 +6,6 @@
* [`enum_count` returns number of enum values.](#enum_count) * [`enum_count` returns number of enum values.](#enum_count)
* [`enum_integer` obtains integer value from enum value.](#enum_integer) * [`enum_integer` obtains integer value from enum value.](#enum_integer)
* [`enum_name` returns name from enum value.](#enum_name) * [`enum_name` returns name from enum value.](#enum_name)
* [`enum_flags_name` returns name from enum-flags value.](#enum_flags_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)
* [`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)
@ -15,6 +14,7 @@
* [`enum_fuse` returns a bijective mix of enum values.](#enum_fuse) * [`enum_fuse` returns a bijective mix of enum values.](#enum_fuse)
* [`enum_switch` allows runtime enum value transformation to constexpr context.](#enum_switch) * [`enum_switch` allows runtime enum value transformation to constexpr context.](#enum_switch)
* [`enum_for_each` calls a function with all enum constexpr value.](#enum_for_each) * [`enum_for_each` calls a function with all enum constexpr value.](#enum_for_each)
* [`enum_flags` API from enum-flags.](#enum_flags)
* [`is_unscoped_enum` checks whether type is an Unscoped enumeration.](#is_unscoped_enum) * [`is_unscoped_enum` checks whether type is an Unscoped enumeration.](#is_unscoped_enum)
* [`is_scoped_enum` checks whether type is an Scoped enumeration.](#is_scoped_enum) * [`is_scoped_enum` checks whether type is an Scoped enumeration.](#is_scoped_enum)
* [`underlying_type` improved UB-free "SFINAE-friendly" underlying_type.](#underlying_type) * [`underlying_type` improved UB-free "SFINAE-friendly" underlying_type.](#underlying_type)
@ -219,25 +219,6 @@ constexpr string_view enum_name() noexcept;
// color_name -> "BLUE" // color_name -> "BLUE"
``` ```
## `enum_flags_name`
```cpp
template <typename E>
string enum_flags_name(E value);
```
* Returns name from enum-flags value as `string` with null-terminated string.
* If enum-flags value does not have name or [out of range](limitations.md), returns empty string.
* Examples
```cpp
auto directions_name = magic_enum::enum_flags_name(Directions::Up | Directions::Right);
// directions_name -> "Directions::Up | Directions::Right"
```
## `enum_names` ## `enum_names`
```cpp ```cpp
@ -383,20 +364,21 @@ constexpr Result enum_switch(Lambda&& lambda, E value);
template <typename Result, typename E, typename Lambda> template <typename Result, typename E, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, E value, Result&& result); constexpr Result enum_switch(Lambda&& lambda, E value, Result&& result);
template <typename E, typename Result = void, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {});
template <typename E, typename Result, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {});
template <typename E, typename Result = void, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, underlying_type_t<E> value);
template <typename E, typename Result, typename Lambda>
constexpr Result enum_switch(Lambda&& lambda, underlying_type_t<E> value, Result&& result);
``` ```
* You should add the required file `<magic_enum_switch.hpp>`.
* Examples
```cpp
Color color = Color::RED;
magic_enum::enum_switch([] (auto val) {
constexpr Color c_color = val;
// ...
}, color);
```
## `enum_for_each` ## `enum_for_each`
```cpp ```cpp
@ -404,6 +386,51 @@ template <typename E, typename Lambda>
constexpr auto enum_for_each(Lambda&& lambda); constexpr auto enum_for_each(Lambda&& lambda);
``` ```
* Examples
```cpp
magic_enum::enum_for_each<Color>([] (auto val) {
constexpr Color c_color = val;
// ...
});
```
## `enum_flags`
```cpp
template <typename E>
string enum_flags_name(E value);
template <typename E>
constexpr optional<E> enum_flags_cast(underlying_type_t<E> value) noexcept;
template <typename E>
constexpr optional<E> enum_flags_cast(string_view value) noexcept;
template <typename E, typename BinaryPredicate>
constexpr optional<E> enum_flags_cast(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v<BinaryPredicate>);
template <typename E>
constexpr bool enum_flags_contains(E value) noexcept;
template <typename E>
constexpr bool enum_flags_contains(underlying_type_t<E> value) noexcept;
template <typename E>
constexpr bool enum_flags_contains(string_view value) noexcept;
template <typename E, typename BinaryPredicate>
constexpr optional<E> enum_flags_contains(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v<BinaryPredicate>);
```
* Examples
```cpp
auto directions_name = magic_enum::enum_flags_name(Directions::Up | Directions::Right);
// directions_name -> "Directions::Up | Directions::Right"
```
## `is_unscoped_enum` ## `is_unscoped_enum`
```cpp ```cpp

View file

@ -22,8 +22,8 @@
// SOFTWARE. // SOFTWARE.
#include <iostream> #include <iostream>
#define MAGIC_ENUM_ENABLE_HASH
#include <magic_enum.hpp> #include <magic_enum_switch.hpp>
enum class Color { RED, BLUE, GREEN }; enum class Color { RED, BLUE, GREEN };
@ -64,9 +64,9 @@ int main() {
} }
}; };
magic_enum::enum_switch<Color>(switcher1, 2 /* GREEN */); // prints nothing magic_enum::enum_switch(switcher1, Color::GREEN); // prints nothing
magic_enum::enum_switch<Color>(switcher1, 1 /* BLUE */); // prints "Blue" magic_enum::enum_switch(switcher1, Color::BLUE); // prints "Blue"
magic_enum::enum_switch<Color>(switcher1, 0 /* RED */); // prints "Red" magic_enum::enum_switch(switcher1, Color::RED); // prints "Red"
// explicit result type // explicit result type
auto switcher2 = overloaded{ auto switcher2 = overloaded{
@ -86,23 +86,18 @@ int main() {
assert(empty.empty()); assert(empty.empty());
// result with default object // result with default object
std::cout << magic_enum::enum_switch<Color, std::string>(switcher2, -3, "unrecognized") << std::endl; // prints "unrecognized" std::cout << magic_enum::enum_switch<std::string>(switcher2, static_cast<Color>(-3), "unrecognized") << std::endl; // prints "unrecognized"
auto switcher3 = overloaded{ auto switcher3 = overloaded{
[] (magic_enum::enum_constant<Color::RED>) { [] (magic_enum::enum_constant<Color::RED>) -> std::optional<std::string> {
return "red result"; return "red result";
}, },
[] (magic_enum::enum_constant<Color::BLUE>) { [] (magic_enum::enum_constant<Color::BLUE>) -> std::optional<std::string> {
return std::nullopt; return std::nullopt;
} }
}; };
std::cout << std::boolalpha; std::cout << std::boolalpha;
std::cout << magic_enum::enum_switch<Color>(switcher3, "GREEN", std::make_optional("cica")).value() << std::endl; // prints default: "cica"
std::cout << magic_enum::enum_switch<Color>(switcher3, "RED", std::make_optional("cica")).value() << std::endl; // prints: "red result"
std::cout << magic_enum::enum_switch<Color>(switcher3, "BLUE", std::make_optional("cica")).has_value() << std::endl; // prints: false
std::cout << magic_enum::enum_switch<Color>(switcher3, "red", std::make_optional("cica"), magic_enum::case_insensitive).value() << std::endl; // prints: "red result"
std::cout << magic_enum::enum_switch(switcher3, Color::GREEN, std::make_optional("cica")).value() << std::endl; // prints default: "cica" std::cout << magic_enum::enum_switch(switcher3, Color::GREEN, std::make_optional("cica")).value() << std::endl; // prints default: "cica"
std::cout << magic_enum::enum_switch(switcher3, Color::RED, std::make_optional("cica")).value() << std::endl; // prints: "red result" std::cout << magic_enum::enum_switch(switcher3, Color::RED, std::make_optional("cica")).value() << std::endl; // prints: "red result"
std::cout << magic_enum::enum_switch(switcher3, Color::BLUE, std::make_optional("cica")).has_value() << std::endl; // prints: false std::cout << magic_enum::enum_switch(switcher3, Color::BLUE, std::make_optional("cica")).has_value() << std::endl; // prints: false

View file

@ -171,6 +171,11 @@ namespace detail {
template <auto V, typename E = std::decay_t<decltype(V)>, std::enable_if_t<std::is_enum_v<E>, int> = 0> template <auto V, typename E = std::decay_t<decltype(V)>, std::enable_if_t<std::is_enum_v<E>, int> = 0>
using enum_constant = std::integral_constant<E, V>; using enum_constant = std::integral_constant<E, V>;
enum class value_type {
default_value,
flags_value
};
template <typename... T> template <typename... T>
inline constexpr bool always_false_v = false; inline constexpr bool always_false_v = false;
@ -695,8 +700,14 @@ struct enable_if_enum<true, R> {
static_assert(supported<R>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); static_assert(supported<R>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
}; };
template <typename T, typename R, typename BinaryPredicate = std::equal_to<>> template <typename T, typename R, typename BinaryPredicate = std::equal_to<>, typename D = std::decay_t<T>>
using enable_if_t = typename enable_if_enum<std::is_enum_v<std::decay_t<T>> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type; using enable_if_t = typename enable_if_enum<std::is_enum_v<D> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
template <typename T, typename R, value_type VT, typename BinaryPredicate = std::equal_to<>, typename D = std::decay_t<T>>
using enable_if_default_t = typename enable_if_enum<std::is_enum_v<D> && VT == value_type::default_value && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
template <typename T, typename R, value_type VT, typename BinaryPredicate = std::equal_to<>, typename D = std::decay_t<T>>
using enable_if_flags_t = typename enable_if_enum<std::is_enum_v<D> && VT == value_type::flags_value && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
template <typename T, std::enable_if_t<std::is_enum_v<std::decay_t<T>>, int> = 0> template <typename T, std::enable_if_t<std::is_enum_v<std::decay_t<T>>, int> = 0>
using enum_concept = T; using enum_concept = T;
@ -719,10 +730,7 @@ struct underlying_type {};
template <typename T> template <typename T>
struct underlying_type<T, true> : std::underlying_type<std::decay_t<T>> {}; struct underlying_type<T, true> : std::underlying_type<std::decay_t<T>> {};
#if defined(MAGIC_ENUM_NO_HASH) #if defined(MAGIC_ENUM_ENABLE_HASH)
template <typename T>
inline constexpr bool has_hash = false;
#else
template <typename T> template <typename T>
inline constexpr bool has_hash = true; inline constexpr bool has_hash = true;
@ -846,7 +854,8 @@ constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r
} }
enum class case_call_t { enum class case_call_t {
index, value index,
value
}; };
template <typename T = void> template <typename T = void>
@ -856,7 +865,7 @@ template <>
inline constexpr auto default_result_type_lambda<void> = []() noexcept {}; inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
template <auto* Arr, typename Hash> template <auto* Arr, typename Hash>
constexpr bool no_duplicate() noexcept { constexpr bool has_duplicate() noexcept {
using value_t = std::decay_t<decltype((*Arr)[0])>; using value_t = std::decay_t<decltype((*Arr)[0])>;
using hash_value_t = std::invoke_result_t<Hash, value_t>; using hash_value_t = std::invoke_result_t<Hash, value_t>;
std::array<hash_value_t, Arr->size()> hashes{}; std::array<hash_value_t, Arr->size()> hashes{};
@ -887,41 +896,43 @@ constexpr bool no_duplicate() noexcept {
T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \
T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255)
#define MAGIC_ENUM_CASE(val) \ #define MAGIC_ENUM_CASE(val) \
case cases[val]: \ case cases[val]: \
if constexpr ((val) + Page < size) { \ if constexpr ((val) + Page < size) { \
if (!pred(values[val + Page], searched)) { \ if (!pred(values[val + Page], searched)) { \
break; \ break; \
} \ } \
if constexpr (CallValue == case_call_t::index) { \ if constexpr (CallValue == case_call_t::index) { \
if constexpr (std::is_invocable_r_v<result_t, Lambda, std::integral_constant<std::size_t, val + Page>>) { \ if constexpr (std::is_invocable_r_v<result_t, Lambda, std::integral_constant<std::size_t, val + Page>>) { \
return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), std::integral_constant<std::size_t, val + Page>{}); \ return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), std::integral_constant<std::size_t, val + Page>{}); \
} else if constexpr (std::is_invocable_v<Lambda, std::integral_constant<std::size_t, val + Page>>) { \ } else if constexpr (std::is_invocable_v<Lambda, std::integral_constant<std::size_t, val + Page>>) { \
assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \
} \ } \
} else if constexpr (CallValue == case_call_t::value) { \ } else if constexpr (CallValue == case_call_t::value) { \
if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \ if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \
return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), enum_constant<values[val + Page]>{}); \ return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), enum_constant<values[val + Page]>{}); \
} else if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \ } else if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \
assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \
} \ } \
} \ } \
break; \ break; \
} else [[fallthrough]]; } else [[fallthrough]];
template <auto* GlobValues, template <auto* GlobValues,
case_call_t CallValue, case_call_t CallValue,
std::size_t Page = 0, std::size_t Page = 0,
typename Hash = constexpr_hash_t<typename std::decay_t<decltype(*GlobValues)>::value_type>, typename Hash = constexpr_hash_t<typename std::decay_t<decltype(*GlobValues)>::value_type>,
typename Lambda, typename ResultGetterType = decltype(default_result_type_lambda<>), typename BinaryPredicate = std::equal_to<>,
typename BinaryPredicate = std::equal_to<>> typename Lambda,
typename ResultGetterType>
constexpr std::invoke_result_t<ResultGetterType> constexpr_switch( constexpr std::invoke_result_t<ResultGetterType> constexpr_switch(
Lambda&& lambda, Lambda&& lambda,
typename std::decay_t<decltype(*GlobValues)>::value_type searched, typename std::decay_t<decltype(*GlobValues)>::value_type searched,
ResultGetterType&& def = default_result_type_lambda<>, ResultGetterType&& def,
BinaryPredicate&& pred = {}) { BinaryPredicate&& pred = {}) {
using result_t = std::invoke_result_t<ResultGetterType>; using result_t = std::invoke_result_t<ResultGetterType>;
using hash_t = std::conditional_t<no_duplicate<GlobValues, Hash>(), Hash, typename Hash::secondary_hash>; using hash_t = std::conditional_t<has_duplicate<GlobValues, Hash>(), Hash, typename Hash::secondary_hash>;
static_assert(has_duplicate<GlobValues, hash_t>(), "magic_enum::detail::constexpr_switch duplicated hash found, please report it: https://github.com/Neargye/magic_enum/issues.");
constexpr std::array values = *GlobValues; constexpr std::array values = *GlobValues;
constexpr std::size_t size = values.size(); constexpr std::size_t size = values.size();
constexpr std::array cases = calculate_cases<GlobValues, hash_t>(Page); constexpr std::array cases = calculate_cases<GlobValues, hash_t>(Page);
@ -939,28 +950,36 @@ constexpr std::invoke_result_t<ResultGetterType> constexpr_switch(
#undef MAGIC_ENUM_FOR_EACH_256 #undef MAGIC_ENUM_FOR_EACH_256
#undef MAGIC_ENUM_CASE #undef MAGIC_ENUM_CASE
#else
template <typename T>
inline constexpr bool has_hash = false;
#endif #endif
template <typename E, typename Lambda, std::size_t... I> template <typename E, typename F, std::size_t... I>
constexpr auto for_each(Lambda&& lambda, std::index_sequence<I...>) { constexpr auto for_each(F&& f, std::index_sequence<I...>) {
static_assert(is_enum_v<E>, "magic_enum::detail::for_each requires enum type."); static_assert(is_enum_v<E>, "magic_enum::detail::for_each requires enum type.");
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> || ...); constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<F, enum_constant<values_v<E>[I]>>> || ...);
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[0]>>, std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> && ...); constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<F, enum_constant<values_v<E>[0]>>, std::invoke_result_t<F, enum_constant<values_v<E>[I]>>> && ...);
if constexpr (has_void_return) { if constexpr (has_void_return) {
(lambda(enum_constant<values_v<E>[I]>{}), ...); (f(enum_constant<values_v<E>[I]>{}), ...);
} else if constexpr (all_same_return) { } else if constexpr (all_same_return) {
return std::array{lambda(enum_constant<values_v<E>[I]>{})...}; return std::array{f(enum_constant<values_v<E>[I]>{})...};
} else { } else {
return std::tuple{lambda(enum_constant<values_v<E>[I]>{})...}; return std::tuple{f(enum_constant<values_v<E>[I]>{})...};
} }
} }
template <typename E, typename Lambda, std::size_t... I> template <typename E, typename F, std::size_t... I>
constexpr bool all_invocable(std::index_sequence<I...>) { constexpr bool all_invocable(std::index_sequence<I...>) {
static_assert(is_enum_v<E>, "magic_enum::detail::all_invocable requires enum type."); static_assert(is_enum_v<E>, "magic_enum::detail::all_invocable requires enum type.");
return (std::is_invocable_v<Lambda, enum_constant<values_v<E>[I]>> && ...); if constexpr (count_v<E> == 0) {
return false;
} else {
return (std::is_invocable_v<F, enum_constant<values_v<E>[I]>> && ...);
}
} }
} // namespace magic_enum::detail } // namespace magic_enum::detail
@ -1066,18 +1085,18 @@ template <typename E>
if constexpr (detail::count_v<D> == 0) { if constexpr (detail::count_v<D> == 0) {
return {}; // Empty enum. return {}; // Empty enum.
} else if constexpr (detail::is_sparse_v<D> || detail::is_flags_v<D>) { } else if constexpr (detail::is_sparse_v<D> || detail::is_flags_v<D>) {
#if defined(MAGIC_ENUM_NO_HASH) #if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>(
[](std::size_t i) { return optional<std::size_t>{i}; },
value,
detail::default_result_type_lambda<optional<std::size_t>>);
#else
for (std::size_t i = 0; i < detail::count_v<D>; ++i) { for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (enum_value<D>(i) == value) { if (enum_value<D>(i) == value) {
return i; return i;
} }
} }
return {}; // Invalid value or out of range. return {}; // Invalid value or out of range.
#else
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>(
[](std::size_t i) { return optional<std::size_t>{i}; },
value,
detail::default_result_type_lambda<optional<std::size_t>>);
#endif #endif
} else { } else {
const auto v = static_cast<U>(value); const auto v = static_cast<U>(value);
@ -1109,8 +1128,8 @@ template <auto V>
// Returns name from enum value. // Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string. // If enum value does not have name or value out of range, returns empty string.
template <typename E> template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> { [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_default_t<E, string_view, VT> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
if (const auto i = enum_index<D>(value)) { if (const auto i = enum_index<D>(value)) {
@ -1119,38 +1138,61 @@ template <typename E>
return {}; return {};
} }
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <detail::value_type VT, typename E>
[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_default_t<E, string_view, VT> {
using D = std::decay_t<E>;
return enum_name<D, VT>(value);
}
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <typename E, detail::value_type VT>
[[nodiscard]] auto enum_name(E value) -> detail::enable_if_flags_t<E, string, VT> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_name requires enum-flags type.");
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.
}
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <detail::value_type VT, typename E>
[[nodiscard]] auto enum_name(E value) -> detail::enable_if_flags_t<E, string, VT> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_name requires enum-flags type.");
return enum_name<D, VT>(value);
}
// Returns name from enum-flags value. // Returns name from enum-flags value.
// If enum-flags value does not have name or value out of range, returns empty string. // If enum-flags value does not have name or value out of range, returns empty string.
template <typename E> template <typename E>
[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t<E, string> { [[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t<E, string> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
using U = underlying_type_t<D>; static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_name requires enum-flags type.");
if constexpr (detail::is_flags_v<D>) { return enum_name<D, detail::value_type::flags_value>(value);
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 {
if (const auto name = enum_name<D>(value); !name.empty()) {
return {name.data(), name.size()};
}
return {}; // Invalid value or out of range.
}
} }
// Returns std::array with names, sorted by enum value. // Returns std::array with names, sorted by enum value.
@ -1170,15 +1212,15 @@ inline constexpr auto case_insensitive = detail::case_insensitive{};
// Obtains enum value from integer value. // Obtains enum value from integer value.
// Returns optional with enum value. // Returns optional with enum value.
template <typename E> template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> { [[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
using U = underlying_type_t<D>; using U = underlying_type_t<D>;
if constexpr (detail::count_v<D> == 0) { if constexpr (detail::count_v<D> == 0) {
return {}; // Empty enum. return {}; // Empty enum.
} else if constexpr (detail::is_sparse_v<D>) { } else if constexpr (VT == detail::value_type::flags_value && detail::is_flags_v<D>) {
if constexpr (detail::is_flags_v<D>) { if constexpr (detail::is_sparse_v<D>) {
auto check_value = U{0}; auto check_value = U{0};
for (std::size_t i = 0; i < detail::count_v<D>; ++i) { for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (const auto v = static_cast<U>(enum_value<D>(i)); (value & v) != 0) { if (const auto v = static_cast<U>(enum_value<D>(i)); (value & v) != 0) {
@ -1191,42 +1233,51 @@ template <typename E>
} }
return {}; // Invalid value or out of range. return {}; // Invalid value or out of range.
} else { } else {
#if defined(MAGIC_ENUM_NO_HASH) constexpr auto min = detail::min_v<D>;
for (std::size_t i = 0; i < detail::count_v<D>; ++i) { constexpr auto max = detail::values_ors<D>();
if (value == static_cast<U>(enum_value<D>(i))) {
return static_cast<D>(value); if (value >= min && value <= max) {
} return static_cast<D>(value);
} }
return {}; // Invalid value or out of range. return {}; // Invalid value or out of range.
#else
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>);
#endif
} }
} else { } else {
constexpr auto min = detail::min_v<D>; #if defined(MAGIC_ENUM_ENABLE_HASH)
constexpr auto max = detail::is_flags_v<D> ? detail::values_ors<D>() : detail::max_v<D>; return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
if (value >= min && value <= max) { static_cast<D>(value),
return static_cast<D>(value); detail::default_result_type_lambda<optional<D>>);
#else
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (value == static_cast<U>(enum_value<D>(i))) {
return static_cast<D>(value);
}
} }
return {}; // Invalid value or out of range. return {}; // Invalid value or out of range.
#endif
} }
} }
// Obtains enum-flags value from integer value.
// Returns optional with enum-flags value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_cast requires enum-flags type.");
return enum_cast<D, detail::value_type::flags_value>(value);
}
// Obtains enum value from name. // Obtains enum value from name.
// Returns optional with enum value. // Returns optional with enum value.
template <typename E, typename BinaryPredicate = std::equal_to<>> template <typename E, detail::value_type VT = detail::value_type::default_value, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> { [[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
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 D = std::decay_t<E>;
using U = underlying_type_t<D>; using U = underlying_type_t<D>;
if constexpr (detail::count_v<D> == 0) { if constexpr (detail::count_v<D> == 0) {
return {}; // Empty enum. return {}; // Empty enum.
} else if constexpr (detail::is_flags_v<D>) { } else if constexpr (VT == detail::value_type::flags_value && detail::is_flags_v<D>) {
auto result = U{0}; auto result = U{0};
while (!value.empty()) { while (!value.empty()) {
const auto d = detail::find(value, '|'); const auto d = detail::find(value, '|');
@ -1249,21 +1300,14 @@ template <typename E, typename BinaryPredicate = std::equal_to<>>
return static_cast<D>(result); return static_cast<D>(result);
} }
return {}; // Invalid value or out of range. return {}; // Invalid value or out of range.
} else if constexpr (detail::count_v<D> > 0) { } else {
if constexpr (detail::is_default_predicate<BinaryPredicate>()) { if constexpr (detail::is_default_predicate<BinaryPredicate>() && detail::has_hash<D>) {
#if defined(MAGIC_ENUM_NO_HASH) #if defined(MAGIC_ENUM_ENABLE_HASH)
for (std::size_t i = 0; i < detail::count_v<D>; ++i) { return detail::constexpr_switch<&detail::names_v<D>, detail::case_call_t::index>(
if (detail::cmp_equal(value, detail::names_v<D>[i], p)) { [](std::size_t i) { return optional<D>{detail::values_v<D>[i]}; },
return enum_value<D>(i); value,
} detail::default_result_type_lambda<optional<D>>,
} [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
return {}; // Invalid value or out of range.
#else
return detail::constexpr_switch<&detail::names_v<D>, detail::case_call_t::index>(
[](std::size_t i) { return optional<D>{detail::values_v<D>[i]}; },
value,
detail::default_result_type_lambda<optional<D>>,
[&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
#endif #endif
} else { } else {
for (std::size_t i = 0; i < detail::count_v<D>; ++i) { for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
@ -1276,113 +1320,84 @@ template <typename E, typename BinaryPredicate = std::equal_to<>>
} }
} }
// Checks whether enum contains enumerator with such enum value. // Obtains enum-flags value from name.
template <typename E> // Returns optional with enum-flags value.
template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_cast requires enum-flags type.");
return enum_cast<D, detail::value_type::flags_value>(value, std::move(p));
}
// Checks whether enum contains value with such value.
template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t<E, bool> { [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
using U = underlying_type_t<D>; using U = underlying_type_t<D>;
return static_cast<bool>(enum_cast<D>(static_cast<U>(value))); return static_cast<bool>(enum_cast<D, VT>(static_cast<U>(value)));
} }
// Checks whether enum contains enumerator with such integer value. // Checks whether enum-flags contains value with such value.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_contains requires enum-flags type.");
return enum_contains<D, detail::value_type::flags_value>(value);
}
// Checks whether enum contains value with such integer value.
template <typename E, detail::value_type VT = detail::value_type::default_value>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> { [[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
return static_cast<bool>(enum_cast<D>(value)); return static_cast<bool>(enum_cast<D, VT>(value));
}
// Checks whether enum-flags contains value with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_contains requires enum-flags type.");
return enum_contains<D, detail::value_type::flags_value>(value);
} }
// Checks whether enum contains enumerator with such name. // Checks whether enum contains enumerator with such name.
template <typename E, detail::value_type VT = detail::value_type::default_value, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
using D = std::decay_t<E>;
return static_cast<bool>(enum_cast<D, VT>(value, std::move(p)));
}
// Checks whether enum-flags contains enumerator with such name.
template <typename E, typename BinaryPredicate = std::equal_to<>> template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> { [[nodiscard]] constexpr auto enum_flags_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
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>; using D = std::decay_t<E>;
static_assert(detail::is_flags_v<D>, "magic_enum::enum_flags_contains requires enum-flags type.");
return static_cast<bool>(enum_cast<D>(value, std::forward<BinaryPredicate>(p))); return enum_contains<D, detail::value_type::flags_value>(value, std::move(p));
} }
template <typename Result = void, typename E, typename Lambda> template <typename E, typename F, detail::enable_if_t<E, int> = 0>
constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t<E, Result> { constexpr auto enum_for_each(F&& f) {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<Lambda>(lambda),
value,
detail::default_result_type_lambda<Result>);
}
template <typename Result, typename E, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t<E, Result> {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<Lambda>(lambda),
value,
[&result] { return std::forward<Result>(result); });
}
template <typename E, typename Result = void, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v);
}
return detail::default_result_type_lambda<Result>();
}
template <typename E, typename Result, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result));
}
return std::forward<Result>(result);
}
template <typename E, typename Result = void, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value) -> detail::enable_if_t<E, Result> {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(value)) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v);
}
return detail::default_result_type_lambda<Result>();
}
template <typename E, typename Result, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value, Result&& result) -> detail::enable_if_t<E, Result> {
using D = std::decay_t<E>;
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH");
if (const auto v = enum_cast<D>(value)) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result));
}
return std::forward<Result>(result);
}
template <typename E, typename Lambda, detail::enable_if_t<E, int> = 0>
constexpr auto enum_for_each(Lambda&& lambda) {
using D = std::decay_t<E>; using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type."); static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
constexpr auto sep = std::make_index_sequence<detail::count_v<D>>{}; constexpr auto sep = std::make_index_sequence<detail::count_v<D>>{};
if constexpr (detail::all_invocable<D, Lambda>(sep)) { if constexpr (detail::all_invocable<D, F>(sep)) {
return detail::for_each<D>(std::forward<Lambda>(lambda), sep); return detail::for_each<D>(std::forward<F>(f), sep);
} else { } else {
static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value."); static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value.");
} }
} }
template <bool AsFlags = true>
inline constexpr auto as_flags = AsFlags ? detail::value_type::flags_value : detail::value_type::default_value;
#if !defined(MAGIC_ENUM_NO_STREAMS) #if !defined(MAGIC_ENUM_NO_STREAMS)
namespace ostream_operators { namespace ostream_operators {
@ -1393,7 +1408,7 @@ std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& o
using U = underlying_type_t<D>; using U = underlying_type_t<D>;
if constexpr (detail::supported<D>::value) { if constexpr (detail::supported<D>::value) {
if (const auto name = enum_flags_name<D>(value); !name.empty()) { if (const auto name = enum_name<D, as_flags<detail::is_flags_v<D>>>(value); !name.empty()) {
for (const auto c : name) { for (const auto c : name) {
os.put(c); os.put(c);
} }
@ -1418,7 +1433,7 @@ std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& i
std::basic_string<Char, Traits> s; std::basic_string<Char, Traits> s;
is >> s; is >> s;
if (const auto v = enum_cast<D>(s)) { if (const auto v = enum_cast<D, as_flags<detail::is_flags_v<D>>>(s)) {
value = *v; value = *v;
} else { } else {
is.setstate(std::basic_ios<Char>::failbit); is.setstate(std::basic_ios<Char>::failbit);

View file

@ -43,12 +43,6 @@
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE # define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
#endif // MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT #endif // MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT
#if !defined(MAGIC_ENUM_NO_EXCEPTIONS) && (defined(__cpp_exceptions) || defined(_EXCEPTIONS) || defined(_HAS_EXCEPTIONS))
# define MAGIC_ENUM_THROW throw std::format_error
#else
# define MAGIC_ENUM_THROW std::terminate(); (void)
#endif
namespace magic_enum::customize { namespace magic_enum::customize {
// customize enum to enable/disable automatic std::format // customize enum to enable/disable automatic std::format
template <typename E> template <typename E>
@ -62,18 +56,15 @@ namespace magic_enum::customize {
template <typename E> template <typename E>
struct std::formatter<E, std::enable_if_t<std::is_enum_v<E> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> { struct std::formatter<E, std::enable_if_t<std::is_enum_v<E> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> {
auto format(E e, format_context& ctx) { auto format(E e, format_context& ctx) {
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
using D = std::decay_t<E>; using D = std::decay_t<E>;
if constexpr (magic_enum::detail::is_flags_v<D>) {
if (auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) { if constexpr (detail::supported<D>::value) {
return this->std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx); if (const auto name = magic_enum::enum_name<D, magic_enum::as_flags<magic_enum::detail::is_flags_v<D>>>(e); !name.empty()) {
} return std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
} else {
if (auto name = magic_enum::enum_name<D>(e); !name.empty()) {
return this->std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
} }
} }
constexpr auto type_name = magic_enum::enum_type_name<E>(); return std::formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
MAGIC_ENUM_THROW("Type of " + std::string{type_name.data(), type_name.size()} + " enum value: " + std::to_string(magic_enum::enum_integer<D>(e)) + " is not exists.");
} }
}; };

View file

@ -0,0 +1,139 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.8.1
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP
#define NEARGYE_MAGIC_ENUM_SWITCH_HPP
#include "magic_enum.hpp"
namespace magic_enum {
namespace detail {
struct default_result_type {};
template <typename T>
struct identity {
using type = T;
};
struct nonesuch {};
template <typename F, typename V, bool = std::is_invocable_v<F, V>>
struct invoke_result : identity<nonesuch> {};
template <typename F, typename V>
struct invoke_result<F, V, true> : std::invoke_result<F, V> {};
template <typename F, typename V>
using invoke_result_t = typename invoke_result<F, V>::type;
template <typename E, typename F, std::size_t... I>
constexpr auto common_invocable(std::index_sequence<I...>) noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::invocable_index requires enum type.");
if constexpr (count_v<E> == 0) {
return identity<nonesuch>{};
} else {
return std::common_type<invoke_result_t<F, enum_constant<values_v<E>[I]>>...>{};
}
}
template <typename E, typename Result, typename F>
constexpr auto result_type() noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::result_type requires enum type.");
constexpr auto seq = std::make_index_sequence<detail::count_v<E>>{};
using R = typename decltype(common_invocable<E, F>(seq))::type;
if constexpr (std::is_same_v<Result, default_result_type>) {
if constexpr (std::is_same_v<R, nonesuch>) {
return identity<void>{};
} else {
return identity<R>{};
}
} else {
if constexpr (std::is_convertible_v<R, Result>) {
return identity<Result>{};
} else if constexpr (std::is_convertible_v<Result, R>) {
return identity<R>{};
} else {
return identity<nonesuch>{};
}
}
}
template <typename T, typename Result, typename F, typename D = std::decay_t<T>, typename R = typename decltype(result_type<D, Result, F>())::type>
using result_t = std::enable_if_t<std::is_enum_v<D> && !std::is_same_v<R, nonesuch>, R>;
} // namespace magic_enum::detail
template <typename Result = detail::default_result_type, typename E, typename F, typename R = detail::result_t<E, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<F>(f),
value,
detail::default_result_type_lambda<R>);
#else
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires defined MAGIC_ENUM_ENABLE_HASH");
#endif
}
template <typename Result, typename E, typename F, typename R = detail::result_t<E, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
std::forward<F>(f),
value,
[&result]() -> R { return std::forward<Result>(result); });
#else
static_assert(detail::has_hash<D>, "magic_enum::enum_switch requires defined MAGIC_ENUM_ENABLE_HASH");
#endif
}
} // namespace magic_enum
template <>
struct std::common_type<magic_enum::detail::nonesuch, magic_enum::detail::nonesuch> : magic_enum::detail::identity<magic_enum::detail::nonesuch> {};
template <typename T>
struct std::common_type<T, magic_enum::detail::nonesuch> : magic_enum::detail::identity<T> {};
template <typename T>
struct std::common_type<magic_enum::detail::nonesuch, T> : magic_enum::detail::identity<T> {};
#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP

View file

@ -545,14 +545,14 @@ TEST_CASE("enum_name") {
Color cb = Color::BLUE; Color cb = Color::BLUE;
REQUIRE(cr_name == "red"); REQUIRE(cr_name == "red");
REQUIRE(enum_name<Color&>(cb) == "BLUE"); REQUIRE(enum_name<Color&>(cb) == "BLUE");
REQUIRE(enum_name(cm[1]) == "GREEN"); REQUIRE(enum_name<as_flags<false>>(cm[1]) == "GREEN");
REQUIRE(enum_name(static_cast<Color>(0)).empty()); REQUIRE(enum_name<detail::value_type::default_value>(static_cast<Color>(0)).empty());
constexpr Numbers no = Numbers::one; constexpr Numbers no = Numbers::one;
constexpr auto no_name = enum_name(no); constexpr auto no_name = enum_name(no);
REQUIRE(no_name == "one"); REQUIRE(no_name == "one");
REQUIRE(enum_name(Numbers::two) == "two"); REQUIRE(enum_name<Numbers, as_flags<false>>(Numbers::two) == "two");
REQUIRE(enum_name(Numbers::three) == "three"); REQUIRE(enum_name<as_flags<false>, Numbers>(Numbers::three) == "three");
REQUIRE(enum_name(Numbers::many).empty()); REQUIRE(enum_name(Numbers::many).empty());
REQUIRE(enum_name(static_cast<Numbers>(0)).empty()); REQUIRE(enum_name(static_cast<Numbers>(0)).empty());
@ -1083,22 +1083,6 @@ constexpr std::string_view DoWork<Color::GREEN>() {
return "override"; return "override";
} }
TEST_CASE("enum_switch") {
constexpr auto bind_enum_switch = [] (Color c) {
return enum_switch([](auto val) {
return DoWork<val>();
}, c, string_view{"unrecognized"});
};
constexpr auto def = bind_enum_switch(Color::BLUE);
REQUIRE(def == "default");
REQUIRE(bind_enum_switch(Color::RED) == "default");
REQUIRE(bind_enum_switch(Color::GREEN) == "override");
REQUIRE(bind_enum_switch(static_cast<Color>(0)) == "unrecognized");
}
TEST_CASE("enum_for_each") { TEST_CASE("enum_for_each") {
SECTION("no return type") { SECTION("no return type") {
underlying_type_t<Color> sum{}; underlying_type_t<Color> sum{};
@ -1207,6 +1191,8 @@ TEST_CASE("multdimensional-switch-case") {
#include <magic_enum_format.hpp> #include <magic_enum_format.hpp>
TEST_CASE("format-base") { TEST_CASE("format-base") {
REQUIRE(std::format("{}", Color::RED) == "red");
REQUIRE(std::format("{}", Color{0}) == "0");
REQUIRE(std::format("Test-{:~^10}.", Color::RED) == "Test-~~~red~~~~."); REQUIRE(std::format("Test-{:~^10}.", Color::RED) == "Test-~~~red~~~~.");
} }

View file

@ -43,6 +43,9 @@ struct MyOpt {
}; };
struct MyString { struct MyString {
using value_type = char; // required
static constexpr auto npos = std::string_view::npos; // required
MyString() : str{} {} // required MyString() : str{} {} // required
MyString(const char* s, std::size_t l) : str{s, l} {} // required MyString(const char* s, std::size_t l) : str{s, l} {} // required
bool empty() const { return str.empty(); } // required bool empty() const { return str.empty(); } // required

View file

@ -93,12 +93,20 @@ TEST_CASE("enum_cast") {
REQUIRE(cr.value() == Color::RED); REQUIRE(cr.value() == Color::RED);
REQUIRE(enum_cast<Color&>("GREEN").value() == Color::GREEN); REQUIRE(enum_cast<Color&>("GREEN").value() == Color::GREEN);
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(enum_cast<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::BLUE | Color::RED)); REQUIRE_FALSE(enum_cast<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).has_value());
REQUIRE(enum_cast<Color&>("GREEN|RED").value() == (Color::GREEN | Color::RED)); REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED").has_value());
REQUIRE(enum_cast<Color&>("GREEN|RED|RED").value() == (Color::GREEN | Color::RED)); REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED|RED").has_value());
REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED|None").has_value()); REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED|None").has_value());
REQUIRE_FALSE(enum_cast<Color>("None").has_value()); REQUIRE_FALSE(enum_cast<Color>("None").has_value());
REQUIRE(enum_flags_cast<Color&>("GREEN").value() == Color::GREEN);
REQUIRE(enum_flags_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
REQUIRE(enum_flags_cast<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::BLUE | Color::RED));
REQUIRE(enum_flags_cast<Color&>("GREEN|RED").value() == (Color::GREEN | Color::RED));
REQUIRE(enum_flags_cast<Color&>("GREEN|RED|RED").value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_flags_cast<Color&>("GREEN|RED|None").has_value());
REQUIRE_FALSE(enum_flags_cast<Color>("None").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);
@ -122,7 +130,7 @@ TEST_CASE("enum_cast") {
REQUIRE_FALSE(enum_cast<Language>("None").has_value()); REQUIRE_FALSE(enum_cast<Language>("None").has_value());
#endif #endif
constexpr auto nto = enum_cast<number>("three|one"); constexpr auto nto = enum_flags_cast<number>("three|one");
REQUIRE(enum_cast<number>("one").value() == number::one); REQUIRE(enum_cast<number>("one").value() == number::one);
REQUIRE(enum_cast<number>("two").value() == number::two); REQUIRE(enum_cast<number>("two").value() == number::two);
REQUIRE(enum_cast<number>("three").value() == number::three); REQUIRE(enum_cast<number>("three").value() == number::three);
@ -137,11 +145,18 @@ TEST_CASE("enum_cast") {
REQUIRE(cr.value() == Color::RED); REQUIRE(cr.value() == Color::RED);
REQUIRE(enum_cast<Color&>(2).value() == Color::GREEN); REQUIRE(enum_cast<Color&>(2).value() == Color::GREEN);
REQUIRE(enum_cast<Color>(static_cast<int>(cm[2])).value() == Color::BLUE); REQUIRE(enum_cast<Color>(static_cast<int>(cm[2])).value() == Color::BLUE);
REQUIRE(enum_cast<Color>(1 | 2).value() == (Color::GREEN | Color::RED)); REQUIRE_FALSE(enum_cast<Color>(1 | 2).has_value());
REQUIRE(enum_cast<Color>(1 | 2 | 1).value() == (Color::GREEN | Color::RED)); REQUIRE_FALSE(enum_cast<Color>(1 | 2 | 1).has_value());
REQUIRE_FALSE(enum_cast<Color>(1 | 2 | 8).has_value()); REQUIRE_FALSE(enum_cast<Color>(1 | 2 | 8).has_value());
REQUIRE_FALSE(enum_cast<Color>(0).has_value()); REQUIRE_FALSE(enum_cast<Color>(0).has_value());
REQUIRE(enum_flags_cast<Color&>(2).value() == Color::GREEN);
REQUIRE(enum_flags_cast<Color>(static_cast<int>(cm[2])).value() == Color::BLUE);
REQUIRE(enum_flags_cast<Color>(1 | 2).value() == (Color::GREEN | Color::RED));
REQUIRE(enum_flags_cast<Color>(1 | 2 | 1).value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_flags_cast<Color>(1 | 2 | 8).has_value());
REQUIRE_FALSE(enum_flags_cast<Color>(0).has_value());
constexpr auto no = enum_cast<Numbers>(2); constexpr auto no = enum_cast<Numbers>(2);
REQUIRE(no.value() == Numbers::one); REQUIRE(no.value() == Numbers::one);
REQUIRE(enum_cast<Numbers>(4).value() == Numbers::two); REQUIRE(enum_cast<Numbers>(4).value() == Numbers::two);
@ -166,7 +181,7 @@ TEST_CASE("enum_cast") {
REQUIRE_FALSE(enum_cast<Language>(0).has_value()); REQUIRE_FALSE(enum_cast<Language>(0).has_value());
#endif #endif
constexpr auto nto = enum_cast<number>(2 | 8); constexpr auto nto = enum_flags_cast<number>(2 | 8);
REQUIRE(enum_cast<number>(1 << 1).value() == number::one); REQUIRE(enum_cast<number>(1 << 1).value() == number::one);
REQUIRE(enum_cast<number>(1 << 2).value() == number::two); REQUIRE(enum_cast<number>(1 << 2).value() == number::two);
REQUIRE(enum_cast<number>(1 << 3).value() == number::three); REQUIRE(enum_cast<number>(1 << 3).value() == number::three);
@ -230,11 +245,20 @@ TEST_CASE("enum_contains") {
REQUIRE(cr); REQUIRE(cr);
REQUIRE(enum_contains<Color&>(cg)); REQUIRE(enum_contains<Color&>(cg));
REQUIRE(enum_contains(cm[2])); REQUIRE(enum_contains(cm[2]));
REQUIRE(enum_contains<Color>(Color::RED | Color::GREEN)); REQUIRE(enum_contains<Color, as_flags<>>(Color::RED | Color::GREEN));
REQUIRE(enum_contains<Color>(Color::RED | Color::GREEN | Color::GREEN)); REQUIRE(enum_contains<Color, as_flags<true>>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color::GREEN));
REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color{8})); REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color{8}));
REQUIRE_FALSE(enum_contains(static_cast<Color>(0))); REQUIRE_FALSE(enum_contains(static_cast<Color>(0)));
REQUIRE(enum_flags_contains<Color&>(cg));
REQUIRE(enum_flags_contains(cm[2]));
REQUIRE(enum_flags_contains<Color>(Color::RED | Color::GREEN));
REQUIRE(enum_flags_contains<Color>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE_FALSE(enum_flags_contains<Color>(Color::RED | Color{8}));
REQUIRE_FALSE(enum_flags_contains(static_cast<Color>(0)));
constexpr auto no = enum_contains(Numbers::one); constexpr auto no = enum_contains(Numbers::one);
REQUIRE(no); REQUIRE(no);
REQUIRE(enum_contains(Numbers::two)); REQUIRE(enum_contains(Numbers::two));
@ -265,19 +289,31 @@ TEST_CASE("enum_contains") {
REQUIRE(enum_contains<number&>(number::two)); REQUIRE(enum_contains<number&>(number::two));
REQUIRE(enum_contains(number::one)); REQUIRE(enum_contains(number::one));
REQUIRE(enum_contains(number::four)); REQUIRE(enum_contains(number::four));
REQUIRE(nto); REQUIRE_FALSE(nto);
REQUIRE_FALSE(enum_contains(static_cast<number>(0))); REQUIRE_FALSE(enum_contains(static_cast<number>(0)));
REQUIRE(enum_flags_contains(number::three | number::one));
} }
SECTION("integer") { SECTION("integer") {
REQUIRE(enum_contains<Color>(1)); REQUIRE(enum_contains<Color>(1));
REQUIRE(enum_contains<Color&>(2)); REQUIRE(enum_contains<Color&>(2));
REQUIRE(enum_contains<Color>(4)); REQUIRE(enum_contains<const Color>(4));
REQUIRE(enum_contains<Color>(1 | 2)); REQUIRE(enum_contains<Color, as_flags<>>(1 | 2));
REQUIRE(enum_contains<Color>(1 | 2 | 1)); REQUIRE(enum_contains<Color, as_flags<true>>(1 | 2 | 1));
REQUIRE_FALSE(enum_contains<Color>(1 | 2));
REQUIRE_FALSE(enum_contains<Color>(1 | 2 | 1));
REQUIRE_FALSE(enum_contains<Color>(1 | 2 | 8)); REQUIRE_FALSE(enum_contains<Color>(1 | 2 | 8));
REQUIRE_FALSE(enum_contains<Color>(0)); REQUIRE_FALSE(enum_contains<Color>(0));
REQUIRE(enum_flags_contains<Color>(1));
REQUIRE(enum_flags_contains<Color&>(2));
REQUIRE(enum_flags_contains<Color>(4));
REQUIRE(enum_flags_contains<Color>(1 | 2));
REQUIRE(enum_flags_contains<Color>(1 | 2 | 1));
REQUIRE_FALSE(enum_flags_contains<Color>(1 | 2 | 8));
REQUIRE_FALSE(enum_flags_contains<Color>(0));
constexpr auto no = enum_contains<Numbers>(1 << 1); constexpr auto no = enum_contains<Numbers>(1 << 1);
REQUIRE(no); REQUIRE(no);
REQUIRE(enum_contains<Numbers>(1 << 2)); REQUIRE(enum_contains<Numbers>(1 << 2));
@ -305,9 +341,9 @@ TEST_CASE("enum_contains") {
REQUIRE(enum_contains<number>(1 << 2)); REQUIRE(enum_contains<number>(1 << 2));
REQUIRE(enum_contains<number>(1 << 3)); REQUIRE(enum_contains<number>(1 << 3));
REQUIRE(enum_contains<number>(1 << 4)); REQUIRE(enum_contains<number>(1 << 4));
REQUIRE(enum_contains<number>(8 | 2 | 16)); REQUIRE_FALSE(enum_contains<number>(8 | 2 | 16));
REQUIRE(enum_contains<number>(8 | 16 | 16)); REQUIRE_FALSE(enum_contains<number>(8 | 16 | 16));
REQUIRE(nto); REQUIRE_FALSE(nto);
REQUIRE_FALSE(enum_contains<number>(8 | 64)); REQUIRE_FALSE(enum_contains<number>(8 | 64));
REQUIRE_FALSE(enum_contains<number>(0)); REQUIRE_FALSE(enum_contains<number>(0));
} }
@ -316,13 +352,24 @@ TEST_CASE("enum_contains") {
constexpr auto cr = "RED"; constexpr auto cr = "RED";
REQUIRE(enum_contains<Color>(cr)); REQUIRE(enum_contains<Color>(cr));
REQUIRE(enum_contains<Color&>("GREEN")); REQUIRE(enum_contains<Color&>("GREEN"));
REQUIRE(enum_contains<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); REQUIRE(enum_contains<const Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); REQUIRE(enum_contains<Color&, as_flags<>>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&>("GREEN|RED")); REQUIRE(enum_contains<Color&, as_flags<true>>("GREEN|RED"));
REQUIRE(enum_contains<Color&>("GREEN|RED|RED")); REQUIRE(enum_contains<Color, as_flags<true>>("GREEN|RED|RED"));
REQUIRE_FALSE(enum_contains<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE_FALSE(enum_contains<Color&>("GREEN|RED"));
REQUIRE_FALSE(enum_contains<Color&>("GREEN|RED|RED"));
REQUIRE_FALSE(enum_contains<Color>("GREEN|RED|None")); REQUIRE_FALSE(enum_contains<Color>("GREEN|RED|None"));
REQUIRE_FALSE(enum_contains<Color>("None")); REQUIRE_FALSE(enum_contains<Color>("None"));
REQUIRE(enum_flags_contains<Color&>("GREEN"));
REQUIRE(enum_flags_contains<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_flags_contains<Color>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_flags_contains<Color>("GREEN|RED"));
REQUIRE(enum_flags_contains<Color>("GREEN|RED|RED"));
REQUIRE_FALSE(enum_flags_contains<Color>("GREEN|RED|None"));
REQUIRE_FALSE(enum_flags_contains<Color>("None"));
constexpr auto no = std::string_view{"one"}; constexpr auto no = std::string_view{"one"};
REQUIRE(enum_contains<Numbers>(no)); REQUIRE(enum_contains<Numbers>(no));
REQUIRE(enum_contains<Numbers>("two")); REQUIRE(enum_contains<Numbers>("two"));
@ -351,8 +398,10 @@ TEST_CASE("enum_contains") {
REQUIRE(enum_contains<number>("two")); REQUIRE(enum_contains<number>("two"));
REQUIRE(enum_contains<number>("three")); REQUIRE(enum_contains<number>("three"));
REQUIRE(enum_contains<number>("four")); REQUIRE(enum_contains<number>("four"));
REQUIRE(nto); REQUIRE_FALSE(nto);
REQUIRE_FALSE(enum_contains<number>("None")); REQUIRE_FALSE(enum_contains<number>("None"));
REQUIRE(enum_flags_contains<number>("three|one"));
} }
} }