Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checking that the enum is valid (not empty and not forward declaration) #323

Merged
merged 4 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Neargye marked this conversation as resolved.
Show resolved Hide resolved
: 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)>;
static_assert(detail::is_valid_enum_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
Neargye marked this conversation as resolved.
Show resolved Hide resolved
constexpr auto index = enum_index<D, S>(V);
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
4 changes: 2 additions & 2 deletions include/magic_enum/magic_enum_containers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ template <typename E, typename V, typename Index = default_indexing<E>>
struct array {
static_assert(std::is_enum_v<E>);
static_assert(std::is_trivially_constructible_v<Index>);
static_assert(enum_count<E>() == 0 || Index::at(enum_values<E>().front())); // check Index is constexpr
static_assert(enum_count<E>() > 0 && Index::at(enum_values<E>().front()));
Neargye marked this conversation as resolved.
Show resolved Hide resolved

using index_type = Index;
using container_type = std::array<V, enum_count<E>()>;
Expand Down Expand Up @@ -481,7 +481,7 @@ template <typename E, typename Index = default_indexing<E>>
class bitset {
static_assert(std::is_enum_v<E>);
static_assert(std::is_trivially_constructible_v<Index>);
static_assert(enum_count<E>() == 0 || Index::at(enum_values<E>().front())); // check Index is constexpr
static_assert(enum_count<E>() > 0 && Index::at(enum_values<E>().front()));

using base_type = std::conditional_t<enum_count<E>() <= 8, std::uint_least8_t,
std::conditional_t<enum_count<E>() <= 16, std::uint_least16_t,
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
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>>);
}
5 changes: 0 additions & 5 deletions test/test_containers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,6 @@ TEST_CASE("containers_array") {
REQUIRE(std::make_pair(colors[1], color_rgb_container_int[colors[1]]) == std::make_pair<Color, std::uint8_t>(Color::GREEN, 2U));
REQUIRE(std::make_pair(colors[2], color_rgb_container_int[colors[2]]) == std::make_pair<Color, std::uint8_t>(Color::BLUE, 4U));

auto empty = magic_enum::containers::array<Empty, std::nullptr_t>();
REQUIRE(empty.empty());
REQUIRE(empty.size() == 0);
REQUIRE(magic_enum::enum_count<Empty>() == empty.size());

auto color_rgb_container = magic_enum::containers::array<Color, RGB>();
REQUIRE_FALSE(color_rgb_container.empty());
REQUIRE(color_rgb_container.size() == 3);
Expand Down
Loading