1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-09 23:34:23 +00:00

clean-up & improve (#170)

This commit is contained in:
Daniil Goncharov 2022-03-28 18:56:16 +04:00 committed by GitHub
parent cee99df7b5
commit 2e7313d3f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 209 additions and 179 deletions

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019 - 2021 Daniil Goncharov Copyright (c) 2019 - 2022 Daniil Goncharov
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -13,8 +13,8 @@
* [`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_type_name` returns type name of enum.](#enum_type_name) * [`enum_type_name` returns type name of enum.](#enum_type_name)
* [`enum_fuse` returns a bijective mix of enum values.](#enum_fuse) * [`enum_fuse` returns a bijective mix of enum values.](#enum_fuse)
* [`enum_switch` TODO.](#enum_switch) * [`enum_switch` allows runtime enum value transformation to constexpr context.](#enum_switch)
* [`enum_for_each` TODO.](#enum_for_each) * [`enum_for_each` calls a function with all enum constexpr value.](#enum_for_each)
* [`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)

View file

@ -1,6 +1,6 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 Daniil Goncharov <neargye@gmail.com>. // Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal

View file

@ -1,6 +1,6 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 Daniil Goncharov <neargye@gmail.com>. // Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal

View file

@ -1,6 +1,6 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2020 - 2021 Daniil Goncharov <neargye@gmail.com>. // Copyright (c) 2020 - 2022 Daniil Goncharov <neargye@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal

View file

@ -1,6 +1,7 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2020 - 2021 Uruha Komachin <uruhakomachin@gmail.com>. // Copyright (c) 2020 - 2022 Daniil Goncharov <neargye@gmail.com>.
// Copyright (c) 2020 - 2022 Uruha Komachin <uruhakomachin@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal

View file

@ -1,6 +1,7 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 Daniil Goncharov <neargye@gmail.com>. // Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
// Copyright (c) 2020 - 2022 Bela Schaum <schaumb@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -36,11 +37,10 @@ constexpr std::string_view DoWork<Color::GREEN>() {
return "override"; return "override";
} }
// helper type for the visitor pattern // Helper type for the visitor pattern.
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() { int main() {
Color c = Color::RED; Color c = Color::RED;
@ -54,13 +54,12 @@ int main() {
magic_enum::enum_switch(lambda, c); // prints "override" magic_enum::enum_switch(lambda, c); // prints "override"
// with object, explicit enum type // with object, explicit enum type
auto switcher1 = overloaded { auto switcher1 = overloaded{
[] (auto val) -> std::enable_if_t <(Color::BLUE == decltype(val){}())> { [] (magic_enum::enum_constant<Color::BLUE>) {
std::cout << "Blue" << std::endl; std::cout << "Blue" << std::endl;
}, },
[] (std::integral_constant<Color, Color::RED>) { [] (magic_enum::enum_constant<Color::RED>) {
std::cout << "Red" << std::endl; std::cout << "Red" << std::endl;
} }
}; };
@ -69,15 +68,14 @@ int main() {
magic_enum::enum_switch<Color>(switcher1, 1 /* BLUE */); // prints "Blue" magic_enum::enum_switch<Color>(switcher1, 1 /* BLUE */); // prints "Blue"
magic_enum::enum_switch<Color>(switcher1, 0 /* RED */); // prints "Red" magic_enum::enum_switch<Color>(switcher1, 0 /* RED */); // prints "Red"
// explicit result type // explicit result type
auto switcher2 = overloaded { auto switcher2 = overloaded{
[] (std::integral_constant<Color, Color::GREEN>) { [] (magic_enum::enum_constant<Color::GREEN>) {
return "called with green argument"; return "called with green argument";
}, },
[] (Color other) { // default case [] (Color other) { // default case
auto name = magic_enum::enum_name(other); // not empty auto name = magic_enum::enum_name(other); // not empty
return "default: " + std::string{name.data(), name.size()}; return "default: " + std::string{name};
} }
}; };
@ -87,15 +85,14 @@ int main() {
auto empty = magic_enum::enum_switch<std::string>(switcher2, static_cast<Color>(-3)); // returns an empty string auto empty = magic_enum::enum_switch<std::string>(switcher2, static_cast<Color>(-3)); // returns an empty string
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<Color, std::string>(switcher2, -3, "unrecognized") << std::endl; // prints "unrecognized"
auto switcher3 = overloaded { auto switcher3 = overloaded{
[] (std::integral_constant<Color, Color::RED>) { [] (magic_enum::enum_constant<Color::RED>) {
return "red result"; return "red result";
}, },
[] (std::integral_constant<Color, Color::BLUE>) { [] (magic_enum::enum_constant<Color::BLUE>) {
return std::nullopt; return std::nullopt;
} }
}; };
@ -109,4 +106,4 @@ int main() {
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

@ -9,7 +9,7 @@
// //
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 Daniil Goncharov <neargye@gmail.com>. // Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -163,6 +163,9 @@ constexpr customize_t enum_type_name() noexcept {
namespace detail { namespace detail {
template <auto V, typename = std::enable_if_t<std::is_enum_v<std::decay_t<decltype(V)>>>>
using enum_constant = std::integral_constant<std::decay_t<decltype(V)>, V>;
template <typename... T> template <typename... T>
inline constexpr bool always_false_v = false; inline constexpr bool always_false_v = false;
@ -292,12 +295,24 @@ constexpr std::size_t find(string_view str, char c) noexcept {
} }
template <typename T, std::size_t N, std::size_t... I> template <typename T, std::size_t N, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N], std::index_sequence<I...>) { constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N], std::index_sequence<I...>) noexcept {
return {{a[I]...}}; return {{a[I]...}};
} }
template <typename BinaryPredicate> template <typename BinaryPredicate>
constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) { constexpr bool is_default_predicate() noexcept {
return std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<string_view::value_type>> ||
std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<>>;
}
template <typename BinaryPredicate>
constexpr bool is_nothrow_invocable() {
return is_default_predicate<BinaryPredicate>() ||
std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>;
}
template <typename BinaryPredicate>
constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(is_nothrow_invocable<BinaryPredicate>()) {
#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) #if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__)
// https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html // https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html
// https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html // https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html
@ -305,11 +320,8 @@ constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] Bina
#else #else
constexpr bool workaround = false; constexpr bool workaround = false;
#endif #endif
constexpr bool custom_predicate =
!std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<char>> &&
!std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<>>;
if constexpr (custom_predicate || workaround) { if constexpr (!is_default_predicate<BinaryPredicate>() || workaround) {
if (lhs.size() != rhs.size()) { if (lhs.size() != rhs.size()) {
return false; return false;
} }
@ -630,18 +642,17 @@ constexpr U values_ors() noexcept {
return ors; return ors;
} }
template <bool, typename T, typename R> template <bool, typename R>
struct enable_if_enum {}; struct enable_if_enum {};
template <typename T, typename R> template <typename R>
struct enable_if_enum<true, T, R> { struct enable_if_enum<true, R> {
using type = R; using type = R;
using D = std::decay_t<T>; static_assert(supported<R>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(supported<D>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
}; };
template <typename T, typename R = void> template <typename T, typename R, typename BinaryPredicate = std::equal_to<>>
using enable_if_enum_t = std::enable_if_t<std::is_enum_v<std::decay_t<T>>, R>; 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;
template <typename T, typename Enable = std::enable_if_t<std::is_enum_v<std::decay_t<T>>>> template <typename T, typename Enable = std::enable_if_t<std::is_enum_v<std::decay_t<T>>>>
using enum_concept = T; using enum_concept = T;
@ -738,27 +749,27 @@ struct constexpr_hash_t<Value, std::enable_if_t<std::is_same_v<Value, string_vie
template <typename Hash> template <typename Hash>
constexpr static Hash hash_v{}; constexpr static Hash hash_v{};
template <auto* globValues, typename Hash> template <auto* GlobValues, typename Hash>
constexpr auto calculate_cases(std::size_t page) { constexpr auto calculate_cases(std::size_t Page) noexcept {
constexpr std::array values = *globValues; constexpr std::array values = *GlobValues;
constexpr std::size_t size = values.size(); constexpr std::size_t size = values.size();
using SwitchType = std::invoke_result_t<Hash, typename decltype(values)::value_type>; using switch_t = std::invoke_result_t<Hash, typename decltype(values)::value_type>;
static_assert(std::is_integral_v<SwitchType> && !std::is_same_v<SwitchType, bool>); static_assert(std::is_integral_v<switch_t> && !std::is_same_v<switch_t, bool>);
const std::size_t values_to = (std::min)(static_cast<std::size_t>(256), size - page); const std::size_t values_to = (std::min)(static_cast<std::size_t>(256), size - Page);
std::array<SwitchType, 256> result{}; std::array<switch_t, 256> result{};
auto fill = result.begin(); auto fill = result.begin();
for (auto first = values.begin() + page, last = values.begin() + page + values_to; first != last; ) { for (auto first = values.begin() + Page, last = values.begin() + Page + values_to; first != last; ) {
*fill++ = hash_v<Hash>(*first++); *fill++ = hash_v<Hash>(*first++);
} }
// dead cases, try to avoid case collisions // dead cases, try to avoid case collisions
for (SwitchType last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits<SwitchType>::max)(); *fill++ = ++last_value) { for (switch_t last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits<switch_t>::max)(); *fill++ = ++last_value) {
} }
auto it = result.begin(); auto it = result.begin();
for (auto last_value = (std::numeric_limits<SwitchType>::min)(); fill != result.end(); *fill++ = last_value) { for (auto last_value = (std::numeric_limits<switch_t>::min)(); fill != result.end(); *fill++ = last_value) {
while (last_value == *it) { while (last_value == *it) {
++last_value, ++it; ++last_value, ++it;
} }
@ -767,7 +778,7 @@ constexpr auto calculate_cases(std::size_t page) {
return result; return result;
} }
template <typename R, typename F, typename... Args > template <typename R, typename F, typename... Args>
constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>) { constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>) {
if constexpr (std::is_void_v<R>) { if constexpr (std::is_void_v<R>) {
std::forward<F>(f)(std::forward<Args>(args)...); std::forward<F>(f)(std::forward<Args>(args)...);
@ -780,14 +791,14 @@ enum class case_call_t {
index, value index, value
}; };
template <typename DefaultResultType = void> template <typename T = void>
constexpr auto default_result_type_lambda = [] { return DefaultResultType{}; }; constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; };
template <> template <>
constexpr auto default_result_type_lambda<void> = [] {}; constexpr auto default_result_type_lambda<void> = []() noexcept {};
template <auto* Arr, typename Hash> template <auto* Arr, typename Hash>
constexpr bool no_duplicate() { constexpr bool no_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{};
@ -820,51 +831,50 @@ constexpr bool no_duplicate() {
#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 (call_v == 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 && "wrong result type"); \ assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \
} \ } \
} else if constexpr (call_v == case_call_t::value) { \ } else if constexpr (CallValue == case_call_t::value) { \
if constexpr (std::is_invocable_r_v<result_t, Lambda, std::integral_constant<value_t, 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), std::integral_constant<value_t, 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, std::integral_constant<value_t, values[val + page]>>) { \ } else if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \
assert(false && "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 call_v, 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 Lambda, typename ResultGetterType = decltype(default_result_type_lambda<>),
typename BinaryPredicate = std::equal_to<>> typename BinaryPredicate = std::equal_to<>>
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 = default_result_type_lambda<>,
BinaryPredicate&& pred = {}) { BinaryPredicate&& pred = {}) {
using result_t = std::invoke_result_t<ResultGetterType>; using result_t = std::invoke_result_t<ResultGetterType>;
using value_t = typename std::decay_t<decltype(*globValues)>::value_type; using hash_t = std::conditional_t<no_duplicate<GlobValues, Hash>(), Hash, typename Hash::secondary_hash>;
using hash_t = std::conditional_t<no_duplicate<globValues, Hash>(), Hash, typename Hash::secondary_hash>; 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);
switch (hash_v<hash_t>(searched)) { switch (hash_v<hash_t>(searched)) {
MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_CASE) MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_CASE)
default: default:
if constexpr (size > 256 + page) { if constexpr (size > 256 + Page) {
return constexpr_switch<globValues, call_v, page + 256, Hash>(std::forward<Lambda>(lambda), searched, std::forward<ResultGetterType>(def)); return constexpr_switch<GlobValues, CallValue, Page + 256, Hash>(std::forward<Lambda>(lambda), searched, std::forward<ResultGetterType>(def));
} }
break; break;
} }
return def(); return def();
} }
@ -872,22 +882,24 @@ 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
template<typename E, typename Lambda, std::size_t ... Ix> template <typename E, typename Lambda, std::size_t... I>
constexpr auto enum_for_each_impl(Lambda&& lambda, std::index_sequence<Ix...>) { constexpr auto for_each(Lambda&& lambda, std::index_sequence<I...>) {
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<Lambda, std::integral_constant<E, values_v<E>[Ix]>>> || ...); static_assert(is_enum_v<E>, "magic_enum::detail::for_each requires enum type.");
constexpr bool all_same_return = (std::is_same_v< constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> || ...);
std::invoke_result_t<Lambda, std::integral_constant<E, values_v<E>[0]>>, 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]>>> && ...);
std::invoke_result_t<Lambda, std::integral_constant<E, values_v<E>[Ix]>>> && ...);
if constexpr (has_void_return) { if constexpr (has_void_return) {
(lambda(std::integral_constant<E, values_v<E>[Ix]>{}), ...); (lambda(enum_constant<values_v<E>[I]>{}), ...);
} else if constexpr (all_same_return) { } else if constexpr (all_same_return) {
return std::array{lambda(std::integral_constant<E, values_v<E>[Ix]>{}) ...}; return std::array{lambda(enum_constant<values_v<E>[I]>{})...};
} else { } else {
return std::tuple{lambda(std::integral_constant<E, values_v<E>[Ix]>{}) ...}; return std::tuple{lambda(enum_constant<values_v<E>[I]>{})...};
} }
} }
template <typename E, typename Lambda, typename D = std::decay_t<E>>
using for_each_t = decltype(for_each<D>(std::declval<Lambda>(), std::make_index_sequence<count_v<D>>{}));
} // namespace magic_enum::detail } // namespace magic_enum::detail
// Checks is magic_enum supported compiler. // Checks is magic_enum supported compiler.
@ -920,9 +932,12 @@ struct underlying_type : detail::underlying_type<T> {};
template <typename T> template <typename T>
using underlying_type_t = typename underlying_type<T>::type; using underlying_type_t = typename underlying_type<T>::type;
template <auto V>
using enum_constant = detail::enum_constant<V>;
// Returns type name of enum. // Returns type name of enum.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_enum_t<E, string_view> { [[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_t<E, string_view> {
constexpr string_view name = detail::type_name_v<std::decay_t<E>>; constexpr string_view name = detail::type_name_v<std::decay_t<E>>;
static_assert(!name.empty(), "magic_enum::enum_type_name enum type does not have a name."); static_assert(!name.empty(), "magic_enum::enum_type_name enum type does not have a name.");
@ -931,14 +946,14 @@ template <typename E>
// Returns number of enum values. // Returns number of enum values.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t<E, std::size_t> { [[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_t<E, std::size_t> {
return detail::count_v<std::decay_t<E>>; return detail::count_v<std::decay_t<E>>;
} }
// Returns enum value at specified index. // Returns enum value at specified index.
// No bounds checking is performed: the behavior is undefined if index >= number of enum values. // No bounds checking is performed: the behavior is undefined if index >= number of enum values.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t<E, std::decay_t<E>> { [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
if constexpr (detail::is_sparse_v<D>) { if constexpr (detail::is_sparse_v<D>) {
@ -953,7 +968,7 @@ template <typename E>
// Returns enum value at specified index. // Returns enum value at specified index.
template <typename E, std::size_t I> template <typename E, std::size_t I>
[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_enum_t<E, std::decay_t<E>> { [[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
static_assert(I < detail::count_v<D>, "magic_enum::enum_value out of range."); static_assert(I < detail::count_v<D>, "magic_enum::enum_value out of range.");
@ -962,13 +977,13 @@ template <typename E, std::size_t I>
// Returns std::array with enum values, sorted by enum value. // Returns std::array with enum values, sorted by enum value.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t<E, detail::values_t<E>> { [[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t<E, detail::values_t<E>> {
return detail::values_v<std::decay_t<E>>; return detail::values_v<std::decay_t<E>>;
} }
// Returns integer value from enum value. // Returns integer value from enum value.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t<E, underlying_type_t<E>> { [[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_t<E, underlying_type_t<E>> {
return static_cast<underlying_type_t<E>>(value); return static_cast<underlying_type_t<E>>(value);
} }
@ -982,7 +997,7 @@ template <typename E>
// Obtains index in enum values from enum value. // Obtains index in enum values from enum value.
// Returns optional with index. // Returns optional with index.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t<E, optional<std::size_t>> { [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
using U = underlying_type_t<D>; using U = underlying_type_t<D>;
@ -990,9 +1005,9 @@ template <typename E>
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>) {
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>( return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>(
[](std::size_t i) { return optional<std::size_t>{i}; }, [](std::size_t i) { return optional<std::size_t>{i}; },
value, value,
detail::default_result_type_lambda<optional<std::size_t>>); detail::default_result_type_lambda<optional<std::size_t>>);
} else { } else {
const auto v = static_cast<U>(value); const auto v = static_cast<U>(value);
if (v >= detail::min_v<D> && v <= detail::max_v<D>) { if (v >= detail::min_v<D> && v <= detail::max_v<D>) {
@ -1005,7 +1020,7 @@ template <typename E>
// Returns name from static storage enum variable. // Returns name from static storage enum variable.
// This version is much lighter on the compile times and is not restricted to the enum_range limitation. // This version is much lighter on the compile times and is not restricted to the enum_range limitation.
template <auto V> template <auto V>
[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_enum_t<decltype(V), string_view> { [[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_t<decltype(V), string_view> {
constexpr string_view name = detail::enum_name_v<std::decay_t<decltype(V)>, V>; constexpr string_view name = detail::enum_name_v<std::decay_t<decltype(V)>, V>;
static_assert(!name.empty(), "magic_enum::enum_name enum value does not have a name."); static_assert(!name.empty(), "magic_enum::enum_name enum value does not have a name.");
@ -1015,10 +1030,11 @@ 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>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t<E, string_view> { [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> {
using D = std::decay_t<E>; using D = std::decay_t<E>;
if (const auto index = enum_index(value)) {
return detail::names_v<D>[*index]; if (const auto i = enum_index<D>(value)) {
return detail::names_v<D>[*i];
} }
return {}; return {};
} }
@ -1026,7 +1042,7 @@ template <typename E>
// 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_enum_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>; using U = underlying_type_t<D>;
@ -1056,20 +1072,20 @@ template <typename E>
// Returns std::array with names, sorted by enum value. // Returns std::array with names, sorted by enum value.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t<E, detail::names_t<E>> { [[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t<E, detail::names_t<E>> {
return detail::names_v<std::decay_t<E>>; return detail::names_v<std::decay_t<E>>;
} }
// Returns std::array with pairs (value, name), sorted by enum value. // Returns std::array with pairs (value, name), sorted by enum value.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t<E, detail::entries_t<E>> { [[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t<E, detail::entries_t<E>> {
return detail::entries_v<std::decay_t<E>>; return detail::entries_v<std::decay_t<E>>;
} }
// 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>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_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>;
@ -1091,9 +1107,9 @@ template <typename E>
return {}; // Invalid value or out of range. return {}; // Invalid value or out of range.
} else { } else {
return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>( return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; }, [](D v) { return optional<D>{v}; },
static_cast<D>(value), static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>); detail::default_result_type_lambda<optional<D>>);
} }
} else { } else {
constexpr auto min = detail::min_v<D>; constexpr auto min = detail::min_v<D>;
@ -1106,13 +1122,13 @@ template <typename E>
} }
} }
// allows you to write magic_enum::enum_cast<foo>("bar", magic_enum::case_insensitive); // Allows you to write magic_enum::enum_cast<foo>("bar", magic_enum::case_insensitive);
inline constexpr auto case_insensitive = detail::case_insensitive{}; inline constexpr auto case_insensitive = detail::case_insensitive{};
// 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<char>> template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, optional<std::decay_t<E>>> { [[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."); 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>;
@ -1143,15 +1159,12 @@ template <typename E, typename BinaryPredicate = std::equal_to<char>>
} }
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::count_v<D> > 0) {
constexpr bool default_predicate = if constexpr (detail::is_default_predicate<BinaryPredicate>()) {
std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<char>> ||
std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<>>;
if constexpr (default_predicate) {
return detail::constexpr_switch<&detail::names_v<D>, detail::case_call_t::index>( 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]}; }, [](std::size_t i) { return optional<D>{detail::values_v<D>[i]}; },
value, value,
detail::default_result_type_lambda<optional<D>>, detail::default_result_type_lambda<optional<D>>,
[&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
} 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) {
if (detail::cmp_equal(value, detail::names_v<D>[i], p)) { if (detail::cmp_equal(value, detail::names_v<D>[i], p)) {
@ -1165,7 +1178,7 @@ template <typename E, typename BinaryPredicate = std::equal_to<char>>
// Obtains index in enum values from static storage enum variable. // Obtains index in enum values from static storage enum variable.
template <auto V> template <auto V>
[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_enum_t<decltype(V), std::size_t> { [[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> {
constexpr auto index = enum_index<std::decay_t<decltype(V)>>(V); constexpr auto index = enum_index<std::decay_t<decltype(V)>>(V);
static_assert(index, "magic_enum::enum_index enum value does not have a index."); static_assert(index, "magic_enum::enum_index enum value does not have a index.");
@ -1174,7 +1187,7 @@ template <auto V>
// Checks whether enum contains enumerator with such enum value. // Checks whether enum contains enumerator with such enum value.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_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>;
@ -1183,69 +1196,88 @@ template <typename E>
// Checks whether enum contains enumerator with such integer value. // Checks whether enum contains enumerator with such integer value.
template <typename E> template <typename E>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, bool> { [[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
return static_cast<bool>(enum_cast<std::decay_t<E>>(value)); using D = std::decay_t<E>;
return static_cast<bool>(enum_cast<D>(value));
} }
// Checks whether enum contains enumerator with such name. // Checks whether enum contains enumerator with such name.
template <typename E, typename BinaryPredicate = std::equal_to<char>> template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, bool> { [[nodiscard]] constexpr auto enum_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."); static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_contains requires bool(char, char) invocable predicate.");
using D = std::decay_t<E>;
return static_cast<bool>(enum_cast<std::decay_t<E>>(value, std::move_if_noexcept(p))); return static_cast<bool>(enum_cast<D>(value, std::forward<BinaryPredicate>(p)));
} }
template<typename ResultType = void, typename Lambda, typename E> template <typename Result = void, typename E, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_enum_t<E, ResultType> { constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t<E, Result> {
return detail::constexpr_switch<&detail::values_v<std::decay_t<E>>, detail::case_call_t::value>( using D = std::decay_t<E>;
std::forward<Lambda>(lambda), value, detail::default_result_type_lambda<ResultType>);
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 ResultType, typename Lambda, typename E> template <typename Result, typename E, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, E value, ResultType&& result) -> detail::enable_if_enum_t<E, ResultType> { constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t<E, Result> {
return detail::constexpr_switch<&detail::values_v<std::decay_t<E>>, detail::case_call_t::value>( using D = std::decay_t<E>;
std::forward<Lambda>(lambda), value,
[&result] { return std::forward<ResultType>(result); }); 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 ResultType = void, typename BinaryPredicate = std::equal_to<char>, typename Lambda> template <typename E, typename Result = void, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, std::string_view name, BinaryPredicate&& p = {}) constexpr auto enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
-> std::enable_if_t<std::is_enum_v<std::decay_t<E>> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, ResultType> { static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
if (auto value = enum_cast<E>(name, std::forward<BinaryPredicate>(p))) { using D = std::decay_t<E>;
return enum_switch<ResultType>(std::forward<Lambda>(lambda), *value);
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<ResultType>(); return detail::default_result_type_lambda<Result>();
} }
template<typename E, typename ResultType, typename BinaryPredicate = std::equal_to<char>, typename Lambda> template <typename E, typename Result, typename BinaryPredicate = std::equal_to<>, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, std::string_view name, ResultType&& result, BinaryPredicate&& p = {}) constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
-> std::enable_if_t<std::is_enum_v<std::decay_t<E>> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, ResultType> { static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
if (auto value = enum_cast<E>(name, std::forward<BinaryPredicate>(p))) { using D = std::decay_t<E>;
return enum_switch(std::forward<Lambda>(lambda), *value, std::forward<ResultType>(result));
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<ResultType>(result); return std::forward<Result>(result);
} }
template<typename E, typename ResultType = void, typename Lambda> template <typename E, typename Result = void, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<std::decay_t<E>> raw_value) -> detail::enable_if_enum_t<E, ResultType> { constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value) -> detail::enable_if_t<E, Result> {
if (auto value = enum_cast<E>(raw_value)) { using D = std::decay_t<E>;
return enum_switch<ResultType>(std::forward<Lambda>(lambda), *value);
if (const auto v = enum_cast<D>(value)) {
return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v);
} }
return detail::default_result_type_lambda<ResultType>(); return detail::default_result_type_lambda<Result>();
} }
template<typename E, typename ResultType, typename Lambda> template <typename E, typename Result, typename Lambda>
constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<std::decay_t<E>> raw_value, ResultType&& result) -> detail::enable_if_enum_t<E, ResultType> { constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value, Result&& result) -> detail::enable_if_t<E, Result> {
if (auto value = enum_cast<E>(raw_value)) { using D = std::decay_t<E>;
return enum_switch(std::forward<Lambda>(lambda), *value, std::forward<ResultType>(result));
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<ResultType>(result); return std::forward<Result>(result);
} }
template<typename E, typename Lambda> template <typename E, typename Lambda>
constexpr auto enum_for_each(Lambda&& lambda) constexpr auto enum_for_each(Lambda&& lambda) -> detail::enable_if_t<E, detail::for_each_t<E, Lambda>> {
-> detail::enable_if_enum_t<E, decltype(detail::enum_for_each_impl<E>(std::declval<Lambda>(), std::make_index_sequence<detail::count_v<E>>{}))> { using D = std::decay_t<E>;
return detail::enum_for_each_impl<E>(std::forward<Lambda>(lambda), std::make_index_sequence<detail::count_v<E>>{});
return detail::for_each<D>(std::forward<Lambda>(lambda), std::make_index_sequence<detail::count_v<D>>{});
} }
namespace detail { namespace detail {
@ -1298,7 +1330,7 @@ template <typename... Es>
namespace ostream_operators { namespace ostream_operators {
template <typename Char, typename Traits, typename E, detail::enable_if_enum_t<E, int> = 0> template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) { std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
using D = std::decay_t<E>; using D = std::decay_t<E>;
using U = underlying_type_t<D>; using U = underlying_type_t<D>;
@ -1314,7 +1346,7 @@ std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& o
return (os << static_cast<U>(value)); return (os << static_cast<U>(value));
} }
template <typename Char, typename Traits, typename E, detail::enable_if_enum_t<E, int> = 0> template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) { std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
return value ? (os << *value) : os; return value ? (os << *value) : os;
} }
@ -1323,37 +1355,37 @@ std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& o
namespace bitwise_operators { namespace bitwise_operators {
template <typename E, detail::enable_if_enum_t<E, int> = 0> template <typename E, detail::enable_if_t<E, int> = 0>
constexpr E operator~(E rhs) noexcept { constexpr E operator~(E rhs) noexcept {
return static_cast<E>(~static_cast<underlying_type_t<E>>(rhs)); return static_cast<E>(~static_cast<underlying_type_t<E>>(rhs));
} }
template <typename E, detail::enable_if_enum_t<E, int> = 0> template <typename E, detail::enable_if_t<E, int> = 0>
constexpr E operator|(E lhs, E rhs) noexcept { constexpr E operator|(E lhs, E rhs) noexcept {
return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) | static_cast<underlying_type_t<E>>(rhs)); return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) | static_cast<underlying_type_t<E>>(rhs));
} }
template <typename E, detail::enable_if_enum_t<E, int> = 0> template <typename E, detail::enable_if_t<E, int> = 0>
constexpr E operator&(E lhs, E rhs) noexcept { constexpr E operator&(E lhs, E rhs) noexcept {
return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) & static_cast<underlying_type_t<E>>(rhs)); return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) & static_cast<underlying_type_t<E>>(rhs));
} }
template <typename E, detail::enable_if_enum_t<E, int> = 0> template <typename E, detail::enable_if_t<E, int> = 0>
constexpr E operator^(E lhs, E rhs) noexcept { constexpr E operator^(E lhs, E rhs) noexcept {
return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) ^ static_cast<underlying_type_t<E>>(rhs)); return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) ^ static_cast<underlying_type_t<E>>(rhs));
} }
template <typename E, detail::enable_if_enum_t<E, int> = 0> template <typename E, detail::enable_if_t<E, int> = 0>
constexpr E& operator|=(E& lhs, E rhs) noexcept { constexpr E& operator|=(E& lhs, E rhs) noexcept {
return lhs = (lhs | rhs); return lhs = (lhs | rhs);
} }
template <typename E, detail::enable_if_enum_t<E, int> = 0> template <typename E, detail::enable_if_t<E, int> = 0>
constexpr E& operator&=(E& lhs, E rhs) noexcept { constexpr E& operator&=(E& lhs, E rhs) noexcept {
return lhs = (lhs & rhs); return lhs = (lhs & rhs);
} }
template <typename E, detail::enable_if_enum_t<E, int> = 0> template <typename E, detail::enable_if_t<E, int> = 0>
constexpr E& operator^=(E& lhs, E rhs) noexcept { constexpr E& operator^=(E& lhs, E rhs) noexcept {
return lhs = (lhs ^ rhs); return lhs = (lhs ^ rhs);
} }

View file

@ -1,6 +1,6 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 Daniil Goncharov <neargye@gmail.com>. // Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -1039,12 +1039,12 @@ TEST_CASE("cmp_less") {
} }
} }
template<Color C> template <Color C>
constexpr std::string_view DoWork() { constexpr std::string_view DoWork() {
return "default"; return "default";
} }
template<> template <>
constexpr std::string_view DoWork<Color::GREEN>() { constexpr std::string_view DoWork<Color::GREEN>() {
return "override"; return "override";
} }
@ -1086,11 +1086,11 @@ TEST_CASE("enum_for_each") {
constexpr auto colorInts = enum_for_each<Color>([](auto val) { constexpr auto colorInts = enum_for_each<Color>([](auto val) {
return val; return val;
}); });
REQUIRE(std::is_same_v<std::remove_const_t<decltype(colorInts)>, std::tuple<
std::integral_constant<Color, Color::RED>, REQUIRE(std::is_same_v<std::remove_const_t<decltype(colorInts)>,
std::integral_constant<Color, Color::GREEN>, std::tuple<enum_constant<Color::RED>,
std::integral_constant<Color, Color::BLUE> enum_constant<Color::GREEN>,
>>); enum_constant<Color::BLUE>>>);
} }
} }

View file

@ -1,6 +1,6 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>. // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 Daniil Goncharov <neargye@gmail.com>. // Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal