diff --git a/README.md b/README.md index d6312d0..5416cb4 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/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/doc/reference.md b/doc/reference.md index cf253e6..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. @@ -88,7 +93,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 +146,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 +177,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 +194,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 +212,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. @@ -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/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..812a3fb --- /dev/null +++ b/example/enum_flag_example.cpp @@ -0,0 +1,95 @@ +// 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 + +#include + +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::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::flags::enum_names(); + std::cout << "AnimalFlags names:"; + for (const auto& n : names) { + std::cout << " " << n; + } + std::cout << std::endl; + // AnimalFlags names: HasClaws CanFly EatsFish Endangered + + // String name to enum value. + auto f2 = magic_enum::flags::enum_cast("EatsFish|CanFly"); + if (f2.has_value()) { + std::cout << "EatsFish = " << magic_enum::flags::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400 + } + + // Integer value to enum value. + 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 + } + + // Enum value to integer value. + auto f4_integer = magic_enum::flags::enum_integer(AnimalFlags::HasClaws); + 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. + std::cout << f1 << " " << f2 << " " << f3 << std::endl; // Endangered CanFly|EatsFish HasClaws|EatsFish + + // Number of enum values. + 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::flags::enum_value(0) << std::endl; // AnimalFlags[0] = HasClaws + + // Enum value sequence. + constexpr auto& values = magic_enum::flags::enum_values(); + std::cout << "AnimalFlags values:"; + for (const auto& f : values) { + std::cout << " " << f; // ostream operator for enum. + } + std::cout << std::endl; + // AnimalFlags sequence: HasClaws CanFly EatsFish Endangered + + 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 + + // Enum pair (value enum, string enum name) sequence. + constexpr auto& entries = magic_enum::flags::enum_entries(); + std::cout << "AnimalFlags entries:"; + for (const auto& e : entries) { + std::cout << " " << e.second << " = " << magic_enum::flags::enum_integer(e.first); + } + std::cout << std::endl; + // 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 5e21bf8..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 color_names = magic_enum::enum_names(); + constexpr auto& names = magic_enum::enum_names(); std::cout << "Color names:"; - for (auto n : color_names) { + for (const auto& n : names) { std::cout << " " << n; } std::cout << std::endl; @@ -49,36 +49,34 @@ 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 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); + 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. 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 // 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 (const auto& c : values) { std::cout << " " << c; // ostream operator for enum. } std::cout << std::endl; @@ -87,8 +85,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; // 5 enum color { red, green, blue }; @@ -103,9 +101,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 (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 2df8bc6..e9ae434 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 @@ -45,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. @@ -72,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{}} { @@ -136,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') || @@ -156,20 +133,116 @@ 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; + } +}; + 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 @@ -188,44 +261,31 @@ constexpr bool cmp_less(L lhs, R rhs) noexcept { } } -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__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36}; -# elif defined(__GNUC__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 49, sizeof(__PRETTY_FUNCTION__) - 51}; -# 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 +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(1); value >>= static_cast(1), ++ret) {}; + + return ret; + } } -template -inline constexpr auto type_name_v = n(); +template +constexpr bool is_pow2(T x) noexcept { + if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; -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 + return is_pow2(static_cast(x)); + } else { + return x != 0 && (x & (x - 1)) == 0; + } } -template -inline constexpr auto name_v = n(); - template constexpr bool is_valid() noexcept { static_assert(is_enum_v, "magic_enum::detail::is_valid requires enum type."); @@ -233,72 +293,122 @@ 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 +template > constexpr int 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 { + 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)(); + + if constexpr (cmp_less(lhs, rhs)) { + return rhs; + } else { + return lhs; + } + } } -template +template > constexpr int 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 { + 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)(); + + if constexpr (cmp_less(lhs, rhs)) { + return lhs; + } else { + return 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 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(static_cast(i) + O)); + } 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 -inline constexpr std::size_t count_v = values_v.size(); + 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 int min_v = static_cast(values_v.front()); +template +inline constexpr auto values_v = values(); -template -inline constexpr int max_v = static_cast(values_v.back()); +template > +using values_t = decltype((values_v)); -template -inline constexpr std::size_t range_size_v = range_size, max_v>(); +template +inline constexpr auto count_v = values_v.size(); + +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 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); + } +} + +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>; @@ -309,7 +419,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)...}}; } @@ -317,34 +427,54 @@ 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{{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 +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], 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 -inline constexpr bool is_sparse_v = range_size_v != count_v; +template > +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 = 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 >= static_cast(min_v) && value <= static_cast(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; @@ -354,18 +484,52 @@ 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 { +template > +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)); + 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 {}; + 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 +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::flags requires enum-flags implementation."); +}; + +template +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; @@ -422,8 +586,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."); @@ -434,8 +599,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; } @@ -445,83 +608,81 @@ 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]; + 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); } } -// 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; } -// 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 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)."); - constexpr std::string_view name = detail::name_v, V>; + static_assert(std::is_enum_v, "Requires enum type."); + constexpr std::string_view name = detail::enum_name_v; static_assert(name.size() > 0, "Enum value does not have a name."); 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 { 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) { + if (const auto i = detail::endex(value); i != detail::invalid_index_v) { return detail::names_v[i]; } - return {}; // Value out of range. + return {}; // Invalid value or 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>)&> { +[[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; } -// 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>)&> { +[[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; } -// Obtains enum value from enum string name. +// 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 [[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) { @@ -533,104 +694,88 @@ 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; - 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]) { - return enum_value(i); - } - } - - return std::nullopt; // Invalid value or out of range. -} - -// 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; - 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); - } - - return std::nullopt; // Invalid value or out of range. + return enum_cast(value, detail::char_equal_to{}); } // 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); } -// 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> { 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) { + if (const auto i = detail::endex(value); i != detail::invalid_index_v) { return i; } 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; - 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; + return detail::endex(value) != detail::invalid_index_v; } // Checks whether enum contains enumerator with such integer value. 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; + return detail::undex(value) != detail::invalid_index_v; } -// 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, 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_if_noexcept(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 { 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(); } 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 , 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; + + 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 -auto operator<<(std::basic_ostream& os, std::optional value) -> detail::enable_if_enum_t&> { +template , int> = 0> +std::basic_ostream& operator<<(std::basic_ostream& os, std::optional value) { if (value.has_value()) { os << value.value(); } @@ -642,43 +787,303 @@ 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 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 { + using D = std::decay_t; + + return detail::count_v; +} + +// 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; + + if constexpr (detail::is_sparse_v) { + return assert((index < detail::count_v)), detail::values_v[index]; + } else { + constexpr auto min = detail::log2(detail::min_v) - 1; + + return assert((index < detail::count_v)), detail::value(index); + } +} + +// 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; + + return detail::values_v; +} + +// 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; + using U = underlying_type_t; + + 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()); + } + } + + 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. +template +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_flags_t> { + using D = std::decay_t; + + return detail::names_v; +} + +// 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; + + return detail::entries_v; +} + +// 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 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; +} + +// 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::flags::enum_cast requires bool(char, char) invocable predicate."); + 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 +[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_flags_t>> { + 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 +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_flags_t> { + 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-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_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 magic_enum::flags + +namespace flags::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()) { + for (const auto c : name) { + os.put(c); + } + return os; + } +#endif + return os << static_cast(value); +} + +template = 0> +std::basic_ostream& operator<<(std::basic_ostream& os, std::optional value) { + if (value.has_value()) { + os << value.value(); + } + + return os; +} + +} // namespace magic_enum::flags::ostream_operators + +namespace flags::bitwise_operators { + +using namespace magic_enum::bitwise_operators; + +} // namespace magic_enum::flags::bitwise_operators + } // namespace magic_enum #if defined(__clang__) diff --git a/test/test.cpp b/test/test.cpp index 7222dbf..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"}; @@ -310,6 +312,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 +410,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 +426,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"}}}); @@ -567,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);