From d9f4cd5e748136b16b1d1417bbf1881264c25920 Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 4 Jul 2020 14:57:25 +0500 Subject: [PATCH 01/18] wip --- include/magic_enum.hpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 2df8bc6..ad1d968 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -32,6 +32,10 @@ #ifndef NEARGYE_MAGIC_ENUM_HPP #define NEARGYE_MAGIC_ENUM_HPP +#define MAGIC_ENUM_VERSION_MAJOR 0 +#define MAGIC_ENUM_VERSION_MINOR 6 +#define MAGIC_ENUM_VERSION_PATCH 6 + #include #include #include @@ -188,14 +192,16 @@ constexpr bool cmp_less(L lhs, R rhs) noexcept { } } -template +#if defined(NEARGYE_NAMEOF_HPP) +using ::nameof::detail::type_name_v; +#else +template constexpr auto n() noexcept { - static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); -#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && _MSC_VER >= 1910 # if defined(__clang__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36}; + constexpr std::string_view name{__PRETTY_FUNCTION__ + 35, sizeof(__PRETTY_FUNCTION__) - 38}; # elif defined(__GNUC__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 49, sizeof(__PRETTY_FUNCTION__) - 51}; + constexpr std::string_view name{__PRETTY_FUNCTION__ + 50, sizeof(__PRETTY_FUNCTION__) - 53}; # elif defined(_MSC_VER) constexpr std::string_view name{__FUNCSIG__ + 40, sizeof(__FUNCSIG__) - 57}; # endif @@ -205,9 +211,13 @@ constexpr auto n() noexcept { #endif } -template -inline constexpr auto type_name_v = n(); +template +inline constexpr auto type_name_v = n(); +#endif +#if defined(NEARGYE_NAMEOF_HPP) +using ::nameof::detail::enum_name_v; +#else template constexpr auto n() noexcept { static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); @@ -224,7 +234,8 @@ constexpr auto n() noexcept { } template -inline constexpr auto name_v = n(); +inline constexpr auto enum_name_v = n(); +#endif template constexpr bool is_valid() noexcept { @@ -321,7 +332,7 @@ template constexpr auto names(std::index_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::names requires enum type."); - return std::array{{name_v[I]>...}}; + return std::array{{enum_name_v[I]>...}}; } template @@ -331,7 +342,7 @@ template constexpr auto entries(std::index_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::entries requires enum type."); - return std::array, sizeof...(I)>{{{values_v[I], name_v[I]>}...}}; + return std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; } template @@ -472,7 +483,7 @@ template [[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_enum_t { using D = std::decay_t; static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - constexpr std::string_view name = detail::name_v, V>; + constexpr std::string_view name = detail::enum_name_v, V>; static_assert(name.size() > 0, "Enum value does not have a name."); return name; From d2904860b8a234897de78f0391fff1cabe9134b8 Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 4 Jul 2020 15:13:15 +0500 Subject: [PATCH 02/18] wip --- include/magic_enum.hpp | 61 +++++++++++++++++++----------------------- test/test.cpp | 6 +++++ 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index ad1d968..983f54f 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -299,6 +299,9 @@ constexpr auto values(std::integer_sequence) noexcept { template inline constexpr auto values_v = values(std::make_integer_sequence, reflected_max_v>()>{}); +template > +using values_t = decltype((values_v)); + template inline constexpr std::size_t count_v = values_v.size(); @@ -338,6 +341,9 @@ constexpr auto names(std::index_sequence) noexcept { template inline constexpr auto names_v = names(std::make_index_sequence>{}); +template > +using names_t = decltype((names_v)); + template constexpr auto entries(std::index_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::entries requires enum type."); @@ -348,6 +354,9 @@ constexpr auto entries(std::index_sequence) noexcept { template inline constexpr auto entries_v = entries(std::make_index_sequence>{}); +template > +using entries_t = decltype((entries_v)); + template inline constexpr bool is_sparse_v = range_size_v != count_v; @@ -375,8 +384,19 @@ constexpr int endex(E value) noexcept { return undex(static_cast>(value)); } +template +struct enable_if_enum {}; + template -using enable_if_enum_t = std::enable_if_t>, R>; +struct enable_if_enum { + using type = R; + using D = std::decay_t; + static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(count_v > 0, "magic_enum requires enum implementation and valid max and min."); +}; + +template +using enable_if_enum_t = typename enable_if_enum>, T, R>::type; template >>> using enum_concept = T; @@ -433,8 +453,9 @@ using underlying_type_t = typename underlying_type::type; // Returns string name of enum type. template -[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr std::string_view enum_type_name() noexcept { using D = std::decay_t; + static_assert(std::is_enum_v, "Requires enum type."); constexpr std::string_view name = detail::type_name_v; static_assert(name.size() > 0, "Enum type does not have a name."); @@ -445,8 +466,6 @@ template template [[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); return detail::count_v; } @@ -456,8 +475,6 @@ template template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); if constexpr (detail::is_sparse_v) { return assert(index < detail::count_v), detail::values_v[index]; @@ -469,10 +486,8 @@ template // Obtains value enum sequence. // Returns std::array with enum values, sorted by enum value. template -[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t>)&> { +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); return detail::values_v; } @@ -480,9 +495,9 @@ template // Returns string enum name from static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. template -[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr std::string_view enum_name() noexcept { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(std::is_enum_v, "Requires enum type."); constexpr std::string_view name = detail::enum_name_v, V>; static_assert(name.size() > 0, "Enum value does not have a name."); @@ -494,8 +509,6 @@ template template [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); if (const auto i = detail::endex(value); i != -1) { return detail::names_v[i]; @@ -507,10 +520,8 @@ template // Obtains string enum name sequence. // Returns std::array with string enum names, sorted by enum value. template -[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t>)&> { +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); return detail::names_v; } @@ -518,10 +529,8 @@ template // Obtains pair (value enum, string enum name) sequence. // Returns std::array with std::pair (value enum, string enum name), sorted by enum value. template -[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t>)&> { +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); return detail::entries_v; } @@ -531,8 +540,6 @@ template template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); static_assert(std::is_invocable_r_v, "magic_enum::enum_cast requires bool(char, char) invocable predicate."); for (std::size_t i = 0; i < detail::count_v; ++i) { @@ -547,8 +554,6 @@ template template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t>> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); for (std::size_t i = 0; i < detail::count_v; ++i) { if (value == detail::names_v[i]) { @@ -564,8 +569,6 @@ template template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); if (detail::undex(value) != -1) { return static_cast(value); @@ -585,8 +588,6 @@ template template [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); if (const auto i = detail::endex(value); i != -1) { return i; @@ -599,8 +600,6 @@ template template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); return detail::endex(value) != -1; } @@ -609,8 +608,6 @@ template template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); return detail::undex(value) != -1; } @@ -619,8 +616,6 @@ template template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - static_assert(detail::supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); return enum_cast(value).has_value(); } diff --git a/test/test.cpp b/test/test.cpp index 7222dbf..1c3d87c 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -310,6 +310,8 @@ TEST_CASE("enum_value") { } TEST_CASE("enum_values") { + REQUIRE(std::is_same_v()), const std::array&>); + constexpr auto s1 = enum_values(); REQUIRE(s1 == std::array{{Color::RED, Color::GREEN, Color::BLUE}}); @@ -406,6 +408,8 @@ TEST_CASE("enum_name") { } TEST_CASE("enum_names") { + REQUIRE(std::is_same_v()), const std::array&>); + constexpr auto s1 = enum_names(); REQUIRE(s1 == std::array{{"RED", "GREEN", "BLUE"}}); @@ -420,6 +424,8 @@ TEST_CASE("enum_names") { } TEST_CASE("enum_entries") { + REQUIRE(std::is_same_v()), const std::array, 3>&>); + constexpr auto s1 = enum_entries(); REQUIRE(s1 == std::array, 3>{{{Color::RED, "RED"}, {Color::GREEN, "GREEN"}, {Color::BLUE, "BLUE"}}}); From 25912781ee84cb2e6d1f7d26a6600b9b43781f5f Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 4 Jul 2020 15:38:50 +0500 Subject: [PATCH 03/18] wip --- include/magic_enum.hpp | 172 +++++++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 58 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 983f54f..852d531 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -107,6 +107,18 @@ struct supported : std::false_type {}; #endif +template +struct has_min : std::false_type {}; + +template +struct has_min::min)>> : std::true_type {}; + +template +struct has_max : std::false_type {}; + +template +struct has_max::max)>> : std::true_type {}; + template inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; @@ -244,75 +256,116 @@ constexpr bool is_valid() noexcept { return n(V)>().size() != 0; } -template -constexpr std::size_t range_size() noexcept { - static_assert(is_enum_v, "magic_enum::detail::range_size requires enum type."); - constexpr auto size = Max - Min + 1; - static_assert(size > 0, "magic_enum::enum_range requires valid size."); - static_assert(size < (std::numeric_limits::max)(), "magic_enum::enum_range requires valid size."); - - return static_cast(size); -} - -template -constexpr int reflected_min() noexcept { +template > +constexpr auto reflected_min() noexcept { static_assert(is_enum_v, "magic_enum::detail::reflected_min requires enum type."); - constexpr auto lhs = enum_range::min; - static_assert(lhs > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); - constexpr auto rhs = (std::numeric_limits>::min)(); - return cmp_less(lhs, rhs) ? rhs : lhs; + if constexpr (IsFlags) { + return 0; + } else { + static_assert(has_min::value, "magic_enum::enum_range requires min."); + constexpr auto lhs = enum_range::min; + static_assert(lhs > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); + constexpr auto rhs = (std::numeric_limits::min)(); + + return cmp_less(lhs, rhs) ? rhs : lhs; + } } -template -constexpr int reflected_max() noexcept { +template > +constexpr auto reflected_max() noexcept { static_assert(is_enum_v, "magic_enum::detail::reflected_max requires enum type."); - constexpr auto lhs = enum_range::max; - static_assert(lhs < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); - constexpr auto rhs = (std::numeric_limits>::max)(); - return cmp_less(lhs, rhs) ? lhs : rhs; + if constexpr (IsFlags) { + return (std::numeric_limits>::max)(); + } else { + static_assert(has_max::value, "magic_enum::enum_range requires max."); + constexpr auto lhs = enum_range::max; + static_assert(lhs < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); + constexpr auto rhs = (std::numeric_limits::max)(); + + return cmp_less(lhs, rhs) ? lhs : rhs; + } } -template -inline constexpr int reflected_min_v = reflected_min(); +template +inline constexpr auto reflected_min_v = reflected_min(); -template -inline constexpr int reflected_max_v = reflected_max(); +template +inline constexpr auto reflected_max_v = reflected_max(); -template +template > +constexpr auto value(std::size_t i) noexcept { + static_assert(is_enum_v, "magic_enum::detail::value requires enum type."); + + if constexpr (IsFlags) { + return static_cast(static_cast(0x1) << static_cast(i)); + } else { + return static_cast(static_cast(i) + O); + } +} + +template constexpr auto values(std::integer_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); - constexpr std::array valid{{is_valid>()...}}; - constexpr int count = ((valid[I] ? 1 : 0) + ...); + constexpr std::array valid{{is_valid(I)>()...}}; + constexpr std::size_t count = ((valid[I] ? 1 : 0) + ...); std::array values{}; - for (int i = 0, v = 0; v < count; ++i) { + for (std::size_t i = 0, v = 0; v < count; ++i) { if (valid[i]) { - values[v++] = static_cast(i + reflected_min_v); + values[v++] = value(i); } } return values; } -template -inline constexpr auto values_v = values(std::make_integer_sequence, reflected_max_v>()>{}); +template > +constexpr auto values() noexcept { + static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); -template > -using values_t = decltype((values_v)); + if constexpr (IsFlags) { + return values(std::make_integer_sequence::digits>{}); + } else { + constexpr auto range_size = reflected_max_v - reflected_min_v + 1; + static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "magic_enum::enum_range requires valid size."); + return values>(std::make_integer_sequence{}); + } +} -template -inline constexpr std::size_t count_v = values_v.size(); +template +inline constexpr auto values_v = values(); -template -inline constexpr int min_v = static_cast(values_v.front()); +template > +using values_t = decltype((values_v)); -template -inline constexpr int max_v = static_cast(values_v.back()); +template +inline constexpr auto count_v = values_v.size(); -template -inline constexpr std::size_t range_size_v = range_size, max_v>(); +template > +inline constexpr auto min_v = static_cast(values_v.front()); + +template > +inline constexpr auto max_v = static_cast(values_v.back()); + +template > +constexpr std::size_t range_size() noexcept { + static_assert(is_enum_v, "magic_enum::detail::range_size requires enum type."); + + if constexpr (IsFlags) { + return std::numeric_limits::digits; + } else { + constexpr int range_size = max_v - min_v + 1; + static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "magic_enum::enum_range requires valid size."); + return static_cast(range_size); + } +} + +template +inline constexpr auto range_size_v = range_size(); template using index_t = std::conditional_t < (std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; @@ -331,40 +384,40 @@ constexpr auto indexes(std::integer_sequence) noexcept { template inline constexpr auto indexes_v = indexes(std::make_integer_sequence>{}); -template +template constexpr auto names(std::index_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::names requires enum type."); - return std::array{{enum_name_v[I]>...}}; + return std::array{{enum_name_v[I]>...}}; } -template -inline constexpr auto names_v = names(std::make_index_sequence>{}); +template +inline constexpr auto names_v = names(std::make_index_sequence>{}); -template > -using names_t = decltype((names_v)); +template > +using names_t = decltype((names_v)); -template +template constexpr auto entries(std::index_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::entries requires enum type."); - return std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; + return std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; } -template -inline constexpr auto entries_v = entries(std::make_index_sequence>{}); +template +inline constexpr auto entries_v = entries(std::make_index_sequence>{}); -template > -using entries_t = decltype((entries_v)); +template > +using entries_t = decltype((entries_v)); -template -inline constexpr bool is_sparse_v = range_size_v != count_v; +template +inline constexpr bool is_sparse_v = range_size_v != count_v; template > constexpr int undex(U value) noexcept { static_assert(is_enum_v, "magic_enum::detail::undex requires enum type."); - if (const auto i = static_cast(value) - min_v; value >= static_cast(min_v) && value <= static_cast(max_v)) { + if (const auto i = static_cast(value) - static_cast(min_v); value >= min_v && value <= max_v) { if constexpr (is_sparse_v) { if (const auto idx = indexes_v[i]; idx != invalid_index_v) { return idx; @@ -685,6 +738,9 @@ constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t Date: Sun, 5 Jul 2020 01:29:21 +0500 Subject: [PATCH 04/18] wip --- example/CMakeLists.txt | 15 ++- example/enum_flag_example.cpp | 100 ++++++++++++++++ example/example.cpp | 24 ++-- include/magic_enum.hpp | 210 +++++++++++++++++++++++++++++++--- 4 files changed, 315 insertions(+), 34 deletions(-) create mode 100644 example/enum_flag_example.cpp diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 3c2a934..2a30686 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -9,8 +9,13 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") endif() endif() -add_executable(example example.cpp) -set_target_properties(example PROPERTIES CXX_EXTENSIONS OFF) -target_compile_features(example PRIVATE cxx_std_17) -target_compile_options(example PRIVATE ${OPTIONS}) -target_link_libraries(example PRIVATE ${CMAKE_PROJECT_NAME}) +function(make_example target) + add_executable(${target} ${target}.cpp) + set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) + target_compile_features(${target} PRIVATE cxx_std_17) + target_compile_options(${target} PRIVATE ${OPTIONS}) + target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME}) +endfunction() + +make_example(example) +make_example(enum_flag_example) diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp new file mode 100644 index 0000000..61eefdf --- /dev/null +++ b/example/enum_flag_example.cpp @@ -0,0 +1,100 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2020 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#include + +enum class AnimalFlags : std::uint64_t { HasClaws = 1 << 1, CanFly = 1 << 2, EatsFish = 1 << 20, Endangered = std::uint64_t{1} << 40 }; + +int main() { + // Enum variable to string name. + AnimalFlags f1 = AnimalFlags::CanFly; + auto f1_name = magic_enum::flag::enum_name(f1); + std::cout << f1_name << std::endl; // CanFly + + // String enum name sequence. + constexpr auto names = magic_enum::flag::enum_names(); + std::cout << "AnimalFlags names:"; + for (auto n : names) { + std::cout << " " << n; + } + std::cout << std::endl; + // AnimalFlags names: HasClaws CanFly EatsFish Endangered + +#if 0 + // String name to enum value. + auto f2 = magic_enum::flag::enum_cast("EatsFish"); + if (f2.has_value() && f2.value() == AnimalFlags::EatsFish) { + std::cout << "EatsFish = " << magic_enum::flag::enum_integer(f2.value()) << std::endl; // EatsFish = 4 + } + + // Integer value to enum value. + auto f3 = magic_enum::flag::enum_cast(8); + if (f3.has_value() && f3.value() == AnimalFlags::Endangered) { + std::cout << "Endangered = " << magic_enum::flag::enum_integer(f3.value()) << std::endl; // Endangered = 8 + } +#endif + + // Enum value to integer value. + auto f4_integer = magic_enum::flag::enum_integer(AnimalFlags::HasClaws); + if (f4_integer == static_cast>(AnimalFlags::HasClaws)) { + std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 2 + } + + using namespace magic_enum::flag::ostream_operators; // out-of-the-box ostream operator for all enums. +#if 0 + // ostream operator for enum. + std::cout << f1 << " " << f2 << " " << f3 << std::endl; // CanFly EatsFish Endangered +#endif + + // Number of enum values. + std::cout << "AnimalFlags enum size: " << magic_enum::flag::enum_count() << std::endl; // AnimalFlags enum size: 4 + + // Indexed access to enum value. + std::cout << "AnimalFlags[0] = " << magic_enum::flag::enum_value(0) << std::endl; // AnimalFlags[0] = HasClaws + + // Enum value sequence. + constexpr auto values = magic_enum::flag::enum_values(); + std::cout << "AnimalFlags values:"; + for (AnimalFlags f : values) { + std::cout << " " << f; // ostream operator for enum. + } + std::cout << std::endl; + // AnimalFlags sequence: HasClaws CanFly EatsFish Endangered + + using namespace magic_enum::flag::bitwise_operators; // out-of-the-box bitwise operators for all enums. + // Support operators: ~, |, &, ^, |=, &=, ^=. + AnimalFlags flag = AnimalFlags::HasClaws | AnimalFlags::CanFly; + std::cout << flag << std::endl; // HasClaws | CanFly + + // Enum pair (value enum, string enum name) sequence. + constexpr auto entries = magic_enum::flag::enum_entries(); + std::cout << "AnimalFlags entries:"; + for (auto e : entries) { + std::cout << " " << e.second << " = " << magic_enum::flag::enum_integer(e.first); + } + std::cout << std::endl; + // AnimalFlags entries: AnimalFlags entries: HasClaws = 2 CanFly = 4 EatsFish = 1048576 Endangered = 1099511627776 + + return 0; +} diff --git a/example/example.cpp b/example/example.cpp index 5e21bf8..d92e87b 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -39,9 +39,9 @@ int main() { std::cout << c1_name << std::endl; // RED // String enum name sequence. - constexpr auto color_names = magic_enum::enum_names(); + constexpr auto names = magic_enum::enum_names(); std::cout << "Color names:"; - for (auto n : color_names) { + for (auto n : names) { std::cout << " " << n; } std::cout << std::endl; @@ -60,9 +60,9 @@ int main() { } // Enum value to integer value. - auto color_integer = magic_enum::enum_integer(c1); - if (color_integer == to_integer(Color::RED)) { - std::cout << "RED = " << color_integer << std::endl; // RED = -10 + auto c4_integer = magic_enum::enum_integer(Color::RED); + if (c4_integer == static_cast>(Color::RED)) { + std::cout << "RED = " << c4_integer << std::endl; // RED = -10 } using namespace magic_enum::ostream_operators; // out-of-the-box ostream operator for all enums. @@ -76,9 +76,9 @@ int main() { std::cout << "Color[0] = " << magic_enum::enum_value(0) << std::endl; // Color[0] = RED // Enum value sequence. - constexpr auto colors = magic_enum::enum_values(); - std::cout << "Colors sequence:"; - for (Color c : colors) { + constexpr auto values = magic_enum::enum_values(); + std::cout << "Colors values:"; + for (Color c : values) { std::cout << " " << c; // ostream operator for enum. } std::cout << std::endl; @@ -87,8 +87,8 @@ int main() { enum class Flags { A = 1, B = 2, C = 4, D = 8 }; using namespace magic_enum::bitwise_operators; // out-of-the-box bitwise operators for all enums. // Support operators: ~, |, &, ^, |=, &=, ^=. - Flags flags = Flags::A | Flags::C; - std::cout << flags << std::endl; + Flags flag = Flags::A | Flags::C; + std::cout << flag << std::endl; enum color { red, green, blue }; @@ -103,9 +103,9 @@ int main() { static_assert(magic_enum::is_scoped_enum_v); // Enum pair (value enum, string enum name) sequence. - constexpr auto color_entries = magic_enum::enum_entries(); + constexpr auto entries = magic_enum::enum_entries(); std::cout << "Colors entries:"; - for (auto e : color_entries) { + for (auto e : entries) { std::cout << " " << e.second << " = " << static_cast(e.first); } std::cout << std::endl; diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 852d531..81e724f 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -277,7 +277,7 @@ constexpr auto reflected_max() noexcept { static_assert(is_enum_v, "magic_enum::detail::reflected_max requires enum type."); if constexpr (IsFlags) { - return (std::numeric_limits>::max)(); + return (std::numeric_limits::max)(); } else { static_assert(has_max::value, "magic_enum::enum_range requires max."); constexpr auto lhs = enum_range::max; @@ -299,7 +299,7 @@ constexpr auto value(std::size_t i) noexcept { static_assert(is_enum_v, "magic_enum::detail::value requires enum type."); if constexpr (IsFlags) { - return static_cast(static_cast(0x1) << static_cast(i)); + return static_cast(static_cast(1) << static_cast(i)); } else { return static_cast(static_cast(i) + O); } @@ -338,7 +338,7 @@ constexpr auto values() noexcept { template inline constexpr auto values_v = values(); -template > +template > using values_t = decltype((values_v)); template @@ -357,7 +357,7 @@ constexpr std::size_t range_size() noexcept { if constexpr (IsFlags) { return std::numeric_limits::digits; } else { - constexpr int range_size = max_v - min_v + 1; + constexpr auto range_size = max_v - min_v + 1; static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); static_assert(range_size < (std::numeric_limits::max)(), "magic_enum::enum_range requires valid size."); return static_cast(range_size); @@ -365,7 +365,7 @@ constexpr std::size_t range_size() noexcept { } template -inline constexpr auto range_size_v = range_size(); +inline constexpr auto range_size_v = range_size(); template using index_t = std::conditional_t < (std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; @@ -392,22 +392,22 @@ constexpr auto names(std::index_sequence) noexcept { } template -inline constexpr auto names_v = names(std::make_index_sequence>{}); +inline constexpr auto names_v = names(std::make_index_sequence>{}); -template > +template > using names_t = decltype((names_v)); template constexpr auto entries(std::index_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::entries requires enum type."); - return std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; + return std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; } template inline constexpr auto entries_v = entries(std::make_index_sequence>{}); -template > +template > using entries_t = decltype((entries_v)); template @@ -430,26 +430,37 @@ constexpr int undex(U value) noexcept { return -1; // Value out of range. } -template +template > constexpr int endex(E value) noexcept { static_assert(is_enum_v, "magic_enum::detail::endex requires enum type."); - return undex(static_cast>(value)); + return undex(static_cast(value)); } -template +template struct enable_if_enum {}; template -struct enable_if_enum { +struct enable_if_enum { using type = R; using D = std::decay_t; static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(count_v > 0, "magic_enum requires enum implementation and valid max and min."); + static_assert(count_v > 0, "magic_enum requires enum implementation and valid max and min."); +}; + +template +struct enable_if_enum { + using type = R; + using D = std::decay_t; + static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); + static_assert(count_v > 0, "magic_enum::flag requires enum flag implementation."); }; template -using enable_if_enum_t = typename enable_if_enum>, T, R>::type; +using enable_if_enum_t = typename enable_if_enum>, false, T, R>::type; + +template +using enable_if_enum_flags_t = typename enable_if_enum>, true, T, R>::type; template >>> using enum_concept = T; @@ -532,7 +543,7 @@ template if constexpr (detail::is_sparse_v) { return assert(index < detail::count_v), detail::values_v[index]; } else { - return assert(index < detail::count_v), static_cast(index + detail::min_v); + return assert(index < detail::count_v), detail::value>(index); } } @@ -738,9 +749,174 @@ constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + + return detail::count_v; } +// Returns enum flag value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum values. +template +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_flags_t> { + using D = std::decay_t; + + if constexpr (detail::is_sparse_v) { + return assert((index < detail::count_v)), detail::values_v[index]; + } else { + return assert((index < detail::count_v)), detail::value(index); + } +} + +// Obtains value enum flag sequence. +// Returns std::array with enum flag values, sorted by enum flag value. +template +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_flags_t> { + using D = std::decay_t; + + return detail::values_v; +} + +// Returns string enum flag name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] auto enum_name(E value) -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + using U = std::underlying_type_t; + + std::string name; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { + const auto n = detail::names_v[i]; + if (!name.empty()) { + name.append(1, '|'); + } + name.append(n.data(), n.size()); + } + } + + return name; +} + +// Obtains string enum flag name sequence. +// Returns std::array with string enum flag names, sorted by enum flag value. +template +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_flags_t> { + using D = std::decay_t; + + return detail::names_v; +} + +// Obtains pair (value enum flag, string enum flag name) sequence. +// Returns std::array with std::pair (value enum flag, string enum flag name), sorted by enum flag value. +template +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_flags_t> { + using D = std::decay_t; + + return detail::entries_v; +} + +// TODO: enum_cast + +// Obtains enum flag value from integer value. +// Returns std::optional with enum flag value. +template +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t>> { + using D = std::decay_t; + using U = std::underlying_type_t; + + if (value < detail::min_v || value > detail::max_v) { + return std::nullopt; // Out of range. + } + + U check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { + check_value |= static_cast(v); + } + } + + if (check_value == value) { + return static_cast(value); + } + + return std::nullopt; // Invalid value. +} + +// Obtains enum flag value from enum flag string name. +// Returns std::optional with enum flag value. +template +[[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t>> { + static_assert(std::is_invocable_r_v, "magic_enum::flag::enum_cast requires bool(char, char) invocable predicate."); + static_assert(sizeof(E) == 0, "not implemented"); + return {}; +} + +// Obtains enum flag value from enum flag string name. +// Returns std::optional with enum flag value. +template +[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { + static_assert(sizeof(E) == 0, "not implemented"); + return {}; +} + +// Returns integer value from enum flag value. +template +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_flags_t> { + return static_cast>(value); +} + +// Obtains index in enum flag value sequence from enum flag value. +// Returns std::optional with index. +template +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { + static_assert(sizeof(E) == 0, "not implemented"); + return {}; +} + +// Checks whether enum flag contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + + return enum_cast(value).has_value(); +} + +// Checks whether enum flag contains enumerator with such value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + using U = std::underlying_type_t; + + return enum_cast(static_cast(value)).has_value(); +} + +// Checks whether enum flag contains enumerator with such string enum name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { + static_assert(sizeof(E) == 0, "not implemented"); + return {}; +} + +namespace ostream_operators { + +// TODO: operator<< +using namespace magic_enum::ostream_operators; + +} // namespace magic_enum::flag::ostream_operators + +namespace bitwise_operators { + +using namespace magic_enum::bitwise_operators; + +} // namespace magic_enum::flag::bitwise_operators + +} // namespace magic_enum::flag + } // namespace magic_enum #if defined(__clang__) From 604054df5ac9434e8aefbba48d88cff13f09c92c Mon Sep 17 00:00:00 2001 From: neargye Date: Sun, 5 Jul 2020 13:18:29 +0500 Subject: [PATCH 05/18] wip --- doc/reference.md | 10 +-- include/magic_enum.hpp | 146 ++++++++++++++++++++++++++--------------- 2 files changed, 99 insertions(+), 57 deletions(-) diff --git a/doc/reference.md b/doc/reference.md index cf253e6..7d2c1fe 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -88,7 +88,7 @@ template constexpr array enum_values() noexcept; ``` -* Returns `std::array` with all enum value where `N = number of enum values`, sorted by enum value. +* Returns `std::array` with all enum values where `N = number of enum values`, sorted by enum value. * Examples @@ -141,7 +141,7 @@ template constexpr string_view enum_name() noexcept; ``` -* Returns `std::string_view` with null-terminated string enum name from enum value. +* Returns `std::string_view` with null-terminated string name from enum value. * If enum value does not have name or [out of range](limitations.md), `enum_name(value)` returns empty string. * If enum value does not have name, `enum_name()` occurs the compilation error `"Enum value does not have a name."`. @@ -172,7 +172,7 @@ template constexpr array enum_names() noexcept; ``` -* Returns `std::array` with all string enum name where `N = number of enum values`, sorted by enum value. +* Returns `std::array` with all string names where `N = number of enum values`, sorted by enum value. * Examples @@ -189,7 +189,7 @@ template constexpr array, N> enum_entries() noexcept; ``` -* Returns `std::array, N>` with all `std::pair` (value enum, string enum name) where `N = number of enum values`, sorted by enum value. +* Returns `std::array, N>` with all pairs (enum value, string name) where `N = number of enum values`, sorted by enum value. * Examples @@ -207,7 +207,7 @@ template constexpr optional enum_index() noexcept; ``` -* Obtains index in enum value sequence from enum value. +* Obtains index in enum values from enum value. * Returns `std::optional` with index. diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 81e724f..0c7da1e 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -204,6 +204,31 @@ constexpr bool cmp_less(L lhs, R rhs) noexcept { } } +template +constexpr std::uint8_t log2(T value) noexcept { + if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; + + return log2(static_cast(value)); + } else { + auto ret = std::uint8_t{0}; + for (; value > static_cast(1U); value >>= static_cast(1U), ++ret) {}; + + return ret; + } +} + +template +constexpr bool is_pow2(T x) noexcept { + if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; + + return is_pow2(static_cast(x)); + } else { + return x != 0 && (x & (x - 1)) == 0; + } +} + #if defined(NEARGYE_NAMEOF_HPP) using ::nameof::detail::type_name_v; #else @@ -294,12 +319,12 @@ inline constexpr auto reflected_min_v = reflected_min(); template inline constexpr auto reflected_max_v = reflected_max(); -template > +template > constexpr auto value(std::size_t i) noexcept { static_assert(is_enum_v, "magic_enum::detail::value requires enum type."); if constexpr (IsFlags) { - return static_cast(static_cast(1) << static_cast(i)); + return static_cast(static_cast(1) << static_cast(i + O)); } else { return static_cast(static_cast(i) + O); } @@ -308,13 +333,13 @@ constexpr auto value(std::size_t i) noexcept { template constexpr auto values(std::integer_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); - constexpr std::array valid{{is_valid(I)>()...}}; + constexpr std::array valid{{is_valid(I)>()...}}; constexpr std::size_t count = ((valid[I] ? 1 : 0) + ...); std::array values{}; for (std::size_t i = 0, v = 0; v < count; ++i) { if (valid[i]) { - values[v++] = value(i); + values[v++] = value(i); } } @@ -376,7 +401,7 @@ inline constexpr auto invalid_index_v = (std::numeric_limits>::max)() template constexpr auto indexes(std::integer_sequence) noexcept { static_assert(is_enum_v, "magic_enum::detail::indexes requires enum type."); - [[maybe_unused]] index_t i = 0; + [[maybe_unused]] auto i = index_t{0}; return std::array, sizeof...(I)>{{(is_valid>() ? i++ : invalid_index_v)...}}; } @@ -410,6 +435,20 @@ inline constexpr auto entries_v = entries(std::make_index_sequence> using entries_t = decltype((entries_v)); +template > +constexpr bool is_sparse() noexcept { + static_assert(is_enum_v, "magic_enum::detail::is_sparse requires enum type."); + + if constexpr (IsFlags) { + auto range_count = std::size_t{0}; + for (auto i = max_v; i != max_v; i <<= static_cast(1), ++range_count) {}; + + return range_count != count_v; + } else { + return range_size_v != count_v; + } +} + template inline constexpr bool is_sparse_v = range_size_v != count_v; @@ -453,7 +492,7 @@ struct enable_if_enum { using type = R; using D = std::decay_t; static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(count_v > 0, "magic_enum::flag requires enum flag implementation."); + static_assert(count_v > 0, "magic_enum::flag requires enum-flags implementation."); }; template @@ -543,11 +582,10 @@ template if constexpr (detail::is_sparse_v) { return assert(index < detail::count_v), detail::values_v[index]; } else { - return assert(index < detail::count_v), detail::value>(index); + return assert(index < detail::count_v), detail::value>(index); } } -// Obtains value enum sequence. // Returns std::array with enum values, sorted by enum value. template [[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t> { @@ -556,7 +594,7 @@ template return detail::values_v; } -// Returns string enum name from static storage enum variable. +// Returns string name from static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. template [[nodiscard]] constexpr std::string_view enum_name() noexcept { @@ -568,7 +606,7 @@ template return name; } -// Returns string enum name from enum value. +// Returns string name from enum value. // If enum value does not have name or value out of range, returns empty string. template [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { @@ -581,8 +619,7 @@ template return {}; // Value out of range. } -// Obtains string enum name sequence. -// Returns std::array with string enum names, sorted by enum value. +// Returns std::array with string names, sorted by enum value. template [[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; @@ -590,8 +627,7 @@ template return detail::names_v; } -// Obtains pair (value enum, string enum name) sequence. -// Returns std::array with std::pair (value enum, string enum name), sorted by enum value. +// Returns std::array with pairs (enum value, string name), sorted by enum value. template [[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; @@ -599,7 +635,7 @@ template return detail::entries_v; } -// Obtains enum value from enum string name. +// Obtains enum value from string name. // Returns std::optional with enum value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { @@ -615,6 +651,8 @@ template return std::nullopt; // Invalid value or out of range. } +// Obtains enum value from string name. +// Returns std::optional with enum value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t>> { using D = std::decay_t; @@ -647,7 +685,7 @@ template return static_cast>(value); } -// Obtains index in enum value sequence from enum value. +// Obtains index in enum values from enum value. // Returns std::optional with index. template [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { @@ -660,7 +698,7 @@ template return std::nullopt; // Value out of range. } -// Checks whether enum contains enumerator with such value. +// Checks whether enum contains enumerator with such enum value. template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; @@ -676,7 +714,7 @@ template return detail::undex(value) != -1; } -// Checks whether enum contains enumerator with such string enum name. +// Checks whether enum contains enumerator with such string name. template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; @@ -751,7 +789,7 @@ constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t [[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; @@ -759,8 +797,8 @@ template return detail::count_v; } -// Returns enum flag value at specified index. -// No bounds checking is performed: the behavior is undefined if index >= number of enum values. +// Returns enum-flags value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum-flags values. template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -768,12 +806,13 @@ template if constexpr (detail::is_sparse_v) { return assert((index < detail::count_v)), detail::values_v[index]; } else { - return assert((index < detail::count_v)), detail::value(index); + constexpr auto min = detail::log2(detail::min_v) - 1; + + return assert((index < detail::count_v)), detail::value(index); } } -// Obtains value enum flag sequence. -// Returns std::array with enum flag values, sorted by enum flag value. +// Returns std::array with enum-flags values, sorted by enum-flags value. template [[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -781,8 +820,8 @@ template return detail::values_v; } -// Returns string enum flag name from enum value. -// If enum value does not have name or value out of range, returns empty string. +// Returns string name from enum-flags value. +// If enum-flags value does not have name or value out of range, returns empty string. template [[nodiscard]] auto enum_name(E value) -> detail::enable_if_enum_flags_t { using D = std::decay_t; @@ -802,8 +841,7 @@ template return name; } -// Obtains string enum flag name sequence. -// Returns std::array with string enum flag names, sorted by enum flag value. +// Returns std::array with string names, sorted by enum-flags value. template [[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -811,8 +849,7 @@ template return detail::names_v; } -// Obtains pair (value enum flag, string enum flag name) sequence. -// Returns std::array with std::pair (value enum flag, string enum flag name), sorted by enum flag value. +// Returns std::array with pairs (enum-flags value, string name), sorted by enum-flags value. template [[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; @@ -822,18 +859,14 @@ template // TODO: enum_cast -// Obtains enum flag value from integer value. -// Returns std::optional with enum flag value. +// Obtains enum-flags value from integer value. +// Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t>> { using D = std::decay_t; using U = std::underlying_type_t; - if (value < detail::min_v || value > detail::max_v) { - return std::nullopt; // Out of range. - } - - U check_value = U{0}; + auto check_value = U{0}; for (std::size_t i = 0; i < detail::count_v; ++i) { if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { check_value |= static_cast(v); @@ -844,11 +877,11 @@ template return static_cast(value); } - return std::nullopt; // Invalid value. + return std::nullopt; // Invalid value or out of range. } -// Obtains enum flag value from enum flag string name. -// Returns std::optional with enum flag value. +// Obtains enum-flags value from string name. +// Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t>> { static_assert(std::is_invocable_r_v, "magic_enum::flag::enum_cast requires bool(char, char) invocable predicate."); @@ -856,29 +889,38 @@ template return {}; } -// Obtains enum flag value from enum flag string name. -// Returns std::optional with enum flag value. +// Obtains enum-flags value from string name. +// Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { static_assert(sizeof(E) == 0, "not implemented"); return {}; } -// Returns integer value from enum flag value. +// Returns integer value from enum-flags value. template [[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_flags_t> { return static_cast>(value); } -// Obtains index in enum flag value sequence from enum flag value. +// Obtains index in enum-flags values from enum-flags value. // Returns std::optional with index. template [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { - static_assert(sizeof(E) == 0, "not implemented"); - return {}; + using D = std::decay_t; + + if (detail::is_pow2(value)) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (enum_value(i) == value) { + return i; + } + } + } + + return std::nullopt; // Value out of range. } -// Checks whether enum flag contains enumerator with such integer value. +// Checks whether enum-flags contains enumerator with such integer value. template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; @@ -886,7 +928,7 @@ template return enum_cast(value).has_value(); } -// Checks whether enum flag contains enumerator with such value. +// Checks whether enum-flags contains enumerator with such enum-flags value. template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; @@ -895,7 +937,7 @@ template return enum_cast(static_cast(value)).has_value(); } -// Checks whether enum flag contains enumerator with such string enum name. +// Checks whether enum-flags contains enumerator with such string name. template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { static_assert(sizeof(E) == 0, "not implemented"); @@ -907,15 +949,15 @@ namespace ostream_operators { // TODO: operator<< using namespace magic_enum::ostream_operators; -} // namespace magic_enum::flag::ostream_operators +} // namespace magic_enum::flags::ostream_operators namespace bitwise_operators { using namespace magic_enum::bitwise_operators; -} // namespace magic_enum::flag::bitwise_operators +} // namespace magic_enum::flags::bitwise_operators -} // namespace magic_enum::flag +} // namespace magic_enum::flags } // namespace magic_enum From 8e343c47b8cedc8a44011d2dc4d46e583f29b3e7 Mon Sep 17 00:00:00 2001 From: neargye Date: Sun, 5 Jul 2020 15:12:05 +0500 Subject: [PATCH 06/18] wip --- example/enum_flag_example.cpp | 50 ++++----- example/example.cpp | 4 +- include/magic_enum.hpp | 198 +++++++++++++++++++++------------- 3 files changed, 150 insertions(+), 102 deletions(-) diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp index 61eefdf..1cedd93 100644 --- a/example/enum_flag_example.cpp +++ b/example/enum_flag_example.cpp @@ -24,16 +24,16 @@ #include -enum class AnimalFlags : std::uint64_t { HasClaws = 1 << 1, CanFly = 1 << 2, EatsFish = 1 << 20, Endangered = std::uint64_t{1} << 40 }; +enum class AnimalFlags : std::uint64_t { HasClaws = 1 << 10, CanFly = 1 << 20, EatsFish = 1 << 30, Endangered = std::uint64_t{1} << 40 }; int main() { // Enum variable to string name. - AnimalFlags f1 = AnimalFlags::CanFly; - auto f1_name = magic_enum::flag::enum_name(f1); - std::cout << f1_name << std::endl; // CanFly + AnimalFlags f1 = AnimalFlags::Endangered; + auto f1_name = magic_enum::flags::enum_name(f1); + std::cout << f1_name << std::endl; // Endangered // String enum name sequence. - constexpr auto names = magic_enum::flag::enum_names(); + constexpr auto names = magic_enum::flags::enum_names(); std::cout << "AnimalFlags names:"; for (auto n : names) { std::cout << " " << n; @@ -43,38 +43,38 @@ int main() { #if 0 // String name to enum value. - auto f2 = magic_enum::flag::enum_cast("EatsFish"); + auto f2 = magic_enum::flags::enum_cast("EatsFish|CanFly"); +#else +auto f2 = magic_enum::flags::enum_cast(1074790400); +#endif if (f2.has_value() && f2.value() == AnimalFlags::EatsFish) { - std::cout << "EatsFish = " << magic_enum::flag::enum_integer(f2.value()) << std::endl; // EatsFish = 4 + std::cout << "EatsFish = " << magic_enum::flags::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400 } // Integer value to enum value. - auto f3 = magic_enum::flag::enum_cast(8); - if (f3.has_value() && f3.value() == AnimalFlags::Endangered) { - std::cout << "Endangered = " << magic_enum::flag::enum_integer(f3.value()) << std::endl; // Endangered = 8 + auto f3 = magic_enum::flags::enum_cast(1073742848); + if (f3.has_value()) { + std::cout << magic_enum::flags::enum_name(f3.value()) << " = " << magic_enum::flags::enum_integer(f3.value()) << std::endl; // HasClaws|EatsFish = 1073742848 } -#endif // Enum value to integer value. - auto f4_integer = magic_enum::flag::enum_integer(AnimalFlags::HasClaws); + auto f4_integer = magic_enum::flags::enum_integer(AnimalFlags::HasClaws); if (f4_integer == static_cast>(AnimalFlags::HasClaws)) { - std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 2 + std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 1024 } - using namespace magic_enum::flag::ostream_operators; // out-of-the-box ostream operator for all enums. -#if 0 + using namespace magic_enum::flags::ostream_operators; // out-of-the-box ostream operator for all enums. // ostream operator for enum. - std::cout << f1 << " " << f2 << " " << f3 << std::endl; // CanFly EatsFish Endangered -#endif + std::cout << f1 << " " << f2 << " " << f3 << std::endl; // Endangered CanFly|EatsFish HasClaws|EatsFish // Number of enum values. - std::cout << "AnimalFlags enum size: " << magic_enum::flag::enum_count() << std::endl; // AnimalFlags enum size: 4 + std::cout << "AnimalFlags enum size: " << magic_enum::flags::enum_count() << std::endl; // AnimalFlags enum size: 4 // Indexed access to enum value. - std::cout << "AnimalFlags[0] = " << magic_enum::flag::enum_value(0) << std::endl; // AnimalFlags[0] = HasClaws + std::cout << "AnimalFlags[0] = " << magic_enum::flags::enum_value(0) << std::endl; // AnimalFlags[0] = HasClaws // Enum value sequence. - constexpr auto values = magic_enum::flag::enum_values(); + constexpr auto values = magic_enum::flags::enum_values(); std::cout << "AnimalFlags values:"; for (AnimalFlags f : values) { std::cout << " " << f; // ostream operator for enum. @@ -82,19 +82,19 @@ int main() { std::cout << std::endl; // AnimalFlags sequence: HasClaws CanFly EatsFish Endangered - using namespace magic_enum::flag::bitwise_operators; // out-of-the-box bitwise operators for all enums. + using namespace magic_enum::flags::bitwise_operators; // out-of-the-box bitwise operators for all enums. // Support operators: ~, |, &, ^, |=, &=, ^=. AnimalFlags flag = AnimalFlags::HasClaws | AnimalFlags::CanFly; - std::cout << flag << std::endl; // HasClaws | CanFly + std::cout << flag << std::endl; // HasClaws|CanFly // Enum pair (value enum, string enum name) sequence. - constexpr auto entries = magic_enum::flag::enum_entries(); + constexpr auto entries = magic_enum::flags::enum_entries(); std::cout << "AnimalFlags entries:"; for (auto e : entries) { - std::cout << " " << e.second << " = " << magic_enum::flag::enum_integer(e.first); + std::cout << " " << e.second << " = " << magic_enum::flags::enum_integer(e.first); } std::cout << std::endl; - // AnimalFlags entries: AnimalFlags entries: HasClaws = 2 CanFly = 4 EatsFish = 1048576 Endangered = 1099511627776 + // AnimalFlags entries: AnimalFlags entries: HasClaws = 1024 CanFly = 1048576 EatsFish = 1073741824 Endangered = 1099511627776 return 0; } diff --git a/example/example.cpp b/example/example.cpp index d92e87b..c321ca1 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -70,7 +70,7 @@ int main() { std::cout << "Color: " << c1 << " " << c2 << " " << c3 << std::endl; // Color: RED BLUE GREEN // Number of enum values. - std::cout << "Color enum size: " << magic_enum::enum_count() << std::endl; // Color enum size: 3 + std::cout << "Color enum size: " << magic_enum::enum_count() << std::endl; // Color size: 3 // Indexed access to enum value. std::cout << "Color[0] = " << magic_enum::enum_value(0) << std::endl; // Color[0] = RED @@ -88,7 +88,7 @@ int main() { using namespace magic_enum::bitwise_operators; // out-of-the-box bitwise operators for all enums. // Support operators: ~, |, &, ^, |=, &=, ^=. Flags flag = Flags::A | Flags::C; - std::cout << flag << std::endl; + std::cout << flag << std::endl; // 5 enum color { red, green, blue }; diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 0c7da1e..0ba8ec4 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -282,7 +282,7 @@ constexpr bool is_valid() noexcept { } template > -constexpr auto reflected_min() noexcept { +constexpr int reflected_min() noexcept { static_assert(is_enum_v, "magic_enum::detail::reflected_min requires enum type."); if constexpr (IsFlags) { @@ -298,7 +298,7 @@ constexpr auto reflected_min() noexcept { } template > -constexpr auto reflected_max() noexcept { +constexpr int reflected_max() noexcept { static_assert(is_enum_v, "magic_enum::detail::reflected_max requires enum type."); if constexpr (IsFlags) { @@ -450,7 +450,7 @@ constexpr bool is_sparse() noexcept { } template -inline constexpr bool is_sparse_v = range_size_v != count_v; +inline constexpr bool is_sparse_v = is_sparse(); template > constexpr int undex(U value) noexcept { @@ -476,6 +476,18 @@ constexpr int endex(E value) noexcept { return undex(static_cast(value)); } +template > +constexpr U value_ors() noexcept { + static_assert(is_enum_v, "magic_enum::detail::endex requires enum type."); + + auto value = U{0}; + for (std::size_t i = 0; i < count_v; ++i) { + value |= static_cast(values_v[i]); + } + + return value; +} + template struct enable_if_enum {}; @@ -492,13 +504,13 @@ struct enable_if_enum { using type = R; using D = std::decay_t; static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); - static_assert(count_v > 0, "magic_enum::flag requires enum-flags implementation."); + static_assert(count_v > 0, "magic_enum::flags requires enum-flags implementation."); }; template using enable_if_enum_t = typename enable_if_enum>, false, T, R>::type; -template +template using enable_if_enum_flags_t = typename enable_if_enum>, true, T, R>::type; template >>> @@ -578,11 +590,12 @@ template template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; + constexpr auto count = detail::count_v; if constexpr (detail::is_sparse_v) { - return assert(index < detail::count_v), detail::values_v[index]; + return assert(index < count), detail::values_v[index]; } else { - return assert(index < detail::count_v), detail::value>(index); + return assert(index < count), detail::value>(index); } } @@ -681,7 +694,7 @@ template // Returns integer value from enum value. template -[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr underlying_type_t enum_integer(E value) noexcept { return static_cast>(value); } @@ -724,21 +737,26 @@ template namespace ostream_operators { -template -auto operator<<(std::basic_ostream& os, E value) -> detail::enable_if_enum_t&> { - if (const auto name = enum_name(value); !name.empty()) { +template > +auto& operator<<(std::basic_ostream& os, E value) { + using namespace magic_enum; + using D = std::decay_t; + + if (const auto name = enum_name(value); !name.empty()) { for (const auto c : name) { os.put(c); } } else { - os << enum_integer(value); + os << enum_integer(value); } return os; } -template -auto operator<<(std::basic_ostream& os, std::optional value) -> detail::enable_if_enum_t&> { +template > +auto& operator<<(std::basic_ostream& os, std::optional value) { + using namespace magic_enum; + if (value.has_value()) { os << value.value(); } @@ -750,44 +768,44 @@ auto operator<<(std::basic_ostream& os, std::optional value) -> namespace bitwise_operators { -template -constexpr auto operator~(E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator~(E rhs) noexcept { return static_cast(~static_cast>(rhs)); } -template -constexpr auto operator|(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator|(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) | static_cast>(rhs)); } -template -constexpr auto operator&(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator&(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) & static_cast>(rhs)); } -template -constexpr auto operator^(E lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E operator^(E lhs, E rhs) noexcept { return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } -template -constexpr auto operator|=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E& operator|=(E& lhs, E rhs) noexcept { return lhs = lhs | rhs; } -template -constexpr auto operator&=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E& operator&=(E& lhs, E rhs) noexcept { return lhs = lhs & rhs; } -template -constexpr auto operator^=(E& lhs, E rhs) noexcept -> detail::enable_if_enum_t { +template , int> = 0> +constexpr E& operator^=(E& lhs, E rhs) noexcept { return lhs = lhs ^ rhs; } } // namespace magic_enum::bitwise_operators -namespace flag { +namespace flags { // Returns number of enum-flags values. template @@ -802,13 +820,14 @@ template template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; + constexpr auto count = detail::count_v; - if constexpr (detail::is_sparse_v) { - return assert((index < detail::count_v)), detail::values_v[index]; + if constexpr (detail::is_sparse_v) { + return assert(index < count), detail::values_v[index]; } else { constexpr auto min = detail::log2(detail::min_v) - 1; - return assert((index < detail::count_v)), detail::value(index); + return assert(index < count), detail::value(index); } } @@ -857,23 +876,53 @@ template return detail::entries_v; } -// TODO: enum_cast +// Checks whether enum-flags contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + using U = std::underlying_type_t; + + if constexpr (detail::is_sparse_v) { + constexpr auto min = detail::min_v; + constexpr auto max = detail::value_ors(); + + return value >= min && value <= max; + } else { + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { + check_value |= v; + } + } + + return check_value == value; + } +} + +// Checks whether enum-flags contains enumerator with such enum-flags value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + using U = std::underlying_type_t; + + return enum_contains(static_cast(value)); +} + +// Checks whether enum-flags contains enumerator with such string name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { + // TODO: impl + static_assert(sizeof(E) == 0, "not implemented"); + return {}; +} // Obtains enum-flags value from integer value. // Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t>> { using D = std::decay_t; - using U = std::underlying_type_t; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { - check_value |= static_cast(v); - } - } - - if (check_value == value) { + if (enum_contains(value)) { return static_cast(value); } @@ -884,7 +933,8 @@ template // Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t>> { - static_assert(std::is_invocable_r_v, "magic_enum::flag::enum_cast requires bool(char, char) invocable predicate."); + static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_cast requires bool(char, char) invocable predicate."); + // TODO: impl static_assert(sizeof(E) == 0, "not implemented"); return {}; } @@ -893,20 +943,15 @@ template // Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { + // TODO: impl static_assert(sizeof(E) == 0, "not implemented"); return {}; } -// Returns integer value from enum-flags value. -template -[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_flags_t> { - return static_cast>(value); -} - // Obtains index in enum-flags values from enum-flags value. // Returns std::optional with index. template -[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; if (detail::is_pow2(value)) { @@ -920,34 +965,37 @@ template return std::nullopt; // Value out of range. } -// Checks whether enum-flags contains enumerator with such integer value. -template -[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { - using D = std::decay_t; - - return enum_cast(value).has_value(); -} - -// Checks whether enum-flags contains enumerator with such enum-flags value. -template -[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { - using D = std::decay_t; - using U = std::underlying_type_t; - - return enum_cast(static_cast(value)).has_value(); -} - -// Checks whether enum-flags contains enumerator with such string name. -template -[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { - static_assert(sizeof(E) == 0, "not implemented"); - return {}; -} +using magic_enum::enum_type_name; // TODO: impl +using magic_enum::enum_integer; // TODO: impl namespace ostream_operators { -// TODO: operator<< -using namespace magic_enum::ostream_operators; +template > +auto& operator<<(std::basic_ostream& os, E value) { + using namespace magic_enum::flags; + using D = std::decay_t; + + if (const auto name = enum_name(value); !name.empty()) { + for (const auto c : name) { + os.put(c); + } + } else { + os << enum_integer(value); + } + + return os; +} + +template > +auto& operator<<(std::basic_ostream& os, std::optional value) { + using namespace magic_enum::flags; + + if (value.has_value()) { + os << value.value(); + } + + return os; +} } // namespace magic_enum::flags::ostream_operators From c5894cf573efb3d3ed945cbb6b9086748cf25825 Mon Sep 17 00:00:00 2001 From: neargye Date: Sun, 5 Jul 2020 15:55:24 +0500 Subject: [PATCH 07/18] wip --- include/magic_enum.hpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 0ba8ec4..30d172d 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -326,7 +326,7 @@ constexpr auto value(std::size_t i) noexcept { if constexpr (IsFlags) { return static_cast(static_cast(1) << static_cast(i + O)); } else { - return static_cast(static_cast(i) + O); + return static_cast(i + O); } } @@ -456,7 +456,7 @@ template > constexpr int undex(U value) noexcept { static_assert(is_enum_v, "magic_enum::detail::undex requires enum type."); - if (const auto i = static_cast(value) - static_cast(min_v); value >= min_v && value <= max_v) { + if (const auto i = static_cast(value - min_v); value >= min_v && value <= max_v) { if constexpr (is_sparse_v) { if (const auto idx = indexes_v[i]; idx != invalid_index_v) { return idx; @@ -613,7 +613,7 @@ template [[nodiscard]] constexpr std::string_view enum_name() noexcept { using D = std::decay_t; static_assert(std::is_enum_v, "Requires enum type."); - constexpr std::string_view name = detail::enum_name_v, V>; + constexpr std::string_view name = detail::enum_name_v; static_assert(name.size() > 0, "Enum value does not have a name."); return name; @@ -737,10 +737,11 @@ template namespace ostream_operators { -template > +template , int> = 0> auto& operator<<(std::basic_ostream& os, E value) { - using namespace magic_enum; +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED using D = std::decay_t; + using namespace magic_enum; if (const auto name = enum_name(value); !name.empty()) { for (const auto c : name) { @@ -749,14 +750,14 @@ auto& operator<<(std::basic_ostream& os, E value) { } else { os << enum_integer(value); } - +#else + os << static_cast>(value); +#endif return os; } -template > +template , int> = 0> auto& operator<<(std::basic_ostream& os, std::optional value) { - using namespace magic_enum; - if (value.has_value()) { os << value.value(); } @@ -970,7 +971,7 @@ using magic_enum::enum_integer; // TODO: impl namespace ostream_operators { -template > +template = 0> auto& operator<<(std::basic_ostream& os, E value) { using namespace magic_enum::flags; using D = std::decay_t; @@ -986,10 +987,8 @@ auto& operator<<(std::basic_ostream& os, E value) { return os; } -template > +template = 0> auto& operator<<(std::basic_ostream& os, std::optional value) { - using namespace magic_enum::flags; - if (value.has_value()) { os << value.value(); } From cbbb86d2d1df5d9bd0c4cae0495add029fd52350 Mon Sep 17 00:00:00 2001 From: neargye Date: Sun, 5 Jul 2020 16:37:24 +0500 Subject: [PATCH 08/18] wip --- include/magic_enum.hpp | 54 +++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 30d172d..5ceeb54 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -49,10 +49,8 @@ #if defined(__clang__) # pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wsign-conversion" // Implicit conversion changes signedness: 'int' to 'size_t'. #elif defined(__GNUC__) # pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wsign-conversion" // Implicit conversion changes signedness: 'int' to 'size_t'. #elif defined(_MSC_VER) # pragma warning(push) # pragma warning(disable : 26495) // Variable 'static_string::chars' is uninitialized. @@ -293,7 +291,11 @@ constexpr int reflected_min() noexcept { static_assert(lhs > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); constexpr auto rhs = (std::numeric_limits::min)(); - return cmp_less(lhs, rhs) ? rhs : lhs; + if constexpr (cmp_less(lhs, rhs)) { + return rhs; + } else { + return lhs; + } } } @@ -309,7 +311,11 @@ constexpr int reflected_max() noexcept { static_assert(lhs < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); constexpr auto rhs = (std::numeric_limits::max)(); - return cmp_less(lhs, rhs) ? lhs : rhs; + if constexpr (cmp_less(lhs, rhs)) { + return lhs; + } else { + return rhs; + } } } @@ -320,13 +326,13 @@ template inline constexpr auto reflected_max_v = reflected_max(); template > -constexpr auto value(std::size_t i) noexcept { +constexpr E value(std::size_t i) noexcept { static_assert(is_enum_v, "magic_enum::detail::value requires enum type."); if constexpr (IsFlags) { - return static_cast(static_cast(1) << static_cast(i + O)); + return static_cast(static_cast(1) << static_cast(static_cast(i) + O)); } else { - return static_cast(i + O); + return static_cast(static_cast(i) + O); } } @@ -453,10 +459,10 @@ template inline constexpr bool is_sparse_v = is_sparse(); template > -constexpr int undex(U value) noexcept { +constexpr std::size_t undex(U value) noexcept { static_assert(is_enum_v, "magic_enum::detail::undex requires enum type."); - if (const auto i = static_cast(value - min_v); value >= min_v && value <= max_v) { + if (const auto i = static_cast(value - min_v); value >= min_v && value <= max_v) { if constexpr (is_sparse_v) { if (const auto idx = indexes_v[i]; idx != invalid_index_v) { return idx; @@ -466,11 +472,11 @@ constexpr int undex(U value) noexcept { } } - return -1; // Value out of range. + return invalid_index_v; // Value out of range. } template > -constexpr int endex(E value) noexcept { +constexpr std::size_t endex(E value) noexcept { static_assert(is_enum_v, "magic_enum::detail::endex requires enum type."); return undex(static_cast(value)); @@ -590,12 +596,11 @@ template template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; - constexpr auto count = detail::count_v; if constexpr (detail::is_sparse_v) { - return assert(index < count), detail::values_v[index]; + return assert((index < detail::count_v)), detail::values_v[index]; } else { - return assert(index < count), detail::value>(index); + return assert((index < detail::count_v)), detail::value>(index); } } @@ -625,7 +630,7 @@ template [[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - if (const auto i = detail::endex(value); i != -1) { + if (const auto i = detail::endex(value); i != detail::invalid_index_v) { return detail::names_v[i]; } @@ -685,7 +690,7 @@ template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { using D = std::decay_t; - if (detail::undex(value) != -1) { + if (detail::undex(value) != detail::invalid_index_v) { return static_cast(value); } @@ -704,7 +709,7 @@ template [[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { using D = std::decay_t; - if (const auto i = detail::endex(value); i != -1) { + if (const auto i = detail::endex(value); i != detail::invalid_index_v) { return i; } @@ -716,7 +721,7 @@ template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - return detail::endex(value) != -1; + return detail::endex(value) != detail::invalid_index_v; } // Checks whether enum contains enumerator with such integer value. @@ -724,7 +729,7 @@ template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; - return detail::undex(value) != -1; + return detail::undex(value) != detail::invalid_index_v; } // Checks whether enum contains enumerator with such string name. @@ -821,14 +826,13 @@ template template [[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_flags_t> { using D = std::decay_t; - constexpr auto count = detail::count_v; if constexpr (detail::is_sparse_v) { - return assert(index < count), detail::values_v[index]; + return assert((index < detail::count_v)), detail::values_v[index]; } else { constexpr auto min = detail::log2(detail::min_v) - 1; - return assert(index < count), detail::value(index); + return assert((index < detail::count_v)), detail::value(index); } } @@ -913,7 +917,7 @@ template template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { // TODO: impl - static_assert(sizeof(E) == 0, "not implemented"); + static_assert(sizeof(decltype(value)) == sizeof(E) * 0, "not implemented"); return {}; } @@ -936,7 +940,7 @@ template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t>> { static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_cast requires bool(char, char) invocable predicate."); // TODO: impl - static_assert(sizeof(E) == 0, "not implemented"); + static_assert(sizeof(decltype(value)) + sizeof(decltype(p)) == sizeof(E) * 0, "not implemented"); return {}; } @@ -945,7 +949,7 @@ template template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { // TODO: impl - static_assert(sizeof(E) == 0, "not implemented"); + static_assert(sizeof(decltype(value)) == sizeof(E) * 0, "not implemented"); return {}; } From 13eff83de55a41786aa06828403f9c8b371c2464 Mon Sep 17 00:00:00 2001 From: neargye Date: Sun, 5 Jul 2020 17:13:25 +0500 Subject: [PATCH 09/18] wip --- example/enum_flag_example.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp index 1cedd93..d2eaf79 100644 --- a/example/enum_flag_example.cpp +++ b/example/enum_flag_example.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include From 573bc60011048f4cce5d58cd33b5cae8c80a9397 Mon Sep 17 00:00:00 2001 From: neargye Date: Mon, 6 Jul 2020 19:22:53 +0500 Subject: [PATCH 10/18] wip --- doc/limitations.md | 5 ++--- include/magic_enum.hpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/limitations.md b/doc/limitations.md index 20a0301..c682bca 100644 --- a/doc/limitations.md +++ b/doc/limitations.md @@ -38,7 +38,7 @@ } // namespace magic_enum ``` -* `magic_enum` won't work if a value is aliased, work with enum-aliases is compiler-implementation-defined. +* `magic_enum` won't work if a value is aliased. Work with enum-aliases is compiler-implementation-defined. ```cpp enum ShapeKind { @@ -80,8 +80,7 @@ // magic_enum::enum_name(ShapeKind::ConvexBegin) -> "Box" ``` - On some compiler enum-aliases not supported: - * + On some compiler enum-aliases not supported, [for example Visual Studio 2017](https://github.com/Neargye/magic_enum/issues/36). * If you hit a message like this: diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 5ceeb54..d20520e 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -210,7 +210,7 @@ constexpr std::uint8_t log2(T value) noexcept { return log2(static_cast(value)); } else { auto ret = std::uint8_t{0}; - for (; value > static_cast(1U); value >>= static_cast(1U), ++ret) {}; + for (; value > static_cast(1); value >>= static_cast(1), ++ret) {}; return ret; } From bcbabba3e4cdda76107dc4a3e789f1e5c0c785aa Mon Sep 17 00:00:00 2001 From: neargye Date: Wed, 8 Jul 2020 21:14:43 +0500 Subject: [PATCH 11/18] wip --- include/magic_enum.hpp | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index d20520e..6fb6d12 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -105,18 +105,6 @@ struct supported : std::false_type {}; #endif -template -struct has_min : std::false_type {}; - -template -struct has_min::min)>> : std::true_type {}; - -template -struct has_max : std::false_type {}; - -template -struct has_max::max)>> : std::true_type {}; - template inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; @@ -286,7 +274,6 @@ constexpr int reflected_min() noexcept { if constexpr (IsFlags) { return 0; } else { - static_assert(has_min::value, "magic_enum::enum_range requires min."); constexpr auto lhs = enum_range::min; static_assert(lhs > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); constexpr auto rhs = (std::numeric_limits::min)(); @@ -306,7 +293,6 @@ constexpr int reflected_max() noexcept { if constexpr (IsFlags) { return (std::numeric_limits::max)(); } else { - static_assert(has_max::value, "magic_enum::enum_range requires max."); constexpr auto lhs = enum_range::max; static_assert(lhs < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); constexpr auto rhs = (std::numeric_limits::max)(); @@ -744,8 +730,8 @@ namespace ostream_operators { template , int> = 0> auto& operator<<(std::basic_ostream& os, E value) { -#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED using D = std::decay_t; +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED using namespace magic_enum; if (const auto name = enum_name(value); !name.empty()) { @@ -756,7 +742,7 @@ auto& operator<<(std::basic_ostream& os, E value) { os << enum_integer(value); } #else - os << static_cast>(value); + os << static_cast(value); #endif return os; } @@ -885,14 +871,9 @@ template template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; - using U = std::underlying_type_t; if constexpr (detail::is_sparse_v) { - constexpr auto min = detail::min_v; - constexpr auto max = detail::value_ors(); - - return value >= min && value <= max; - } else { + using U = std::underlying_type_t; auto check_value = U{0}; for (std::size_t i = 0; i < detail::count_v; ++i) { if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { @@ -901,6 +882,11 @@ template } return check_value == value; + } else { + constexpr auto min = detail::min_v; + constexpr auto max = detail::value_ors(); + + return value >= min && value <= max; } } From 0c0b03131a82abc5e028dc2ac9c1ae3a17c6052f Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 15 Aug 2020 18:09:27 +0500 Subject: [PATCH 12/18] wip --- include/magic_enum.hpp | 114 ++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 37 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 6fb6d12..444c215 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -158,20 +158,31 @@ constexpr std::string_view pretty_name(std::string_view name) noexcept { return {}; // Invalid name. } +struct char_equal_to { + constexpr bool operator()(char lhs, char rhs) const noexcept { + return lhs == rhs; + } +}; + template constexpr bool cmp_equal(std::string_view lhs, std::string_view rhs, BinaryPredicate&& p) noexcept(std::is_nothrow_invocable_r_v) { - if (lhs.size() != rhs.size()) { - return false; - } - - const auto size = lhs.size(); - for (std::size_t i = 0; i < size; ++i) { - if (!p(lhs[i], rhs[i])) { + if constexpr (std::is_same_v) { + static_cast(p); + return lhs == rhs; + } else { + if (lhs.size() != rhs.size()) { return false; } - } - return true; + const auto size = lhs.size(); + for (std::size_t i = 0; i < size; ++i) { + if (!p(lhs[i], rhs[i])) { + return false; + } + } + + return true; + } } template @@ -620,7 +631,7 @@ template return detail::names_v[i]; } - return {}; // Value out of range. + return {}; // Invalid value or out of range. } // Returns std::array with string names, sorted by enum value. @@ -661,13 +672,7 @@ template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t>> { using D = std::decay_t; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (value == detail::names_v[i]) { - return enum_value(i); - } - } - - return std::nullopt; // Invalid value or out of range. + return enum_cast(value, detail::char_equal_to{}); } // Obtains enum value from integer value. @@ -729,7 +734,7 @@ template namespace ostream_operators { template , int> = 0> -auto& operator<<(std::basic_ostream& os, E value) { +std::basic_ostream& operator<<(std::basic_ostream& os, E value) { using D = std::decay_t; #if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED using namespace magic_enum; @@ -738,17 +743,14 @@ auto& operator<<(std::basic_ostream& os, E value) { for (const auto c : name) { os.put(c); } - } else { - os << enum_integer(value); + return os; } -#else - os << static_cast(value); #endif - return os; + return os << static_cast(value); } template , int> = 0> -auto& operator<<(std::basic_ostream& os, std::optional value) { +std::basic_ostream& operator<<(std::basic_ostream& os, std::optional value) { if (value.has_value()) { os << value.value(); } @@ -835,11 +837,13 @@ template template [[nodiscard]] auto enum_name(E value) -> detail::enable_if_enum_flags_t { using D = std::decay_t; - using U = std::underlying_type_t; + using U = underlying_type_t; std::string name; + auto check_value = U{0}; for (std::size_t i = 0; i < detail::count_v; ++i) { if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { + check_value |= static_cast(v); const auto n = detail::names_v[i]; if (!name.empty()) { name.append(1, '|'); @@ -848,7 +852,11 @@ template } } - return name; + if (check_value == static_cast(value)) { + return name; + } + + return {}; // Invalid value or out of range. } // Returns std::array with string names, sorted by enum-flags value. @@ -873,7 +881,7 @@ template using D = std::decay_t; if constexpr (detail::is_sparse_v) { - using U = std::underlying_type_t; + using U = underlying_type_t; auto check_value = U{0}; for (std::size_t i = 0; i < detail::count_v; ++i) { if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { @@ -894,7 +902,7 @@ template template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; - using U = std::underlying_type_t; + using U = underlying_type_t; return enum_contains(static_cast(value)); } @@ -962,23 +970,22 @@ using magic_enum::enum_integer; // TODO: impl namespace ostream_operators { template = 0> -auto& operator<<(std::basic_ostream& os, E value) { - using namespace magic_enum::flags; +std::basic_ostream& operator<<(std::basic_ostream& os, E value) { using D = std::decay_t; - +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED + using namespace magic_enum::flags; if (const auto name = enum_name(value); !name.empty()) { for (const auto c : name) { os.put(c); } - } else { - os << enum_integer(value); + return os; } - - return os; +#endif + return os << static_cast(value); } template = 0> -auto& operator<<(std::basic_ostream& os, std::optional value) { +std::basic_ostream& operator<<(std::basic_ostream& os, std::optional value) { if (value.has_value()) { os << value.value(); } @@ -990,7 +997,40 @@ auto& operator<<(std::basic_ostream& os, std::optional value) { namespace bitwise_operators { -using namespace magic_enum::bitwise_operators; +template = 0> +constexpr E operator~(E rhs) noexcept { + return static_cast(~static_cast>(rhs)); +} + +template = 0> +constexpr E operator|(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +template = 0> +constexpr E operator&(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +template = 0> +constexpr E operator^(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +template = 0> +constexpr E& operator|=(E& lhs, E rhs) noexcept { + return lhs = lhs | rhs; +} + +template = 0> +constexpr E& operator&=(E& lhs, E rhs) noexcept { + return lhs = lhs & rhs; +} + +template = 0> +constexpr E& operator^=(E& lhs, E rhs) noexcept { + return lhs = lhs ^ rhs; +} } // namespace magic_enum::flags::bitwise_operators From 619164687f3cfa479a4ee677310ce2a412530559 Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 15 Aug 2020 18:52:41 +0500 Subject: [PATCH 13/18] wip --- include/magic_enum.hpp | 175 ++++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 79 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 444c215..dad0d7b 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -74,40 +74,15 @@ # define MAGIC_ENUM_RANGE_MAX 128 #endif -namespace magic_enum { +#if !defined(NEARGYE_DETAIL_N) +# define NEARGYE_DETAIL_N 1 +namespace neargye::detail { -// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. -// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. -// If need another range for specific enum type, add specialization enum_range for necessary enum type. -template -struct enum_range { - static_assert(std::is_enum_v, "magic_enum::enum_range requires enum type."); - inline static constexpr int min = MAGIC_ENUM_RANGE_MIN; - inline static constexpr int max = MAGIC_ENUM_RANGE_MAX; - static_assert(max > min, "magic_enum::enum_range requires max > min."); +template +struct identity { + using type = T; }; -static_assert(MAGIC_ENUM_RANGE_MIN <= 0, "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); -static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "MAGIC_ENUM_RANGE_MIN must be greater than INT16_MIN."); - -static_assert(MAGIC_ENUM_RANGE_MAX > 0, "MAGIC_ENUM_RANGE_MAX must be greater than 0."); -static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE_MAX must be less than INT16_MAX."); - -static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); - -namespace detail { - -template -struct supported -#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template -inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; - template struct static_string { constexpr explicit static_string(std::string_view str) noexcept : static_string{str, std::make_index_sequence{}} { @@ -138,7 +113,7 @@ struct static_string<0> { constexpr operator std::string_view() const noexcept { return {}; } }; -constexpr std::string_view pretty_name(std::string_view name) noexcept { +constexpr std::string_view enum_name(std::string_view name) noexcept { for (std::size_t i = name.size(); i > 0; --i) { if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || (name[i - 1] >= 'a' && name[i - 1] <= 'z') || @@ -158,6 +133,91 @@ constexpr std::string_view pretty_name(std::string_view name) noexcept { return {}; // Invalid name. } +template +constexpr auto n() noexcept { +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && _MSC_VER >= 1910 +# if defined(__clang__) + constexpr std::string_view name{__PRETTY_FUNCTION__ + 32, sizeof(__PRETTY_FUNCTION__) - 35}; +# elif defined(__GNUC__) + constexpr std::string_view name{__PRETTY_FUNCTION__ + 47, sizeof(__PRETTY_FUNCTION__) - 50}; +# elif defined(_MSC_VER) + constexpr std::string_view name{__FUNCSIG__ + 65, sizeof(__FUNCSIG__) - 83 - (__FUNCSIG__[sizeof(__FUNCSIG__) - 19] == ' ' ? 1 : 0)}; +# endif + + return static_string{name}; +#else + return std::string_view{}; // Unsupported compiler. +#endif +} + +#if defined(_MSC_VER) +template > +#else +template +#endif +inline constexpr auto type_name_v = n(); + +template +constexpr auto n() noexcept { + static_assert(std::is_enum_v && std::is_same_v>); +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 +# if defined(__clang__) || defined(__GNUC__) + constexpr auto name = enum_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); +# elif defined(_MSC_VER) + constexpr auto name = enum_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); +# endif + return static_string{name}; +#else + return std::string_view{}; // Unsupported compiler. +#endif +} + +template +inline constexpr auto enum_name_v = n(); + +} // namespace neargye::detail +#endif + +namespace magic_enum { + +// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. +// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. +// If need another range for specific enum type, add specialization enum_range for necessary enum type. +template +struct enum_range { + static_assert(std::is_enum_v, "magic_enum::enum_range requires enum type."); + inline static constexpr int min = MAGIC_ENUM_RANGE_MIN; + inline static constexpr int max = MAGIC_ENUM_RANGE_MAX; + static_assert(max > min, "magic_enum::enum_range requires max > min."); +}; + +static_assert(MAGIC_ENUM_RANGE_MIN <= 0, "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); +static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "MAGIC_ENUM_RANGE_MIN must be greater than INT16_MIN."); + +static_assert(MAGIC_ENUM_RANGE_MAX > 0, "MAGIC_ENUM_RANGE_MAX must be greater than 0."); +static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE_MAX must be less than INT16_MAX."); + +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); + +namespace detail { + +using ::neargye::detail::identity; +using ::neargye::detail::static_string; +using ::neargye::detail::n; +using ::neargye::detail::type_name_v; +using ::neargye::detail::enum_name_v; + +template +struct supported +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; + struct char_equal_to { constexpr bool operator()(char lhs, char rhs) const noexcept { return lhs == rhs; @@ -226,51 +286,6 @@ constexpr bool is_pow2(T x) noexcept { } } -#if defined(NEARGYE_NAMEOF_HPP) -using ::nameof::detail::type_name_v; -#else -template -constexpr auto n() noexcept { -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && _MSC_VER >= 1910 -# if defined(__clang__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 35, sizeof(__PRETTY_FUNCTION__) - 38}; -# elif defined(__GNUC__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 50, sizeof(__PRETTY_FUNCTION__) - 53}; -# elif defined(_MSC_VER) - constexpr std::string_view name{__FUNCSIG__ + 40, sizeof(__FUNCSIG__) - 57}; -# endif - return static_string{name}; -#else - return std::string_view{}; // Unsupported compiler. -#endif -} - -template -inline constexpr auto type_name_v = n(); -#endif - -#if defined(NEARGYE_NAMEOF_HPP) -using ::nameof::detail::enum_name_v; -#else -template -constexpr auto n() noexcept { - static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); -#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED -# if defined(__clang__) || defined(__GNUC__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); -# elif defined(_MSC_VER) - constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); -# endif - return static_string{name}; -#else - return std::string_view{}; // Unsupported compiler. -#endif -} - -template -inline constexpr auto enum_name_v = n(); -#endif - template constexpr bool is_valid() noexcept { static_assert(is_enum_v, "magic_enum::detail::is_valid requires enum type."); @@ -736,6 +751,7 @@ namespace ostream_operators { template , int> = 0> std::basic_ostream& operator<<(std::basic_ostream& os, E value) { using D = std::decay_t; + using U = underlying_type_t; #if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED using namespace magic_enum; @@ -746,7 +762,7 @@ std::basic_ostream& operator<<(std::basic_ostream& o return os; } #endif - return os << static_cast(value); + return os << static_cast(value); } template , int> = 0> @@ -972,6 +988,7 @@ namespace ostream_operators { template = 0> std::basic_ostream& operator<<(std::basic_ostream& os, E value) { using D = std::decay_t; + using U = underlying_type_t; #if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED using namespace magic_enum::flags; if (const auto name = enum_name(value); !name.empty()) { @@ -981,7 +998,7 @@ std::basic_ostream& operator<<(std::basic_ostream& o return os; } #endif - return os << static_cast(value); + return os << static_cast(value); } template = 0> From 8f31ad7cfaa0588a759adaaf9ef45eef192081a4 Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 15 Aug 2020 19:52:37 +0500 Subject: [PATCH 14/18] wip --- README.md | 2 ++ doc/reference.md | 16 ++++++++++++++-- include/magic_enum.hpp | 16 ++++++++++++++-- test/test.cpp | 16 +++++++++------- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index cafca07..71f39d3 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,8 @@ enum class Color { RED = 2, BLUE = 4, GREEN = 8 }; * Before use, read the [limitations](doc/limitations.md) of functionality. +* For the small enum use the API from the namespace `magic_enum`, and for enum-flags use the API from the namespace `magic_enum::flags`. + ## Integration You should add the required file [magic_enum.hpp](include/magic_enum.hpp). diff --git a/doc/reference.md b/doc/reference.md index 7d2c1fe..a4a8566 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -24,14 +24,19 @@ * To check is magic_enum supported compiler use macro `MAGIC_ENUM_SUPPORTED` or constexpr constant `magic_enum::is_magic_enum_supported`.
If magic_enum used on unsupported compiler, occurs the compilation error. To suppress error define macro `MAGIC_ENUM_NO_CHECK_SUPPORT`. +* For the small enum use the API from the namespace `magic_enum`, and for enum-flags use the API from the namespace `magic_enum::flags`. + ## `enum_cast` ```cpp template -constexpr optional enum_cast(string_view value) noexcept; +constexpr optional enum_cast(underlying_type_t value) noexcept; template -constexpr optional enum_cast(underlying_type_t value) noexcept; +constexpr optional enum_cast(string_view value) noexcept; + +template +constexpr optional enum_cast(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v); ``` * Obtains enum value from string or integer. @@ -224,8 +229,15 @@ constexpr optional enum_index() noexcept; ```cpp template constexpr bool enum_contains(E value) noexcept; + +template constexpr bool enum_contains(underlying_type_t value) noexcept; + +template constexpr bool enum_contains(string_view value) noexcept; + +template +constexpr optional enum_contains(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v); ``` * Checks whether enum contains enumerator with such value. diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index dad0d7b..5698dff 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -738,6 +738,15 @@ template return detail::undex(value) != detail::invalid_index_v; } +// Checks whether enum contains enumerator with such string name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(std::is_invocable_r_v, "magic_enum::enum_contains requires bool(char, char) invocable predicate."); + + return enum_cast(value, std::move(p)).has_value(); +} + // Checks whether enum contains enumerator with such string name. template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_t { @@ -980,8 +989,11 @@ template return std::nullopt; // Value out of range. } -using magic_enum::enum_type_name; // TODO: impl -using magic_enum::enum_integer; // TODO: impl +// Returns string name of enum type. +using magic_enum::enum_type_name; + +// Returns integer value from enum value. +using magic_enum::enum_integer; namespace ostream_operators { diff --git a/test/test.cpp b/test/test.cpp index 1c3d87c..cbad4cf 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -27,6 +27,10 @@ #define MAGIC_ENUM_RANGE_MAX 120 #include +#if defined(_MSC_VER) && _MSC_VER >= 1920 || defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 +# define MAGIC_ENUM_SUPPORTED_ALIASES +#endif + #include #include #include @@ -44,9 +48,7 @@ enum number : unsigned long { three = 300, four = 400, -#if defined(_MSC_VER) && _MSC_VER >= 1920 - // Aliases won't work on vs2017. - +#if defined(MAGIC_ENUM_SUPPORTED_ALIASES) _1 = one, _2 = two, _3 = three, @@ -260,7 +262,7 @@ TEST_CASE("enum_contains") { constexpr auto cr = "RED"; REQUIRE(enum_contains(cr)); REQUIRE(enum_contains("GREEN")); - REQUIRE(enum_contains("BLUE")); + REQUIRE(enum_contains("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); })); REQUIRE_FALSE(enum_contains("None")); constexpr auto no = std::string_view{"one"}; @@ -573,16 +575,16 @@ TEST_CASE("type_traits") { REQUIRE_FALSE(is_scoped_enum_v); } - +/* TODO: https://github.com/Neargye/nameof/issues/22 TEST_CASE("enum_type_name") { REQUIRE(enum_type_name() == "Color"); REQUIRE(enum_type_name() == "Numbers"); REQUIRE(enum_type_name() == "Directions"); REQUIRE(enum_type_name() == "number"); } +*/ -#if defined(_MSC_VER) && _MSC_VER >= 1920 -// Aliases won't work on vs2017. +#if defined(MAGIC_ENUM_SUPPORTED_ALIASES) TEST_CASE("aliases") { REQUIRE(enum_count() == 3); From 0228a6249733462c4b52ab7418307c6aafb07f4f Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 15 Aug 2020 20:30:35 +0500 Subject: [PATCH 15/18] wip --- include/magic_enum.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 5698dff..877c438 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -932,6 +932,17 @@ template return enum_contains(static_cast(value)); } +// Checks whether enum-flags contains enumerator with such string name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_contains requires bool(char, char) invocable predicate."); + // TODO: impl + static_assert(sizeof(decltype(value)) == sizeof(E) * 0, "not implemented"); + return {}; +} + + // Checks whether enum-flags contains enumerator with such string name. template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { From 789251699094b182a4a66cb282c1e02785b25905 Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 15 Aug 2020 21:55:52 +0500 Subject: [PATCH 16/18] wip --- include/magic_enum.hpp | 81 ++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 877c438..2e36ce7 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -859,26 +859,34 @@ template // Returns string name from enum-flags value. // If enum-flags value does not have name or value out of range, returns empty string. -template -[[nodiscard]] auto enum_name(E value) -> detail::enable_if_enum_flags_t { +template +[[nodiscard]] auto enum_name(E value) -> detail::enable_if_enum_flags_t> { using D = std::decay_t; using U = underlying_type_t; - std::string name; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { - check_value |= static_cast(v); - const auto n = detail::names_v[i]; - if (!name.empty()) { - name.append(1, '|'); + if constexpr (Strict) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (enum_value(i) == value) { + return detail::names_v[i]; + } + } + } else { + std::string name; + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = enum_value(i); (static_cast(value) & static_cast(v)) != 0) { + check_value |= static_cast(v); + const auto n = detail::names_v[i]; + if (!name.empty()) { + name.append(1, '|'); + } + name.append(n.data(), n.size()); } - name.append(n.data(), n.size()); } - } - if (check_value == static_cast(value)) { - return name; + if (check_value == static_cast(value)) { + return name; + } } return {}; // Invalid value or out of range. @@ -901,35 +909,47 @@ template } // Checks whether enum-flags contains enumerator with such integer value. -template +template [[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; - if constexpr (detail::is_sparse_v) { - using U = underlying_type_t; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { - check_value |= v; + if constexpr(Strict) { + if constexpr (detail::is_sparse_v) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (value == enum_value(i)) { + return true; + } } + + return false; } - - return check_value == value; } else { - constexpr auto min = detail::min_v; - constexpr auto max = detail::value_ors(); + if constexpr (detail::is_sparse_v) { + using U = underlying_type_t; + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { + check_value |= v; + } + } - return value >= min && value <= max; + return check_value == value; + } else { + constexpr auto min = detail::min_v; + constexpr auto max = detail::value_ors(); + + return value >= min && value <= max; + } } } // Checks whether enum-flags contains enumerator with such enum-flags value. -template +template [[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { using D = std::decay_t; using U = underlying_type_t; - return enum_contains(static_cast(value)); + return enum_contains(static_cast(value)); } // Checks whether enum-flags contains enumerator with such string name. @@ -942,7 +962,6 @@ template return {}; } - // Checks whether enum-flags contains enumerator with such string name. template [[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { @@ -953,11 +972,11 @@ template // Obtains enum-flags value from integer value. // Returns std::optional with enum-flags value. -template +template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t>> { using D = std::decay_t; - if (enum_contains(value)) { + if (enum_contains(value)) { return static_cast(value); } From d525acae66237298bf8fb87f3fcf70ffcba8fab9 Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 15 Aug 2020 22:47:20 +0500 Subject: [PATCH 17/18] wip --- example/enum_flag_example.cpp | 22 ++-- example/example.cpp | 20 ++- include/magic_enum.hpp | 239 ++++++++++++++++------------------ 3 files changed, 131 insertions(+), 150 deletions(-) diff --git a/example/enum_flag_example.cpp b/example/enum_flag_example.cpp index d2eaf79..812a3fb 100644 --- a/example/enum_flag_example.cpp +++ b/example/enum_flag_example.cpp @@ -34,21 +34,17 @@ int main() { std::cout << f1_name << std::endl; // Endangered // String enum name sequence. - constexpr auto names = magic_enum::flags::enum_names(); + constexpr auto& names = magic_enum::flags::enum_names(); std::cout << "AnimalFlags names:"; - for (auto n : names) { + for (const auto& n : names) { std::cout << " " << n; } std::cout << std::endl; // AnimalFlags names: HasClaws CanFly EatsFish Endangered -#if 0 // String name to enum value. auto f2 = magic_enum::flags::enum_cast("EatsFish|CanFly"); -#else -auto f2 = magic_enum::flags::enum_cast(1074790400); -#endif - if (f2.has_value() && f2.value() == AnimalFlags::EatsFish) { + if (f2.has_value()) { std::cout << "EatsFish = " << magic_enum::flags::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400 } @@ -60,9 +56,7 @@ auto f2 = magic_enum::flags::enum_cast(1074790400); // Enum value to integer value. auto f4_integer = magic_enum::flags::enum_integer(AnimalFlags::HasClaws); - if (f4_integer == static_cast>(AnimalFlags::HasClaws)) { - std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 1024 - } + std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 1024 using namespace magic_enum::flags::ostream_operators; // out-of-the-box ostream operator for all enums. // ostream operator for enum. @@ -75,9 +69,9 @@ auto f2 = magic_enum::flags::enum_cast(1074790400); std::cout << "AnimalFlags[0] = " << magic_enum::flags::enum_value(0) << std::endl; // AnimalFlags[0] = HasClaws // Enum value sequence. - constexpr auto values = magic_enum::flags::enum_values(); + constexpr auto& values = magic_enum::flags::enum_values(); std::cout << "AnimalFlags values:"; - for (AnimalFlags f : values) { + for (const auto& f : values) { std::cout << " " << f; // ostream operator for enum. } std::cout << std::endl; @@ -89,9 +83,9 @@ auto f2 = magic_enum::flags::enum_cast(1074790400); std::cout << flag << std::endl; // HasClaws|CanFly // Enum pair (value enum, string enum name) sequence. - constexpr auto entries = magic_enum::flags::enum_entries(); + constexpr auto& entries = magic_enum::flags::enum_entries(); std::cout << "AnimalFlags entries:"; - for (auto e : entries) { + for (const auto& e : entries) { std::cout << " " << e.second << " = " << magic_enum::flags::enum_integer(e.first); } std::cout << std::endl; diff --git a/example/example.cpp b/example/example.cpp index c321ca1..bc41d26 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -39,9 +39,9 @@ int main() { std::cout << c1_name << std::endl; // RED // String enum name sequence. - constexpr auto names = magic_enum::enum_names(); + constexpr auto& names = magic_enum::enum_names(); std::cout << "Color names:"; - for (auto n : names) { + for (const auto& n : names) { std::cout << " " << n; } std::cout << std::endl; @@ -49,21 +49,19 @@ int main() { // String name to enum value. auto c2 = magic_enum::enum_cast("BLUE"); - if (c2.has_value() && c2.value() == Color::BLUE) { + if (c2.has_value()) { std::cout << "BLUE = " << to_integer(c2.value()) << std::endl; // BLUE = 0 } // Integer value to enum value. auto c3 = magic_enum::enum_cast(10); - if (c3.has_value() && c3.value() == Color::GREEN) { + if (c3.has_value()) { std::cout << "GREEN = " << magic_enum::enum_integer(c3.value()) << std::endl; // GREEN = 10 } // Enum value to integer value. auto c4_integer = magic_enum::enum_integer(Color::RED); - if (c4_integer == static_cast>(Color::RED)) { - std::cout << "RED = " << c4_integer << std::endl; // RED = -10 - } + std::cout << "RED = " << c4_integer << std::endl; // RED = -10 using namespace magic_enum::ostream_operators; // out-of-the-box ostream operator for all enums. // ostream operator for enum. @@ -76,9 +74,9 @@ int main() { std::cout << "Color[0] = " << magic_enum::enum_value(0) << std::endl; // Color[0] = RED // Enum value sequence. - constexpr auto values = magic_enum::enum_values(); + constexpr auto& values = magic_enum::enum_values(); std::cout << "Colors values:"; - for (Color c : values) { + for (const auto& c : values) { std::cout << " " << c; // ostream operator for enum. } std::cout << std::endl; @@ -103,9 +101,9 @@ int main() { static_assert(magic_enum::is_scoped_enum_v); // Enum pair (value enum, string enum name) sequence. - constexpr auto entries = magic_enum::enum_entries(); + constexpr auto& entries = magic_enum::enum_entries(); std::cout << "Colors entries:"; - for (auto e : entries) { + for (const auto& e : entries) { std::cout << " " << e.second << " = " << static_cast(e.first); } std::cout << std::endl; diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 2e36ce7..ea0fb9a 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -665,6 +665,19 @@ template return detail::entries_v; } +// Obtains enum value from integer value. +// Returns std::optional with enum value. +template +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { + using D = std::decay_t; + + if (detail::undex(value) != detail::invalid_index_v) { + return static_cast(value); + } + + return std::nullopt; // Invalid value or out of range. +} + // Obtains enum value from string name. // Returns std::optional with enum value. template @@ -690,19 +703,6 @@ template return enum_cast(value, detail::char_equal_to{}); } -// Obtains enum value from integer value. -// Returns std::optional with enum value. -template -[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { - using D = std::decay_t; - - if (detail::undex(value) != detail::invalid_index_v) { - return static_cast(value); - } - - return std::nullopt; // Invalid value or out of range. -} - // Returns integer value from enum value. template [[nodiscard]] constexpr underlying_type_t enum_integer(E value) noexcept { @@ -744,7 +744,7 @@ template using D = std::decay_t; static_assert(std::is_invocable_r_v, "magic_enum::enum_contains requires bool(char, char) invocable predicate."); - return enum_cast(value, std::move(p)).has_value(); + return enum_cast(value, std::move_if_noexcept(p)).has_value(); } // Checks whether enum contains enumerator with such string name. @@ -826,6 +826,9 @@ constexpr E& operator^=(E& lhs, E rhs) noexcept { namespace flags { +// Returns string name of enum type. +using magic_enum::enum_type_name; + // Returns number of enum-flags values. template [[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_flags_t { @@ -908,100 +911,91 @@ template return detail::entries_v; } -// Checks whether enum-flags contains enumerator with such integer value. -template -[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { - using D = std::decay_t; - - if constexpr(Strict) { - if constexpr (detail::is_sparse_v) { - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (value == enum_value(i)) { - return true; - } - } - - return false; - } - } else { - if constexpr (detail::is_sparse_v) { - using U = underlying_type_t; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { - check_value |= v; - } - } - - return check_value == value; - } else { - constexpr auto min = detail::min_v; - constexpr auto max = detail::value_ors(); - - return value >= min && value <= max; - } - } -} - -// Checks whether enum-flags contains enumerator with such enum-flags value. -template -[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { - using D = std::decay_t; - using U = underlying_type_t; - - return enum_contains(static_cast(value)); -} - -// Checks whether enum-flags contains enumerator with such string name. -template -[[nodiscard]] constexpr auto enum_contains(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t { - using D = std::decay_t; - static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_contains requires bool(char, char) invocable predicate."); - // TODO: impl - static_assert(sizeof(decltype(value)) == sizeof(E) * 0, "not implemented"); - return {}; -} - -// Checks whether enum-flags contains enumerator with such string name. -template -[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { - // TODO: impl - static_assert(sizeof(decltype(value)) == sizeof(E) * 0, "not implemented"); - return {}; -} - // Obtains enum-flags value from integer value. // Returns std::optional with enum-flags value. template [[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t>> { using D = std::decay_t; + using U = underlying_type_t; - if (enum_contains(value)) { - return static_cast(value); + if constexpr(Strict) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (value == static_cast(enum_value(i))) { + return static_cast(value); + } + } + } else { + if constexpr (detail::is_sparse_v) { + auto check_value = U{0}; + 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; + } + } + + if (check_value == value) { + return static_cast(value); + } + } else { + constexpr auto min = detail::min_v; + constexpr auto max = detail::value_ors(); + + if (value >= min && value <= max) { + return static_cast(value); + } + } } - return std::nullopt; // Invalid value or out of range. + return std::nullopt; } // Obtains enum-flags value from string name. // Returns std::optional with enum-flags value. -template +template [[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t>> { static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_cast requires bool(char, char) invocable predicate."); - // TODO: impl - static_assert(sizeof(decltype(value)) + sizeof(decltype(p)) == sizeof(E) * 0, "not implemented"); - return {}; + using D = std::decay_t; + using U = underlying_type_t; + + auto result = U{0}; + while (!value.empty()) { + const auto d = value.find_first_of('|'); + const auto s = (d == std::string_view::npos) ? value : value.substr(0, d); + auto f = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(s, detail::names_v[i], p)) { + f = static_cast(enum_value(i)); + result |= f; + break; + } + } + if (f == 0) { + return std::nullopt; + } else { + result |= f; + } + value.remove_prefix((d == std::string_view::npos) ? value.size() : d + 1); + } + + if (result == 0) { + return std::nullopt; + } else { + return static_cast(result); + } } // Obtains enum-flags value from string name. // Returns std::optional with enum-flags value. -template +template [[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { - // TODO: impl - static_assert(sizeof(decltype(value)) == sizeof(E) * 0, "not implemented"); - return {}; + using D = std::decay_t; + + return enum_cast(value, detail::char_equal_to{}); } +// Returns integer value from enum value. +using magic_enum::enum_integer; + // Obtains index in enum-flags values from enum-flags value. // Returns std::optional with index. template @@ -1019,11 +1013,39 @@ template return std::nullopt; // Value out of range. } -// Returns string name of enum type. -using magic_enum::enum_type_name; +// Checks whether enum-flags contains enumerator with such enum-flags value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + using U = underlying_type_t; -// Returns integer value from enum value. -using magic_enum::enum_integer; + return enum_cast(static_cast(value)); +} + +// Checks whether enum-flags contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + + return enum_cast(value).has_value(); +} + +// Checks whether enum-flags contains enumerator with such string name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_contains requires bool(char, char) invocable predicate."); + + return enum_cast(value, std::move_if_noexcept(p)).has_value(); +} + +// Checks whether enum-flags contains enumerator with such string name. +template +[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept -> detail::enable_if_enum_flags_t { + using D = std::decay_t; + + return enum_cast(value).has_value(); +} namespace ostream_operators { @@ -1056,40 +1078,7 @@ std::basic_ostream& operator<<(std::basic_ostream& o namespace bitwise_operators { -template = 0> -constexpr E operator~(E rhs) noexcept { - return static_cast(~static_cast>(rhs)); -} - -template = 0> -constexpr E operator|(E lhs, E rhs) noexcept { - return static_cast(static_cast>(lhs) | static_cast>(rhs)); -} - -template = 0> -constexpr E operator&(E lhs, E rhs) noexcept { - return static_cast(static_cast>(lhs) & static_cast>(rhs)); -} - -template = 0> -constexpr E operator^(E lhs, E rhs) noexcept { - return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); -} - -template = 0> -constexpr E& operator|=(E& lhs, E rhs) noexcept { - return lhs = lhs | rhs; -} - -template = 0> -constexpr E& operator&=(E& lhs, E rhs) noexcept { - return lhs = lhs & rhs; -} - -template = 0> -constexpr E& operator^=(E& lhs, E rhs) noexcept { - return lhs = lhs ^ rhs; -} +using namespace magic_enum::bitwise_operators; } // namespace magic_enum::flags::bitwise_operators From e2578822edd9da570f577cfdb0a8dc615c135083 Mon Sep 17 00:00:00 2001 From: neargye Date: Sat, 15 Aug 2020 22:52:03 +0500 Subject: [PATCH 18/18] wip --- include/magic_enum.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index ea0fb9a..e9ae434 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -918,7 +918,7 @@ template using D = std::decay_t; using U = underlying_type_t; - if constexpr(Strict) { + if constexpr (Strict) { for (std::size_t i = 0; i < detail::count_v; ++i) { if (value == static_cast(enum_value(i))) { return static_cast(value); @@ -1047,7 +1047,9 @@ template return enum_cast(value).has_value(); } -namespace ostream_operators { +} // namespace magic_enum::flags + +namespace flags::ostream_operators { template = 0> std::basic_ostream& operator<<(std::basic_ostream& os, E value) { @@ -1076,14 +1078,12 @@ std::basic_ostream& operator<<(std::basic_ostream& o } // namespace magic_enum::flags::ostream_operators -namespace bitwise_operators { +namespace flags::bitwise_operators { using namespace magic_enum::bitwise_operators; } // namespace magic_enum::flags::bitwise_operators -} // namespace magic_enum::flags - } // namespace magic_enum #if defined(__clang__)