Skip to content

Commit

Permalink
checking that the enum is valid (not empty and not forward declaration)
Browse files Browse the repository at this point in the history
  • Loading branch information
Neargye committed Dec 8, 2023
1 parent 804738b commit f5cbb1d
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 13 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
44 changes: 37 additions & 7 deletions include/magic_enum/magic_enum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ namespace detail {
template <typename T>
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 <auto V, typename E = std::decay_t<decltype(V)>, std::enable_if_t<std::is_enum_v<E>, int> = 0>
Expand Down Expand Up @@ -881,6 +881,17 @@ constexpr U values_ors() noexcept {
return ors;
}

template <typename E, enum_subtype S>
struct is_valid_enum
#if defined(MAGIC_ENUM_NO_CHECK_VALID_ENUM_TYPE)
: std::true_type {};
#else
: std::bool_constant<std::is_enum_v<E> & (count_v<E, S> > 0)> {};
#endif

template <typename E, enum_subtype S>
inline constexpr auto is_valid_enum_v = is_valid_enum<std::decay_t<E>, S>{};

template <bool, typename R>
struct enable_if_enum {};

Expand Down Expand Up @@ -1180,6 +1191,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, detail::enum_subtype S = detail::subtype_v<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>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

if constexpr (detail::is_sparse_v<D, S>) {
return MAGIC_ENUM_ASSERT(index < detail::count_v<D, S>), detail::values_v<D, S>[index];
Expand All @@ -1194,6 +1206,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, std::size_t I, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
static_assert(I < detail::count_v<D, S>, "magic_enum::enum_value out of range.");

return enum_value<D, S>(I);
Expand All @@ -1202,7 +1215,10 @@ template <typename E, std::size_t I, detail::enum_subtype S = detail::subtype_v<
// Returns std::array with enum values, sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t<E, detail::values_t<E, S>> {
return detail::values_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

return detail::values_v<D, S>;
}

// Returns integer value from enum value.
Expand All @@ -1223,6 +1239,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
Expand Down Expand Up @@ -1255,14 +1272,17 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <detail::enum_subtype S, typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

return enum_index<D, S>(value);
}

// Obtains index in enum values from static storage enum variable.
template <auto V, detail::enum_subtype S = detail::subtype_v<std::decay_t<decltype(V)>>>
[[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)>, S>(V);
[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> {\
using D = std::decay_t<decltype(V)>;
constexpr auto index = enum_index<D, S>(V);
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
static_assert(index, "magic_enum::enum_index enum value does not have a index.");

return *index;
Expand All @@ -1283,6 +1303,7 @@ template <auto V>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> {
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

if (const auto i = enum_index<D, S>(value)) {
return detail::names_v<D, S>[*i];
Expand All @@ -1295,20 +1316,27 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <detail::enum_subtype S, typename E>
[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_t<E, string_view> {
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

return enum_name<D, S>(value);
}

// Returns std::array with names, sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t<E, detail::names_t<E, S>> {
return detail::names_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

return detail::names_v<D, S>;
}

// Returns std::array with pairs (value, name), sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t<E, detail::entries_t<E, S>> {
return detail::entries_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

return detail::entries_v<D, S>;
}

// Allows you to write magic_enum::enum_cast<foo>("bar", magic_enum::case_insensitive);
Expand All @@ -1319,6 +1347,7 @@ inline constexpr auto case_insensitive = detail::case_insensitive<>{};
template <typename E, detail::enum_subtype S = detail::subtype_v<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>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
Expand Down Expand Up @@ -1352,6 +1381,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
using D = std::decay_t<E>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
Expand Down
3 changes: 3 additions & 0 deletions include/magic_enum/magic_enum_flags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ template <typename E>
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
constexpr auto S = detail::enum_subtype::flags;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

string name;
auto check_value = U{0};
Expand Down Expand Up @@ -82,6 +83,7 @@ template <typename E>
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
constexpr auto S = detail::enum_subtype::flags;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
Expand Down Expand Up @@ -117,6 +119,7 @@ template <typename E, typename BinaryPredicate = std::equal_to<>>
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
constexpr auto S = detail::enum_subtype::flags;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");

if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
Expand Down
1 change: 1 addition & 0 deletions include/magic_enum/magic_enum_fuse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ template <detail::enum_subtype S, typename... Es>
// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements.
template <typename... Es>
[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept {
static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type.");
return enum_fuse<detail::subtypes_v<std::decay_t<Es>...>>(values...);
}

Expand Down
2 changes: 2 additions & 0 deletions include/magic_enum/magic_enum_switch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ template <typename Result = detail::default_result_type, typename E, detail::enu
constexpr decltype(auto) enum_switch(F&& f, E value) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
static_assert(detail::is_valid_enum_v<D, S>, "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<D, S>, detail::case_call_t::value>(
Expand All @@ -160,6 +161,7 @@ template <typename Result, typename E, detail::enum_subtype S = detail::subtype_
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
static_assert(detail::is_valid_enum_v<D, S>, "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<D, S>, detail::case_call_t::value>(
Expand Down
1 change: 1 addition & 0 deletions include/magic_enum/magic_enum_utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F,
constexpr auto enum_for_each(F&& f) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
constexpr auto sep = std::make_index_sequence<detail::count_v<D, S>>{};

if constexpr (detail::all_invocable<D, S, F>(sep)) {
Expand Down
31 changes: 29 additions & 2 deletions test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

#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 <magic_enum.hpp>
#include <magic_enum_fuse.hpp>
Expand Down Expand Up @@ -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<Forward1, as_flags<true>>);
//REQUIRE(magic_enum::detail::is_valid_enum_v<Forward1, as_flags<false>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Forward2, as_flags<true>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Forward2, as_flags<false>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Forward3, as_flags<true>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Forward3, as_flags<false>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Forward4, as_flags<true>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Forward4, as_flags<false>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty1, as_flags<true>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty1, as_flags<false>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty2, as_flags<true>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty2, as_flags<false>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty3, as_flags<true>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty3, as_flags<false>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty4, as_flags<true>>);
REQUIRE(magic_enum::detail::is_valid_enum_v<Empty4, as_flags<false>>);
}

0 comments on commit f5cbb1d

Please sign in to comment.