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__)