diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index cc7f141..4f724bf 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -166,8 +166,8 @@ constexpr customize_t enum_type_name() noexcept { namespace detail { -template >>> -using enum_constant = std::integral_constant, V>; +template , std::enable_if_t, int> = 0> +using enum_constant = std::integral_constant; template inline constexpr bool always_false_v = false; @@ -696,7 +696,7 @@ struct enable_if_enum { template > using enable_if_t = typename enable_if_enum> && std::is_invocable_r_v, R>::type; -template >>> +template >, int> = 0> using enum_concept = T; template > @@ -717,6 +717,13 @@ struct underlying_type {}; template struct underlying_type : std::underlying_type> {}; +#if defined(MAGIC_ENUM_NO_HASH) +template +inline constexpr bool has_hash = false; +#else +template +inline constexpr bool has_hash = true; + template struct constexpr_hash_t; @@ -930,6 +937,7 @@ constexpr std::invoke_result_t constexpr_switch( #undef MAGIC_ENUM_FOR_EACH_256 #undef MAGIC_ENUM_CASE +#endif template constexpr auto for_each(Lambda&& lambda, std::index_sequence) { @@ -946,6 +954,13 @@ constexpr auto for_each(Lambda&& lambda, std::index_sequence) { } } +template +constexpr bool all_invocable(std::index_sequence) { + static_assert(is_enum_v, "magic_enum::detail::all_invocable requires enum type."); + + return (std::is_invocable_v[I]>> && ...); +} + } // namespace magic_enum::detail // Checks is magic_enum supported compiler. @@ -1049,10 +1064,19 @@ template if constexpr (detail::count_v == 0) { return {}; // Empty enum. } else if constexpr (detail::is_sparse_v || detail::is_flags_v) { +#if defined(MAGIC_ENUM_NO_HASH) + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (enum_value(i) == value) { + return i; + } + } + return {}; // Invalid value or out of range. +#else 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>); +#endif } else { const auto v = static_cast(value); if (v >= detail::min_v && v <= detail::max_v) { @@ -1120,7 +1144,10 @@ template return {}; // Invalid value or out of range. } else { - return string{enum_name(value)}; + if (const auto name = enum_name(value); !name.empty()) { + return {name.data(), name.size()}; + } + return {}; // Invalid value or out of range. } } @@ -1150,9 +1177,8 @@ template return {}; // Empty enum. } else if constexpr (detail::is_sparse_v) { if constexpr (detail::is_flags_v) { - constexpr auto count = detail::count_v; auto check_value = U{0}; - for (std::size_t i = 0; i < count; ++i) { + for (std::size_t i = 0; i < detail::count_v; ++i) { if (const auto v = static_cast(enum_value(i)); (value & v) != 0) { check_value |= v; } @@ -1163,10 +1189,19 @@ template } return {}; // Invalid value or out of range. } else { +#if defined(MAGIC_ENUM_NO_HASH) + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (value == static_cast(enum_value(i))) { + return static_cast(value); + } + } + 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>); +#endif } } else { constexpr auto min = detail::min_v; @@ -1214,11 +1249,20 @@ template > return {}; // Invalid value or out of range. } else if constexpr (detail::count_v > 0) { if constexpr (detail::is_default_predicate()) { +#if defined(MAGIC_ENUM_NO_HASH) + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(value, detail::names_v[i], p)) { + return enum_value(i); + } + } + return {}; // Invalid value or out of range. +#else 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); }); +#endif } else { for (std::size_t i = 0; i < detail::count_v; ++i) { if (detail::cmp_equal(value, detail::names_v[i], p)) { @@ -1259,6 +1303,7 @@ template > template constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t { using D = std::decay_t; + static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( std::forward(lambda), @@ -1269,6 +1314,7 @@ constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t { using D = std::decay_t; + static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( std::forward(lambda), @@ -1280,6 +1326,7 @@ template 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; + static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); if (const auto v = enum_cast(name, std::forward(p))) { return enum_switch(std::forward(lambda), *v); @@ -1291,6 +1338,7 @@ template 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; + static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); if (const auto v = enum_cast(name, std::forward(p))) { return enum_switch(std::forward(lambda), *v, std::forward(result)); @@ -1301,6 +1349,7 @@ constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, B template constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value) -> detail::enable_if_t { using D = std::decay_t; + static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); if (const auto v = enum_cast(value)) { return enum_switch(std::forward(lambda), *v); @@ -1311,6 +1360,7 @@ constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value) -> detai template constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value, Result&& result) -> detail::enable_if_t { using D = std::decay_t; + static_assert(detail::has_hash, "magic_enum::enum_switch requires no defined MAGIC_ENUM_NO_HASH"); if (const auto v = enum_cast(value)) { return enum_switch(std::forward(lambda), *v, std::forward(result)); @@ -1318,12 +1368,17 @@ constexpr auto enum_switch(Lambda&& lambda, underlying_type_t value, Result&& return std::forward(result); } -template +template = 0> constexpr auto enum_for_each(Lambda&& lambda) { using D = std::decay_t; static_assert(std::is_enum_v, "magic_enum::enum_for_each requires enum type."); + constexpr auto sep = std::make_index_sequence>{}; - return detail::for_each(std::forward(lambda), std::make_index_sequence>{}); + if constexpr (detail::all_invocable(sep)) { + return detail::for_each(std::forward(lambda), sep); + } else { + static_assert(detail::always_false_v, "magic_enum::enum_for_each requires invocable of all enum value."); + } } namespace ostream_operators {