1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-10 23:44:29 +00:00

remove enum_traits (#38)

This commit is contained in:
Daniil Goncharov 2020-05-24 16:11:08 +05:00 committed by GitHub
parent 11b9c109b3
commit 2b9f24fd2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 203 additions and 183 deletions

View file

@ -33,6 +33,7 @@ Header-only C++17 library provides static reflection for enums, work with any en
* `enum_entries` obtains pair (value enum, string enum name) sequence.
* `enum_index` obtains index in enum value sequence from enum value.
* `enum_contains` checks whether enum contains enumerator with such value.
* `enum_type_name` returns string name of enum type.
* `is_unscoped_enum` checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration).
* `is_scoped_enum` checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations).
* `underlying_type` improved UB-free "SFINAE-friendly" [std::underlying_type](https://en.cppreference.com/w/cpp/types/underlying_type).

View file

@ -93,5 +93,3 @@
* GCC `-fconstexpr-depth=N`, `-fconstexpr-loop-limit=N`, `-fconstexpr-ops-limit=N` <https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Dialect-Options.html>
* Intellisense Visual Studio may have some problems analyzing `magic_enum`.
* [Intellisense hangs forever when using `magic_enum` since 0.6.1 on x86 projects.](https://github.com/Neargye/magic_enum/issues/34)
* [Intellisense incorrect analysis constexpr.](https://github.com/Neargye/magic_enum/issues/26)

View file

@ -10,6 +10,7 @@
* [`enum_entries` obtains pair (value enum, string enum name) sequence.](#enum_entries)
* [`enum_index` obtains index in enum value sequence from enum value.](#enum_index)
* [`enum_contains` checks whether enum contains enumerator with such value.](#enum_contains)
* [`enum_type_name` returns string name of enum type.](#enum_type_name)
* [`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" std::underlying_type.](#underlying_type)
@ -241,6 +242,23 @@ constexpr bool enum_contains(string_view value) noexcept;
magic_enum::enum_contains<Color>("fda"); // -> false
```
## `enum_type_name`
```cpp
template <typename E>
constexpr string_view enum_type_name() noexcept;
```
* Returns `std::string_view` with null-terminated string name of enum type.
* Examples
```cpp
Color color = Color::RED;
auto type_name = magic_enum::enum_type_name<decltype(color)>();
// color_name -> "Color"
```
## `is_unscoped_enum`
```cpp

View file

@ -46,7 +46,6 @@
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 26495) // Variable 'magic_enum::detail::static_string<N>::chars' is uninitialized.
# pragma warning(disable : 26451) // Arithmetic overflow: 'indexes[static_cast<U>(value) - min_v<E>]' using operator '-' on a 4 byte value and then casting the result to a 8 byte value.
#endif
// Checks magic_enum compiler compatibility.
@ -308,6 +307,9 @@ constexpr auto indexes(std::integer_sequence<int, I...>) noexcept {
return std::array<index_t<E>, sizeof...(I)>{{((n<E, static_cast<E>(I + min_v<E>)>().size() != 0) ? i++ : invalid_index_v<E>)...}};
}
template <typename E>
inline constexpr auto indexes_v = indexes<E>(std::make_integer_sequence<int, range_size_v<E>>{});
template <typename E, std::size_t... I>
constexpr auto names(std::index_sequence<I...>) noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::names requires enum type.");
@ -315,6 +317,9 @@ constexpr auto names(std::index_sequence<I...>) noexcept {
return std::array<std::string_view, sizeof...(I)>{{name_v<E, values_v<E>[I]>...}};
}
template <typename E>
inline constexpr auto names_v = names<E>(std::make_index_sequence<count_v<E>>{});
template <typename E, std::size_t... I>
constexpr auto entries(std::index_sequence<I...>) noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::entries requires enum type.");
@ -322,6 +327,36 @@ constexpr auto entries(std::index_sequence<I...>) noexcept {
return std::array<std::pair<E, std::string_view>, sizeof...(I)>{{{values_v<E>[I], name_v<E, values_v<E>[I]>}...}};
}
template <typename E>
inline constexpr auto entries_v = entries<E>(std::make_index_sequence<count_v<E>>{});
template <typename E>
inline static constexpr bool is_sparse = range_size_v<E> != count_v<E>;
template <typename E, typename U = std::underlying_type_t<E>>
constexpr int undex(U value) noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::undex requires enum type.");
if (const auto i = static_cast<int>(value) - min_v<E>; value >= static_cast<U>(min_v<E>) && value <= static_cast<U>(max_v<E>)) {
if constexpr (is_sparse<E>) {
if (const auto idx = indexes_v<E>[i]; idx != invalid_index_v<E>) {
return idx;
}
} else {
return i;
}
}
return -1; // Value out of range.
}
template <typename E>
constexpr int endex(E value) noexcept {
static_assert(is_enum_v<E>, "magic_enum::detail::endex requires enum type.");
return undex<E>(static_cast<std::underlying_type_t<E>>(value));
}
template <typename T, typename R>
using enable_if_enum_t = std::enable_if_t<std::is_enum_v<std::decay_t<T>>, R>;
@ -346,76 +381,6 @@ struct underlying_type {};
template <typename T>
struct underlying_type<T, true> : std::underlying_type<std::decay_t<T>> {};
template <typename E, bool = is_enum_v<E>>
struct enum_traits {};
template <typename E>
struct enum_traits<E, true> {
using type = E;
using underlying_type = typename detail::underlying_type<E>::type;
inline static constexpr std::string_view type_name = detail::type_name_v<E>;
inline static constexpr bool is_unscoped = detail::is_unscoped_enum<E>::value;
inline static constexpr bool is_scoped = detail::is_scoped_enum<E>::value;
inline static constexpr bool is_dense = detail::range_size_v<E> == detail::count_v<E>;
inline static constexpr bool is_sparse = detail::range_size_v<E> != detail::count_v<E>;
inline static constexpr std::size_t count = detail::count_v<E>;
inline static constexpr std::array<E, count> values = detail::values_v<E>;
inline static constexpr std::array<std::string_view, count> names = detail::names<E>(std::make_index_sequence<count_v<E>>{});
inline static constexpr std::array<std::pair<E, std::string_view>, count> entries = detail::entries<E>(std::make_index_sequence<count_v<E>>{});
[[nodiscard]] static constexpr bool reflected(E value) noexcept {
return reflected(static_cast<U>(value));
}
[[nodiscard]] static constexpr int index(E value) noexcept {
return index(static_cast<U>(value));
}
[[nodiscard]] static constexpr E value(std::size_t index) noexcept {
if constexpr (is_sparse) {
return assert(index < count), values[index];
} else {
return assert(index < count), static_cast<E>(index + min_v<E>);
}
}
[[nodiscard]] static constexpr std::string_view name(E value) noexcept {
if (const auto i = index(value); i != -1) {
return names[i];
}
return {}; // Value out of range.
}
private:
static_assert(is_enum_v<E>, "magic_enum::enum_traits requires enum type.");
static_assert(supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(count > 0, "magic_enum::enum_range requires enum implementation and valid max and min.");
using U = underlying_type;
inline static constexpr auto indexes = detail::indexes<E>(std::make_integer_sequence<int, range_size_v<E>>{});
static constexpr bool reflected(U value) noexcept {
return value >= static_cast<U>(reflected_min_v<E>) && value <= static_cast<U>(reflected_max_v<E>);
}
static constexpr int index(U value) noexcept {
if (value >= static_cast<U>(min_v<E>) && value <= static_cast<U>(max_v<E>)) {
if constexpr (is_sparse) {
if (const auto i = indexes[value - min_v<E>]; i != invalid_index_v<E>) {
return i;
}
} else {
return value - min_v<E>;
}
}
return -1; // Value out of range.
}
};
} // namespace magic_enum::detail
// Checks is magic_enum supported compiler.
@ -448,105 +413,47 @@ struct underlying_type : detail::underlying_type<T> {};
template <typename T>
using underlying_type_t = typename underlying_type<T>::type;
// Enum traits defines a compile-time template-based interface to query the properties of enum.
// Returns string name of enum type.
template <typename E>
using enum_traits = detail::enum_traits<std::decay_t<E>>;
[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_enum_t<E, std::string_view> {
constexpr std::string_view name = detail::type_name_v<std::decay_t<E>>;
static_assert(name.size() > 0, "Enum type does not have a name.");
// Obtains enum value from enum string name.
// Returns std::optional with enum value.
template <typename E, typename BinaryPredicate>
[[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_cast requires bool(char, char) invocable predicate.");
using D = std::decay_t<E>;
if constexpr (detail::range_size_v<D> > detail::count_v<D> * 2) {
for (std::size_t i = 0; i < enum_traits<D>::count; ++i) {
if (detail::cmp_equal(value, enum_traits<D>::names[i], p)) {
return enum_traits<D>::values[i];
}
}
} else {
for (auto i = detail::min_v<D>; i <= detail::max_v<D>; ++i) {
if (detail::cmp_equal(value, enum_traits<D>::name(static_cast<D>(i)), p)) {
return static_cast<D>(i);
}
}
}
return std::nullopt; // Invalid value or out of range.
return name;
}
// Returns number of enum values.
template <typename E>
[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
return enum_cast<E>(value, detail::char_equal{});
}
[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t<E, std::size_t> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
// Obtains enum value from integer value.
// Returns std::optional with enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
if (enum_traits<D>::index(static_cast<D>(value)) != -1) {
return static_cast<D>(value);
}
return std::nullopt; // Invalid value or out of range.
}
// Returns integer value from enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t<E, underlying_type_t<E>> {
return static_cast<underlying_type_t<E>>(value);
}
// Obtains index in enum value sequence from enum value.
// Returns std::optional with index.
template <typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::size_t>> {
if (const auto i = enum_traits<E>::index(value); i != -1) {
return i;
}
return std::nullopt; // Value out of range.
}
// Checks whether enum contains enumerator with such value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t<E, bool> {
return enum_traits<E>::index(value) != -1;
}
// Checks whether enum contains enumerator with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, bool> {
return enum_cast<E>(value).has_value();
}
// Checks whether enum contains enumerator with such string enum name.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t<E, bool> {
return enum_cast<E>(value).has_value();
return detail::count_v<std::decay_t<E>>;
}
// Returns enum value at specified index.
// No bounds checking is performed: the behavior is undefined if index >= number of enum values.
template <typename E>
[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t<E, std::decay_t<E>> {
return enum_traits<E>::value(index);
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
using D = std::decay_t<E>;
if constexpr (detail::is_sparse<D>) {
return assert(index < detail::count_v<D>), detail::values_v<D>[index];
} else {
return assert(index < detail::count_v<D>), static_cast<D>(index + detail::min_v<D>);
}
}
// Obtains value enum sequence.
// Returns std::array with enum values, sorted by enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t<E, decltype(enum_traits<E>::values)&> {
return enum_traits<E>::values;
}
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t<E, decltype(detail::values_v<std::decay_t<E>>)&> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
// Returns number of enum values.
template <typename E>
[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t<E, std::size_t> {
return enum_traits<E>::count;
return detail::values_v<std::decay_t<E>>;
}
// Returns string enum name from static storage enum variable.
@ -563,21 +470,131 @@ template <auto V>
// If enum value does not have name or value out of range, returns empty string.
template <typename E>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t<E, std::string_view> {
return enum_traits<E>::name(value);
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
using D = std::decay_t<E>;
if (const auto i = detail::endex<D>(value); i != -1) {
return detail::names_v<D>[i];
}
return {}; // Value out of range.
}
// Obtains string enum name sequence.
// Returns std::array with string enum names, sorted by enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t<E, decltype(enum_traits<E>::names)&> {
return enum_traits<E>::names;
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t<E, decltype(detail::names_v<std::decay_t<E>>)&> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
return detail::names_v<std::decay_t<E>>;
}
// Obtains pair (value enum, string enum name) sequence.
// Returns std::array with std::pair (value enum, string enum name), sorted by enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t<E, decltype(enum_traits<E>::entries)&> {
return enum_traits<E>::entries;
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t<E, decltype(detail::entries_v<std::decay_t<E>>)&> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
return detail::entries_v<std::decay_t<E>>;
}
// Obtains enum value from enum string name.
// Returns std::optional with enum value.
template <typename E, typename BinaryPredicate>
[[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_cast requires bool(char, char) invocable predicate.");
using D = std::decay_t<E>;
if constexpr (detail::range_size_v<D> > detail::count_v<D> * 2) {
for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
if (detail::cmp_equal(value, detail::names_v<D>[i], p)) {
return detail::values_v<D>[i];
}
}
} else {
for (auto i = detail::min_v<D>; i <= detail::max_v<D>; ++i) {
if (detail::cmp_equal(value, enum_name(static_cast<D>(i)), p)) {
return static_cast<D>(i);
}
}
}
return std::nullopt; // Invalid value or out of range.
}
template <typename E>
[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
return enum_cast<E>(value, detail::char_equal{});
}
// Obtains enum value from integer value.
// Returns std::optional with enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
using D = std::decay_t<E>;
if (detail::undex<D>(value) != -1) {
return static_cast<D>(value);
}
return std::nullopt; // Invalid value or out of range.
}
// Returns integer value from enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t<E, underlying_type_t<E>> {
return static_cast<underlying_type_t<E>>(value);
}
// Obtains index in enum value sequence from enum value.
// Returns std::optional with index.
template <typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::size_t>> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
if (const auto i = detail::endex<std::decay_t<E>>(value); i != -1) {
return i;
}
return std::nullopt; // Value out of range.
}
// Checks whether enum contains enumerator with such value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t<E, bool> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
return detail::endex<std::decay_t<E>>(value) != -1;
}
// Checks whether enum contains enumerator with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_enum_t<E, bool> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
return detail::undex<std::decay_t<E>>(value) != -1;
}
// Checks whether enum contains enumerator with such string enum name.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t<E, bool> {
static_assert(detail::supported<E>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
static_assert(detail::count_v<std::decay_t<E>> > 0, "magic_enum requires enum implementation and valid max and min.");
return enum_cast<E>(value).has_value();
}
namespace ostream_operators {

View file

@ -567,31 +567,12 @@ TEST_CASE("type_traits") {
REQUIRE_FALSE(is_scoped_enum_v<number>);
}
TEST_CASE("enum_traits") {
REQUIRE(enum_traits<Color&>::type_name == "Color");
REQUIRE(enum_traits<Numbers>::type_name == "Numbers");
REQUIRE(enum_traits<Directions&>::type_name == "Directions");
REQUIRE(enum_traits<number>::type_name == "number");
REQUIRE_FALSE(enum_traits<Color&>::is_unscoped);
REQUIRE_FALSE(enum_traits<Numbers>::is_unscoped);
REQUIRE(enum_traits<Directions&>::is_unscoped);
REQUIRE(enum_traits<number>::is_unscoped);
REQUIRE(enum_traits<Color&>::is_scoped);
REQUIRE(enum_traits<Numbers>::is_scoped);
REQUIRE_FALSE(enum_traits<Directions&>::is_scoped);
REQUIRE_FALSE(enum_traits<number>::is_scoped);
REQUIRE_FALSE(enum_traits<Color&>::is_dense);
REQUIRE(enum_traits<Numbers>::is_dense);
REQUIRE_FALSE(enum_traits<Directions&>::is_dense);
REQUIRE_FALSE(enum_traits<number>::is_dense);
REQUIRE(enum_traits<Color&>::is_sparse);
REQUIRE_FALSE(enum_traits<Numbers>::is_sparse);
REQUIRE(enum_traits<Directions&>::is_sparse);
REQUIRE(enum_traits<number>::is_sparse);
TEST_CASE("enum_type_name") {
REQUIRE(enum_type_name<Color&>() == "Color");
REQUIRE(enum_type_name<Numbers>() == "Numbers");
REQUIRE(enum_type_name<Directions&>() == "Directions");
REQUIRE(enum_type_name<number>() == "number");
}
#if defined(_MSC_VER) && _MSC_VER >= 1920
@ -628,6 +609,11 @@ TEST_CASE("extrema") {
NONE = std::numeric_limits<std::uint64_t>::max()
};
REQUIRE(magic_enum::enum_name<BadColor>(BadColor::NONE).empty());
REQUIRE_FALSE(magic_enum::enum_cast<BadColor>(std::numeric_limits<std::uint64_t>::max()).has_value());
REQUIRE_FALSE(magic_enum::enum_contains<BadColor>(std::numeric_limits<std::uint64_t>::max()));
REQUIRE_FALSE(magic_enum::enum_contains<BadColor>(BadColor::NONE));
SECTION("min") {
REQUIRE(magic_enum::enum_range<BadColor>::min == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::reflected_min_v<BadColor> == 0);