From e09f25780ac8be5523474dea1e707318274ab047 Mon Sep 17 00:00:00 2001 From: Daniil Goncharov Date: Sat, 16 Dec 2023 17:00:00 +0400 Subject: [PATCH] checking that the enum is valid (not empty and not forward declaration) (#323) --- README.md | 4 -- include/magic_enum/magic_enum.hpp | 44 ++++++++++++++++---- include/magic_enum/magic_enum_containers.hpp | 4 +- include/magic_enum/magic_enum_flags.hpp | 3 ++ include/magic_enum/magic_enum_switch.hpp | 2 + include/magic_enum/magic_enum_utility.hpp | 1 + test/test.cpp | 31 +++++++++++++- test/test_containers.cpp | 5 --- 8 files changed, 74 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 21cf12fca..7d5818841 100644 --- a/README.md +++ b/README.md @@ -194,10 +194,6 @@ Header-only C++17 library provides static reflection for enums, work with any en * IOstream operator for enum ```cpp - using namespace magic_enum::ostream_operators; // out-of-the-box ostream operators for enums. - Color color = Color::BLUE; - std::cout << color << std::endl; // "BLUE" - using namespace magic_enum::ostream_operators; // out-of-the-box ostream operators for enums. Color color = Color::BLUE; std::cout << color << std::endl; // "BLUE" diff --git a/include/magic_enum/magic_enum.hpp b/include/magic_enum/magic_enum.hpp index d050c7710..985f7dd5c 100644 --- a/include/magic_enum/magic_enum.hpp +++ b/include/magic_enum/magic_enum.hpp @@ -218,9 +218,9 @@ namespace detail { template struct supported #if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) - : std::true_type {}; + : std::true_type {}; #else - : std::false_type {}; + : std::false_type {}; #endif template , std::enable_if_t, int> = 0> @@ -881,6 +881,17 @@ constexpr U values_ors() noexcept { return ors; } +template +struct is_valid_enum +#if defined(MAGIC_ENUM_NO_CHECK_VALID_ENUM_TYPE) + : std::true_type {}; +#else + : std::bool_constant && (count_v > 0)> {}; +#endif + +template +inline constexpr auto is_valid_enum_v = is_valid_enum, S>{}; + template struct enable_if_enum {}; @@ -1180,6 +1191,7 @@ template > template > [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t> { using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); if constexpr (detail::is_sparse_v) { return MAGIC_ENUM_ASSERT(index < detail::count_v), detail::values_v[index]; @@ -1194,6 +1206,7 @@ template > template > [[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t> { using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); static_assert(I < detail::count_v, "magic_enum::enum_value out of range."); return enum_value(I); @@ -1202,7 +1215,10 @@ template > [[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t> { - return detail::values_v, S>; + using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); + + return detail::values_v; } // Returns integer value from enum value. @@ -1223,6 +1239,7 @@ template > [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { using D = std::decay_t; using U = underlying_type_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); if constexpr (detail::count_v == 0) { static_cast(value); @@ -1255,14 +1272,17 @@ template > template [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); return enum_index(value); } // Obtains index in enum values from static storage enum variable. template >> -[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t { - constexpr auto index = enum_index, S>(V); +[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t {\ + using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); + constexpr auto index = enum_index(V); static_assert(index, "magic_enum::enum_index enum value does not have a index."); return *index; @@ -1283,6 +1303,7 @@ template template > [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t { using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); if (const auto i = enum_index(value)) { return detail::names_v[*i]; @@ -1295,6 +1316,7 @@ template > template [[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_t { using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); return enum_name(value); } @@ -1302,13 +1324,19 @@ template // Returns std::array with names, sorted by enum value. template > [[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t> { - return detail::names_v, S>; + using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); + + 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_t> { - return detail::entries_v, S>; + using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); + + return detail::entries_v; } // Allows you to write magic_enum::enum_cast("bar", magic_enum::case_insensitive); @@ -1319,6 +1347,7 @@ inline constexpr auto case_insensitive = detail::case_insensitive<>{}; template > [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_t>> { using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); if constexpr (detail::count_v == 0) { static_cast(value); @@ -1352,6 +1381,7 @@ template > template , typename BinaryPredicate = std::equal_to<>> [[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable()) -> detail::enable_if_t>, BinaryPredicate> { using D = std::decay_t; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); if constexpr (detail::count_v == 0) { static_cast(value); diff --git a/include/magic_enum/magic_enum_containers.hpp b/include/magic_enum/magic_enum_containers.hpp index 870f1586b..7883b4ad3 100644 --- a/include/magic_enum/magic_enum_containers.hpp +++ b/include/magic_enum/magic_enum_containers.hpp @@ -331,7 +331,7 @@ template > struct array { static_assert(std::is_enum_v); static_assert(std::is_trivially_constructible_v); - static_assert(enum_count() == 0 || Index::at(enum_values().front())); // check Index is constexpr + static_assert(enum_count() > 0 && Index::at(enum_values().front())); using index_type = Index; using container_type = std::array()>; @@ -481,7 +481,7 @@ template > class bitset { static_assert(std::is_enum_v); static_assert(std::is_trivially_constructible_v); - static_assert(enum_count() == 0 || Index::at(enum_values().front())); // check Index is constexpr + static_assert(enum_count() > 0 && Index::at(enum_values().front())); using base_type = std::conditional_t() <= 8, std::uint_least8_t, std::conditional_t() <= 16, std::uint_least16_t, diff --git a/include/magic_enum/magic_enum_flags.hpp b/include/magic_enum/magic_enum_flags.hpp index 8cd77653c..3bb5cdfb9 100644 --- a/include/magic_enum/magic_enum_flags.hpp +++ b/include/magic_enum/magic_enum_flags.hpp @@ -52,6 +52,7 @@ template using D = std::decay_t; using U = underlying_type_t; constexpr auto S = detail::enum_subtype::flags; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); string name; auto check_value = U{0}; @@ -82,6 +83,7 @@ template using D = std::decay_t; using U = underlying_type_t; constexpr auto S = detail::enum_subtype::flags; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); if constexpr (detail::count_v == 0) { static_cast(value); @@ -117,6 +119,7 @@ template > using D = std::decay_t; using U = underlying_type_t; constexpr auto S = detail::enum_subtype::flags; + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); if constexpr (detail::count_v == 0) { static_cast(value); diff --git a/include/magic_enum/magic_enum_switch.hpp b/include/magic_enum/magic_enum_switch.hpp index a343e6af4..5b4e85d94 100644 --- a/include/magic_enum/magic_enum_switch.hpp +++ b/include/magic_enum/magic_enum_switch.hpp @@ -137,6 +137,7 @@ template ; static_assert(std::is_enum_v, "magic_enum::enum_switch requires enum type."); + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); #if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH) return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( @@ -160,6 +161,7 @@ template ; static_assert(std::is_enum_v, "magic_enum::enum_switch requires enum type."); + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); #if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH) return detail::constexpr_switch<&detail::values_v, detail::case_call_t::value>( diff --git a/include/magic_enum/magic_enum_utility.hpp b/include/magic_enum/magic_enum_utility.hpp index 6693b610b..bc917b705 100644 --- a/include/magic_enum/magic_enum_utility.hpp +++ b/include/magic_enum/magic_enum_utility.hpp @@ -67,6 +67,7 @@ template , typename F, constexpr auto enum_for_each(F&& f) { using D = std::decay_t; static_assert(std::is_enum_v, "magic_enum::enum_for_each requires enum type."); + static_assert(detail::is_valid_enum_v, "magic_enum requires enum implementation and valid max and min."); constexpr auto sep = std::make_index_sequence>{}; if constexpr (detail::all_invocable(sep)) { diff --git a/test/test.cpp b/test/test.cpp index 3e46e044d..b831f557d 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -23,9 +23,8 @@ #define CATCH_CONFIG_MAIN #include -#undef MAGIC_ENUM_RANGE_MIN +#define MAGIC_ENUM_NO_CHECK_VALID_ENUM_TYPE #define MAGIC_ENUM_RANGE_MIN -120 -#undef MAGIC_ENUM_RANGE_MAX #define MAGIC_ENUM_RANGE_MAX 120 #include #include @@ -1247,3 +1246,31 @@ TEST_CASE("enum_prev_value_circular") { REQUIRE(enum_prev_value_circular(Color::RED, -3) == Color::RED); REQUIRE(enum_prev_value_circular(Color::RED, -4) == Color::GREEN); } + +TEST_CASE("valid_enum") { + //enum Forward1; + enum Forward2 : uint32_t; + enum class Forward3; + enum class Forward4 : uint32_t; + enum Empty1 {}; + enum Empty2 : uint32_t {}; + enum class Empty3 {}; + enum class Empty4 : uint32_t {}; + + //REQUIRE(magic_enum::detail::is_valid_enum_v>); + //REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); + REQUIRE(magic_enum::detail::is_valid_enum_v>); +} diff --git a/test/test_containers.cpp b/test/test_containers.cpp index 167306d71..a486a4c0a 100644 --- a/test/test_containers.cpp +++ b/test/test_containers.cpp @@ -119,11 +119,6 @@ TEST_CASE("containers_array") { REQUIRE(std::make_pair(colors[1], color_rgb_container_int[colors[1]]) == std::make_pair(Color::GREEN, 2U)); REQUIRE(std::make_pair(colors[2], color_rgb_container_int[colors[2]]) == std::make_pair(Color::BLUE, 4U)); - auto empty = magic_enum::containers::array(); - REQUIRE(empty.empty()); - REQUIRE(empty.size() == 0); - REQUIRE(magic_enum::enum_count() == empty.size()); - auto color_rgb_container = magic_enum::containers::array(); REQUIRE_FALSE(color_rgb_container.empty()); REQUIRE(color_rgb_container.size() == 3);