From 6dfdc1e806495f373bc13d67a7a603c93d658132 Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Tue, 10 Sep 2024 21:33:16 -0700 Subject: [PATCH] Rich enum combos --- .clang-tidy | 1 + BUILD.bazel | 1 + test/enum_utils_test.cpp | 272 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) diff --git a/.clang-tidy b/.clang-tidy index 1506231e..356edaa1 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -74,6 +74,7 @@ Checks: > -bugprone-easily-swappable-parameters, -bugprone-switch-missing-default-case, -bugprone-unchecked-optional-access, + -performance-avoid-endl, # Turn all the warnings from the checks above into errors. WarningsAsErrors: "*" diff --git a/BUILD.bazel b/BUILD.bazel index 34ebbba6..0b55b218 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1111,6 +1111,7 @@ cc_test( deps = [ ":concepts", ":consteval_compare", + ":enum_set", ":enum_utils", ":enums_test_common", "@com_google_googletest//:gtest", diff --git a/test/enum_utils_test.cpp b/test/enum_utils_test.cpp index 1358051c..0d42e971 100644 --- a/test/enum_utils_test.cpp +++ b/test/enum_utils_test.cpp @@ -4,11 +4,16 @@ #include "fixed_containers/concepts.hpp" #include "fixed_containers/consteval_compare.hpp" +#include "fixed_containers/enum_map.hpp" +#include "fixed_containers/enum_set.hpp" +#include "fixed_containers/optional_reference.hpp" +#include "fixed_containers/pair.hpp" #include #include #include +#include #include #include @@ -117,6 +122,273 @@ static_assert(consteval_compare::equal<2, sizeof(TestRichEnumBool)>); static_assert(consteval_compare::equal); +namespace nested_enums +{ + +enum class ColorBaseBackingEnum +{ + RED, + BLUE, +}; + +class ColorBase : public SkeletalRichEnum +{ + friend SkeletalRichEnum::ValuesFriend; + using SkeletalRichEnum::SkeletalRichEnum; + +public: + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorBase, RED) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorBase, BLUE) +}; + +enum class ColorVariantRed +{ + PINK, + ORANGE, +}; +enum class ColorVariantBlue +{ + CYAN, + AZURE, +}; + +enum class ColorVariantBackingEnum +{ + PINK, + ORANGE, + CYAN, + AZURE, +}; + +enum class ColorVariantAll +{ + INVALID, +}; + +template +class ColorVariant : public SkeletalRichEnum, ColorVariantBackingEnum> +{ + friend SkeletalRichEnum::ValuesFriend; + using SkeletalRichEnum::SkeletalRichEnum; + +public: + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<>, PINK) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<>, ORANGE) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<>, CYAN) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<>, AZURE) +}; + +enum class ColorBackingEnum +{ + RED_PINK, + RED_ORANGE, + BLUE_CYAN, + BLUE_AZURE, +}; + +struct ColorData +{ + ColorBase plain_color{}; + ColorVariant<> color_variant{}; +}; + +struct ColorValues +{ + using BE = ColorBackingEnum; + static constexpr auto VALUES = EnumMap::create_with_all_entries< + Pair{BE::RED_PINK, ColorData{ColorBase::RED(), ColorVariant<>::PINK()}}, + Pair{BE::RED_ORANGE, ColorData{ColorBase::RED(), ColorVariant<>::ORANGE()}}, + Pair{BE::BLUE_CYAN, ColorData{ColorBase::BLUE(), ColorVariant<>::CYAN()}}, + Pair{BE::BLUE_AZURE, ColorData{ColorBase::BLUE(), ColorVariant<>::AZURE()}}>(); + + static constexpr EnumMap, BE>> REVERSE_MAP = []() + { + EnumMap, BE>> output{}; + + for (const auto& [backing_enum, v] : VALUES) + { + output[v.plain_color][v.color_variant] = backing_enum; + } + + return output; + }(); +}; + +class Color : public SkeletalRichEnum +{ +private: + friend SkeletalRichEnum::ValuesFriend; + using SkeletalRichEnum::SkeletalRichEnum; + +public: + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, RED_PINK) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, RED_ORANGE) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, BLUE_CYAN) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, BLUE_AZURE) + + static constexpr OptionalReference from(const ColorBase& plain_color, + const ColorVariant<>& color_variant) + { + auto it1 = ColorValues::REVERSE_MAP.find(plain_color); + if (it1 == ColorValues::REVERSE_MAP.end()) + { + return std::nullopt; + } + + const EnumMap, BackingEnum>& variant_map = it1->second; + auto it2 = variant_map.find(color_variant); + if (it2 == variant_map.end()) + { + return std::nullopt; + } + + return value_of(it2->second); + } + +public: + [[nodiscard]] constexpr ColorBase plain_color() const + { + return ColorValues::VALUES.at(backing_enum()).plain_color; + } + [[nodiscard]] constexpr ColorVariant<> color_variant() const + { + return ColorValues::VALUES.at(backing_enum()).color_variant; + } +}; + +static constexpr EnumSet ALL_RED_VARIANTS = []() +{ + EnumSet out{}; + for (const auto& color : Color::values()) + { + if (color.plain_color() == ColorBase::RED()) + { + out.insert(color); + } + } + return out; +}(); + +static_assert(ALL_RED_VARIANTS.size() == 2); +static_assert(ALL_RED_VARIANTS.contains(Color::RED_ORANGE())); +static_assert(ALL_RED_VARIANTS.contains(Color::RED_PINK())); + +template <> +class ColorVariant + : public SkeletalRichEnum, ColorVariantRed> +{ + friend SkeletalRichEnum::ValuesFriend; + using SkeletalRichEnum::SkeletalRichEnum; + +public: + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant, PINK) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant, ORANGE) + + static constexpr OptionalReference> from( + const ColorVariant<>& flat_variant) + { + switch (flat_variant) + { + case ColorVariant<>::PINK(): + return OptionalReference{PINK()}; + case ColorVariant<>::ORANGE(): + return OptionalReference{ORANGE()}; + case ColorVariant<>::CYAN(): + case ColorVariant<>::AZURE(): + return std::nullopt; + } + return std::nullopt; + } +}; + +template <> +class ColorVariant + : public SkeletalRichEnum, ColorVariantBlue> +{ + friend SkeletalRichEnum::ValuesFriend; + using SkeletalRichEnum::SkeletalRichEnum; + +public: + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant, CYAN) + FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant, AZURE) + + static constexpr OptionalReference> from( + const ColorVariant<>& flat_variant) + { + switch (flat_variant) + { + case ColorVariant<>::CYAN(): + return OptionalReference{CYAN()}; + case ColorVariant<>::AZURE(): + return OptionalReference{AZURE()}; + case ColorVariant<>::PINK(): + case ColorVariant<>::ORANGE(): + return std::nullopt; + } + return std::nullopt; + } +}; + +namespace +{ +void do_stuff_with_plain_color(const Color& color) +{ + switch (color) + { + case Color::RED_PINK(): + std::cout << "RED:PINK" << std::endl; + break; + case Color::RED_ORANGE(): + std::cout << color.to_string() << std::endl; + break; + case Color::BLUE_CYAN(): + std::cout << "BLUE:CYAN" << std::endl; + break; + case Color::BLUE_AZURE(): + std::cout << "BLUE:AZURE" << std::endl; + break; + } + + switch (color.plain_color()) + { + case ColorBase::RED(): + { + switch (ColorVariant::from(color.color_variant()).value()) + { + case ColorVariant::PINK(): + std::cout << "RED:PINK" << std::endl; + break; + case ColorVariant::ORANGE(): + std::cout << "RED:ORANGE" << std::endl; + break; + } + break; + } + case ColorBase::BLUE(): + { + switch (ColorVariant::from(color.color_variant()).value()) + { + case ColorVariant::CYAN(): + std::cout << "BLUE:CYAN" << std::endl; + break; + case ColorVariant::AZURE(): + std::cout << "BLUE:AZURE" << std::endl; + break; + } + break; + } + } +} +} // namespace + +TEST(NestedEnums, Example) +{ + const Color color = Color::from(ColorBase::BLUE(), ColorVariant<>::AZURE()).value(); + do_stuff_with_plain_color(color); +} + +} // namespace nested_enums + TEST(BuiltinEnumAdapter, Ordinal) { {