diff --git a/LICENSE b/LICENSE index 05b04b9..05b298b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ 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 of this software and associated documentation files (the "Software"), to deal diff --git a/doc/reference.md b/doc/reference.md index 97894f1..30c0e26 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -13,8 +13,8 @@ * [`enum_contains` checks whether enum contains enumerator with such value.](#enum_contains) * [`enum_type_name` returns type name of enum.](#enum_type_name) * [`enum_fuse` returns a bijective mix of enum values.](#enum_fuse) -* [`enum_switch` TODO.](#enum_switch) -* [`enum_for_each` TODO.](#enum_for_each) +* [`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) * [`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) * [`underlying_type` improved UB-free "SFINAE-friendly" underlying_type.](#underlying_type) diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp index 318aff0..530770d 100644 --- a/example/enum_flag_example.cpp +++ b/example/enum_flag_example.cpp @@ -1,6 +1,6 @@ // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// 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 // of this software and associated documentation files (the "Software"), to deal diff --git a/example/example.cpp b/example/example.cpp index 7e05916..7a5c7a0 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -1,6 +1,6 @@ // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// 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 // of this software and associated documentation files (the "Software"), to deal diff --git a/example/example_custom_name.cpp b/example/example_custom_name.cpp index 0e7451b..99ecb71 100644 --- a/example/example_custom_name.cpp +++ b/example/example_custom_name.cpp @@ -1,6 +1,6 @@ // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// Copyright (c) 2020 - 2021 Daniil Goncharov . +// Copyright (c) 2020 - 2022 Daniil Goncharov . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/example/example_nonascii_name.cpp b/example/example_nonascii_name.cpp index d7cf2de..0451987 100644 --- a/example/example_nonascii_name.cpp +++ b/example/example_nonascii_name.cpp @@ -1,6 +1,7 @@ // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// Copyright (c) 2020 - 2021 Uruha Komachin . +// Copyright (c) 2020 - 2022 Daniil Goncharov . +// Copyright (c) 2020 - 2022 Uruha Komachin . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/example/example_switch.cpp b/example/example_switch.cpp index 4884805..a756169 100644 --- a/example/example_switch.cpp +++ b/example/example_switch.cpp @@ -1,6 +1,7 @@ // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// Copyright (c) 2019 - 2021 Daniil Goncharov . +// Copyright (c) 2019 - 2022 Daniil Goncharov . +// Copyright (c) 2020 - 2022 Bela Schaum . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,11 +37,10 @@ constexpr std::string_view DoWork() { return "override"; } -// helper type for the visitor pattern +// Helper type for the visitor pattern. template struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; - int main() { Color c = Color::RED; @@ -54,13 +54,12 @@ int main() { magic_enum::enum_switch(lambda, c); // prints "override" - // with object, explicit enum type - auto switcher1 = overloaded { - [] (auto val) -> std::enable_if_t <(Color::BLUE == decltype(val){}())> { + auto switcher1 = overloaded{ + [] (magic_enum::enum_constant) { std::cout << "Blue" << std::endl; }, - [] (std::integral_constant) { + [] (magic_enum::enum_constant) { std::cout << "Red" << std::endl; } }; @@ -69,15 +68,14 @@ int main() { magic_enum::enum_switch(switcher1, 1 /* BLUE */); // prints "Blue" magic_enum::enum_switch(switcher1, 0 /* RED */); // prints "Red" - // explicit result type - auto switcher2 = overloaded { - [] (std::integral_constant) { + auto switcher2 = overloaded{ + [] (magic_enum::enum_constant) { return "called with green argument"; }, [] (Color other) { // default case 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(switcher2, static_cast(-3)); // returns an empty string assert(empty.empty()); - // result with default object std::cout << magic_enum::enum_switch(switcher2, -3, "unrecognized") << std::endl; // prints "unrecognized" - auto switcher3 = overloaded { - [] (std::integral_constant) { + auto switcher3 = overloaded{ + [] (magic_enum::enum_constant) { return "red result"; }, - [] (std::integral_constant) { + [] (magic_enum::enum_constant) { 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::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 -} \ No newline at end of file +} diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index d2e015d..08de1dd 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -9,7 +9,7 @@ // // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// 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 // of this software and associated documentation files (the "Software"), to deal @@ -163,6 +163,9 @@ constexpr customize_t enum_type_name() noexcept { namespace detail { +template >>> +using enum_constant = std::integral_constant, V>; + template inline constexpr bool always_false_v = false; @@ -292,12 +295,24 @@ constexpr std::size_t find(string_view str, char c) noexcept { } template -constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) { +constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) noexcept { return {{a[I]...}}; } template -constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(std::is_nothrow_invocable_r_v) { +constexpr bool is_default_predicate() noexcept { + return std::is_same_v, std::equal_to> || + std::is_same_v, std::equal_to<>>; +} + +template +constexpr bool is_nothrow_invocable() { + return is_default_predicate() || + std::is_nothrow_invocable_r_v; +} + +template +constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(is_nothrow_invocable()) { #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/232218/c-constexpr-string-view.html @@ -305,11 +320,8 @@ constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] Bina #else constexpr bool workaround = false; #endif - constexpr bool custom_predicate = - !std::is_same_v, std::equal_to> && - !std::is_same_v, std::equal_to<>>; - if constexpr (custom_predicate || workaround) { + if constexpr (!is_default_predicate() || workaround) { if (lhs.size() != rhs.size()) { return false; } @@ -630,18 +642,17 @@ constexpr U values_ors() noexcept { return ors; } -template +template struct enable_if_enum {}; -template -struct enable_if_enum { +template +struct enable_if_enum { using type = R; - using D = std::decay_t; - static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); }; -template -using enable_if_enum_t = std::enable_if_t>, R>; +template > +using enable_if_t = typename enable_if_enum> && std::is_invocable_r_v, R>::type; template >>> using enum_concept = T; @@ -738,27 +749,27 @@ struct constexpr_hash_t constexpr static Hash hash_v{}; -template -constexpr auto calculate_cases(std::size_t page) { - constexpr std::array values = *globValues; +template +constexpr auto calculate_cases(std::size_t Page) noexcept { + constexpr std::array values = *GlobValues; constexpr std::size_t size = values.size(); - using SwitchType = std::invoke_result_t; - static_assert(std::is_integral_v && !std::is_same_v); - const std::size_t values_to = (std::min)(static_cast(256), size - page); + using switch_t = std::invoke_result_t; + static_assert(std::is_integral_v && !std::is_same_v); + const std::size_t values_to = (std::min)(static_cast(256), size - Page); - std::array result{}; + std::array result{}; 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(*first++); } // dead cases, try to avoid case collisions - for (SwitchType last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits::max)(); *fill++ = ++last_value) { + for (switch_t last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits::max)(); *fill++ = ++last_value) { } auto it = result.begin(); - for (auto last_value = (std::numeric_limits::min)(); fill != result.end(); *fill++ = last_value) { + for (auto last_value = (std::numeric_limits::min)(); fill != result.end(); *fill++ = last_value) { while (last_value == *it) { ++last_value, ++it; } @@ -767,7 +778,7 @@ constexpr auto calculate_cases(std::size_t page) { return result; } -template +template constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v) { if constexpr (std::is_void_v) { std::forward(f)(std::forward(args)...); @@ -780,14 +791,14 @@ enum class case_call_t { index, value }; -template -constexpr auto default_result_type_lambda = [] { return DefaultResultType{}; }; +template +constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v) { return T{}; }; template <> -constexpr auto default_result_type_lambda = [] {}; +constexpr auto default_result_type_lambda = []() noexcept {}; template -constexpr bool no_duplicate() { +constexpr bool no_duplicate() noexcept { using value_t = std::decay_t; using hash_value_t = std::invoke_result_t; std::arraysize()> hashes{}; @@ -820,51 +831,50 @@ constexpr bool no_duplicate() { #define MAGIC_ENUM_CASE(val) \ case cases[val]: \ - if constexpr ((val) + page < size) { \ - if (!pred(values[val + page], searched)) { \ + if constexpr ((val) + Page < size) { \ + if (!pred(values[val + Page], searched)) { \ break; \ } \ - if constexpr (call_v == case_call_t::index) { \ - if constexpr (std::is_invocable_r_v>) { \ - return detail::invoke_r(std::forward(lambda), std::integral_constant{}); \ - } else if constexpr (std::is_invocable_v>) { \ - assert(false && "wrong result type"); \ + if constexpr (CallValue == case_call_t::index) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), std::integral_constant{}); \ + } else if constexpr (std::is_invocable_v>) { \ + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ } \ - } else if constexpr (call_v == case_call_t::value) { \ - if constexpr (std::is_invocable_r_v>) { \ - return detail::invoke_r(std::forward(lambda), std::integral_constant{}); \ - } else if constexpr (std::is_invocable_r_v>) { \ - assert(false && "wrong result type"); \ + } else if constexpr (CallValue == case_call_t::value) { \ + if constexpr (std::is_invocable_r_v>) { \ + return detail::invoke_r(std::forward(lambda), enum_constant{}); \ + } else if constexpr (std::is_invocable_r_v>) { \ + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ } \ } \ break; \ } else [[fallthrough]]; -template ::value_type>, +template ::value_type>, typename Lambda, typename ResultGetterType = decltype(default_result_type_lambda<>), typename BinaryPredicate = std::equal_to<>> constexpr std::invoke_result_t constexpr_switch( Lambda&& lambda, - typename std::decay_t::value_type searched, + typename std::decay_t::value_type searched, ResultGetterType&& def = default_result_type_lambda<>, BinaryPredicate&& pred = {}) { using result_t = std::invoke_result_t; - using value_t = typename std::decay_t::value_type; - using hash_t = std::conditional_t(), Hash, typename Hash::secondary_hash>; - constexpr std::array values = *globValues; + using hash_t = std::conditional_t(), Hash, typename Hash::secondary_hash>; + constexpr std::array values = *GlobValues; constexpr std::size_t size = values.size(); - constexpr std::array cases = calculate_cases(page); + constexpr std::array cases = calculate_cases(Page); switch (hash_v(searched)) { MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_CASE) - default: - if constexpr (size > 256 + page) { - return constexpr_switch(std::forward(lambda), searched, std::forward(def)); - } - break; + default: + if constexpr (size > 256 + Page) { + return constexpr_switch(std::forward(lambda), searched, std::forward(def)); + } + break; } return def(); } @@ -872,22 +882,24 @@ constexpr std::invoke_result_t constexpr_switch( #undef MAGIC_ENUM_FOR_EACH_256 #undef MAGIC_ENUM_CASE -template -constexpr auto enum_for_each_impl(Lambda&& lambda, std::index_sequence) { - constexpr bool has_void_return = (std::is_void_v[Ix]>>> || ...); - constexpr bool all_same_return = (std::is_same_v< - std::invoke_result_t[0]>>, - std::invoke_result_t[Ix]>>> && ...); +template +constexpr auto for_each(Lambda&& lambda, std::index_sequence) { + static_assert(is_enum_v, "magic_enum::detail::for_each requires enum type."); + constexpr bool has_void_return = (std::is_void_v[I]>>> || ...); + constexpr bool all_same_return = (std::is_same_v[0]>>, std::invoke_result_t[I]>>> && ...); if constexpr (has_void_return) { - (lambda(std::integral_constant[Ix]>{}), ...); + (lambda(enum_constant[I]>{}), ...); } else if constexpr (all_same_return) { - return std::array{lambda(std::integral_constant[Ix]>{}) ...}; + return std::array{lambda(enum_constant[I]>{})...}; } else { - return std::tuple{lambda(std::integral_constant[Ix]>{}) ...}; + return std::tuple{lambda(enum_constant[I]>{})...}; } } +template > +using for_each_t = decltype(for_each(std::declval(), std::make_index_sequence>{})); + } // namespace magic_enum::detail // Checks is magic_enum supported compiler. @@ -920,9 +932,12 @@ struct underlying_type : detail::underlying_type {}; template using underlying_type_t = typename underlying_type::type; +template +using enum_constant = detail::enum_constant; + // Returns type name of enum. template -[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_t { constexpr string_view name = detail::type_name_v>; static_assert(!name.empty(), "magic_enum::enum_type_name enum type does not have a name."); @@ -931,14 +946,14 @@ template // Returns number of enum values. template -[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_t { return detail::count_v>; } // Returns enum value at specified index. // No bounds checking is performed: the behavior is undefined if index >= number of enum values. template -[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t> { using D = std::decay_t; if constexpr (detail::is_sparse_v) { @@ -953,7 +968,7 @@ template // Returns enum value at specified index. template -[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t> { using D = std::decay_t; static_assert(I < detail::count_v, "magic_enum::enum_value out of range."); @@ -962,13 +977,13 @@ template // Returns std::array with enum values, sorted by enum value. template -[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t> { return detail::values_v>; } // Returns integer value from enum value. template -[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_t> { return static_cast>(value); } @@ -982,7 +997,7 @@ template // Obtains index in enum values from enum value. // Returns optional with index. template -[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { using D = std::decay_t; using U = underlying_type_t; @@ -990,9 +1005,9 @@ template return {}; // Empty enum. } else if constexpr (detail::is_sparse_v || detail::is_flags_v) { return detail::constexpr_switch<&detail::values_v, detail::case_call_t::index>( - [](std::size_t i) { return optional{i}; }, - value, - detail::default_result_type_lambda>); + [](std::size_t i) { return optional{i}; }, + value, + detail::default_result_type_lambda>); } else { const auto v = static_cast(value); if (v >= detail::min_v && v <= detail::max_v) { @@ -1005,7 +1020,7 @@ template // 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. template -[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_t { constexpr string_view name = detail::enum_name_v, V>; static_assert(!name.empty(), "magic_enum::enum_name enum value does not have a name."); @@ -1015,10 +1030,11 @@ template // Returns name from enum value. // If enum value does not have name or value out of range, returns empty string. template -[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t { using D = std::decay_t; - if (const auto index = enum_index(value)) { - return detail::names_v[*index]; + + if (const auto i = enum_index(value)) { + return detail::names_v[*i]; } return {}; } @@ -1026,7 +1042,7 @@ template // Returns name from enum-flags value. // If enum-flags value does not have name or value out of range, returns empty string. template -[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_enum_t { +[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t { using D = std::decay_t; using U = underlying_type_t; @@ -1056,20 +1072,20 @@ template // Returns std::array with names, sorted by enum value. template -[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t> { return detail::names_v>; } // Returns std::array with pairs (value, name), sorted by enum value. template -[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t> { return detail::entries_v>; } // Obtains enum value from integer value. // Returns optional with enum value. template -[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_t>> { using D = std::decay_t; using U = underlying_type_t; @@ -1091,9 +1107,9 @@ template return {}; // Invalid value or out of range. } else { return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( - [](D v) { return optional{v}; }, - static_cast(value), - detail::default_result_type_lambda>); + [](D v) { return optional{v}; }, + static_cast(value), + detail::default_result_type_lambda>); } } else { constexpr auto min = detail::min_v; @@ -1106,13 +1122,13 @@ template } } -// allows you to write magic_enum::enum_cast("bar", magic_enum::case_insensitive); +// Allows you to write magic_enum::enum_cast("bar", magic_enum::case_insensitive); inline constexpr auto case_insensitive = detail::case_insensitive{}; // Obtains enum value from name. // Returns optional with enum value. -template > -[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { +template > +[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t>, BinaryPredicate> { static_assert(std::is_invocable_r_v, "magic_enum::enum_cast requires bool(char, char) invocable predicate."); using D = std::decay_t; using U = underlying_type_t; @@ -1143,15 +1159,12 @@ template > } return {}; // Invalid value or out of range. } else if constexpr (detail::count_v > 0) { - constexpr bool default_predicate = - std::is_same_v, std::equal_to> || - std::is_same_v, std::equal_to<>>; - if constexpr (default_predicate) { + if constexpr (detail::is_default_predicate()) { return detail::constexpr_switch<&detail::names_v, detail::case_call_t::index>( - [](std::size_t i) { return optional{detail::values_v[i]}; }, - value, - detail::default_result_type_lambda>, - [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); + [](std::size_t i) { return optional{detail::values_v[i]}; }, + value, + detail::default_result_type_lambda>, + [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); } else { for (std::size_t i = 0; i < detail::count_v; ++i) { if (detail::cmp_equal(value, detail::names_v[i], p)) { @@ -1165,7 +1178,7 @@ template > // Obtains index in enum values from static storage enum variable. template -[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t { constexpr auto index = enum_index>(V); static_assert(index, "magic_enum::enum_index enum value does not have a index."); @@ -1174,7 +1187,7 @@ template // Checks whether enum contains enumerator with such enum value. template -[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t { using D = std::decay_t; using U = underlying_type_t; @@ -1183,69 +1196,88 @@ template // Checks whether enum contains enumerator with such integer value. template -[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_t { - return static_cast(enum_cast>(value)); +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_t { + using D = std::decay_t; + + return static_cast(enum_cast(value)); } // Checks whether enum contains enumerator with such name. -template > -[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p = {}) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t { +template > +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t { static_assert(std::is_invocable_r_v, "magic_enum::enum_contains requires bool(char, char) invocable predicate."); + using D = std::decay_t; - return static_cast(enum_cast>(value, std::move_if_noexcept(p))); + return static_cast(enum_cast(value, std::forward(p))); } -template -constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_enum_t { - return detail::constexpr_switch<&detail::values_v>, detail::case_call_t::value>( - std::forward(lambda), value, detail::default_result_type_lambda); +template +constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t { + using D = std::decay_t; + + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( + std::forward(lambda), + value, + detail::default_result_type_lambda); } -template -constexpr auto enum_switch(Lambda&& lambda, E value, ResultType&& result) -> detail::enable_if_enum_t { - return detail::constexpr_switch<&detail::values_v>, detail::case_call_t::value>( - std::forward(lambda), value, - [&result] { return std::forward(result); }); +template +constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t { + using D = std::decay_t; + + return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( + std::forward(lambda), + value, + [&result] { return std::forward(result); }); } -template, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, std::string_view name, BinaryPredicate&& p = {}) - -> std::enable_if_t> && std::is_invocable_r_v, ResultType> { - if (auto value = enum_cast(name, std::forward(p))) { - return enum_switch(std::forward(lambda), *value); +template , typename Lambda> +constexpr auto enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}) -> detail::enable_if_t { + static_assert(std::is_invocable_r_v, "magic_enum::enum_switch requires bool(char, char) invocable predicate."); + using D = std::decay_t; + + if (const auto v = enum_cast(name, std::forward(p))) { + return enum_switch(std::forward(lambda), *v); } - return detail::default_result_type_lambda(); + return detail::default_result_type_lambda(); } -template, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, std::string_view name, ResultType&& result, BinaryPredicate&& p = {}) - -> std::enable_if_t> && std::is_invocable_r_v, ResultType> { - if (auto value = enum_cast(name, std::forward(p))) { - return enum_switch(std::forward(lambda), *value, std::forward(result)); +template , typename Lambda> +constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}) -> detail::enable_if_t { + static_assert(std::is_invocable_r_v, "magic_enum::enum_switch requires bool(char, char) invocable predicate."); + using D = std::decay_t; + + if (const auto v = enum_cast(name, std::forward(p))) { + return enum_switch(std::forward(lambda), *v, std::forward(result)); } - return std::forward(result); + return std::forward(result); } -template -constexpr auto enum_switch(Lambda&& lambda, underlying_type_t> raw_value) -> detail::enable_if_enum_t { - if (auto value = enum_cast(raw_value)) { - return enum_switch(std::forward(lambda), *value); +template +constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value) -> detail::enable_if_t { + using D = std::decay_t; + + if (const auto v = enum_cast(value)) { + return enum_switch(std::forward(lambda), *v); } - return detail::default_result_type_lambda(); + return detail::default_result_type_lambda(); } -template -constexpr auto enum_switch(Lambda&& lambda, underlying_type_t> raw_value, ResultType&& result) -> detail::enable_if_enum_t { - if (auto value = enum_cast(raw_value)) { - return enum_switch(std::forward(lambda), *value, std::forward(result)); +template +constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value, Result&& result) -> detail::enable_if_t { + using D = std::decay_t; + + if (const auto v = enum_cast(value)) { + return enum_switch(std::forward(lambda), *v, std::forward(result)); } - return std::forward(result); + return std::forward(result); } -template -constexpr auto enum_for_each(Lambda&& lambda) - -> detail::enable_if_enum_t(std::declval(), std::make_index_sequence>{}))> { - return detail::enum_for_each_impl(std::forward(lambda), std::make_index_sequence>{}); +template +constexpr auto enum_for_each(Lambda&& lambda) -> detail::enable_if_t> { + using D = std::decay_t; + + return detail::for_each(std::forward(lambda), std::make_index_sequence>{}); } namespace detail { @@ -1298,7 +1330,7 @@ template namespace ostream_operators { -template = 0> +template = 0> std::basic_ostream& operator<<(std::basic_ostream& os, E value) { using D = std::decay_t; using U = underlying_type_t; @@ -1314,7 +1346,7 @@ std::basic_ostream& operator<<(std::basic_ostream& o return (os << static_cast(value)); } -template = 0> +template = 0> std::basic_ostream& operator<<(std::basic_ostream& os, optional value) { return value ? (os << *value) : os; } @@ -1323,37 +1355,37 @@ std::basic_ostream& operator<<(std::basic_ostream& o namespace bitwise_operators { -template = 0> +template = 0> constexpr E operator~(E rhs) noexcept { return static_cast(~static_cast>(rhs)); } -template = 0> +template = 0> constexpr E operator|(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) | static_cast>(rhs)); } -template = 0> +template = 0> constexpr E operator&(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) & static_cast>(rhs)); } -template = 0> +template = 0> constexpr E operator^(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } -template = 0> +template = 0> constexpr E& operator|=(E& lhs, E rhs) noexcept { return lhs = (lhs | rhs); } -template = 0> +template = 0> constexpr E& operator&=(E& lhs, E rhs) noexcept { return lhs = (lhs & rhs); } -template = 0> +template = 0> constexpr E& operator^=(E& lhs, E rhs) noexcept { return lhs = (lhs ^ rhs); } diff --git a/test/test.cpp b/test/test.cpp index 8319cf5..2f995cf 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,6 +1,6 @@ // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// 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 // of this software and associated documentation files (the "Software"), to deal @@ -1039,12 +1039,12 @@ TEST_CASE("cmp_less") { } } -template +template constexpr std::string_view DoWork() { return "default"; } -template<> +template <> constexpr std::string_view DoWork() { return "override"; } @@ -1086,11 +1086,11 @@ TEST_CASE("enum_for_each") { constexpr auto colorInts = enum_for_each([](auto val) { return val; }); - REQUIRE(std::is_same_v, std::tuple< - std::integral_constant, - std::integral_constant, - std::integral_constant - >>); + + REQUIRE(std::is_same_v, + std::tuple, + enum_constant, + enum_constant>>); } } diff --git a/test/test_flags.cpp b/test/test_flags.cpp index 183639a..07f5a7c 100644 --- a/test/test_flags.cpp +++ b/test/test_flags.cpp @@ -1,6 +1,6 @@ // Licensed under the MIT License . // SPDX-License-Identifier: MIT -// 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 // of this software and associated documentation files (the "Software"), to deal