From 68ffd5fa079528aea10a347d521af9667d12121b Mon Sep 17 00:00:00 2001 From: Alexander Karatarakis Date: Fri, 13 Sep 2024 21:07:27 -0700 Subject: [PATCH] `std::visit` --- test/enum_utils_test.cpp | 132 ++++++++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 15 deletions(-) diff --git a/test/enum_utils_test.cpp b/test/enum_utils_test.cpp index 8b3d14df..e021b8d7 100644 --- a/test/enum_utils_test.cpp +++ b/test/enum_utils_test.cpp @@ -160,10 +160,26 @@ enum class ColorVariantAll }; template -class ColorVariant : public std::variant +class ColorVariant { public: - using variant::variant; + using ColorStdVariant = std::variant; + +private: + ColorStdVariant variant_{}; + +public: + template + constexpr ColorVariant(Args&&... args) + : variant_{std::forward(args)...} + { + } + + constexpr bool operator==(const ColorVariant<>& other) const = default; + constexpr auto operator<=>(const ColorVariant<>& other) const = default; + + [[nodiscard]] const ColorStdVariant& as_std_variant() const { return variant_; } + ColorStdVariant& as_std_variant() { return variant_; } }; enum class ColorBackingEnum @@ -243,6 +259,12 @@ class Color : public SkeletalRichEnum { return ColorValues::VALUES.at(backing_enum()).color_variant; } + + template + constexpr void variant_switch(Args&&... args) const + { + std::visit(Overloaded{std::forward(args)...}, color_variant().as_std_variant()); + } }; static constexpr EnumSet ALL_RED_VARIANTS = []() @@ -276,10 +298,11 @@ class ColorVariant static constexpr OptionalReference> from( const ColorVariant<>& flat_variant) { - if (const ColorVariantRed* valid_variant = std::get_if(&flat_variant); + if (const ColorVariantRed* valid_variant = + std::get_if(&flat_variant.as_std_variant()); valid_variant != nullptr) { - switch (std::get(flat_variant)) + switch (std::get(flat_variant.as_std_variant())) { case ColorVariantRed::PINK: return OptionalReference{PINK()}; @@ -306,10 +329,11 @@ class ColorVariant static constexpr OptionalReference> from( const ColorVariant<>& flat_variant) { - if (const ColorVariantBlue* valid_variant = std::get_if(&flat_variant); + if (const ColorVariantBlue* valid_variant = + std::get_if(&flat_variant.as_std_variant()); valid_variant != nullptr) { - switch (std::get(flat_variant)) + switch (std::get(flat_variant.as_std_variant())) { case ColorVariantBlue::CYAN: return OptionalReference{CYAN()}; @@ -324,35 +348,110 @@ class ColorVariant namespace { -void do_stuff_with_plain_color(const Color& color) +void flat_switch(const Color& color) { switch (color) { case Color::RED_PINK(): - std::cout << "RED:PINK" << std::endl; + 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; + std::cout << "BLUE_CYAN" << std::endl; break; case Color::BLUE_AZURE(): - std::cout << "BLUE:AZURE" << std::endl; + std::cout << "BLUE_AZURE" << std::endl; break; } +} +void automatic_hierarchical_switch(const Color& color) +{ + color.variant_switch( + [](const ColorVariantRed& variant) + { + switch (variant) + { + case ColorVariantRed::PINK: + std::cout << "RED:PINK2" << std::endl; + break; + case ColorVariantRed::ORANGE: + std::cout << "RED:ORANGE2" << std::endl; + break; + } + }, + [](const ColorVariantBlue& variant) + { + switch (variant) + { + case ColorVariantBlue::CYAN: + std::cout << "BLUE:CYAN2" << std::endl; + break; + case ColorVariant::AZURE(): + std::cout << "BLUE:AZURE2" << std::endl; + break; + } + }); +} + +void std_visit_with_if_constexpr(const Color& color) +{ + std::visit( + [](const T& variant) + { + if constexpr (std::same_as) + { + switch (variant) + { + case ColorVariantRed::PINK: + std::cout << "RED:PINK3" << std::endl; + break; + case ColorVariantRed::ORANGE: + std::cout << "RED:ORANGE3" << std::endl; + break; + } + } + else if constexpr (std::same_as) + { + switch (variant) + { + case ColorVariantBlue::CYAN: + std::cout << "BLUE:CYAN3" << std::endl; + break; + case ColorVariant::AZURE(): + std::cout << "BLUE:AZURE3" << std::endl; + break; + } + } + else + { + // if-constexpr has better compilation errors that std::visit but you have to + // remember to have this `static_assert`. `overloaded<...>` pattern has bad + // compilation errors, but you can't forget to handle a case. Syntax is also + // slightly convoluted. + static_assert(AlwaysFalseV); + } + }, + color.color_variant().as_std_variant()); +} + +void manual_hierarchical_switch(const Color& color) +{ switch (color.plain_color()) { case ColorBase::RED(): { + // It is possible to have the wrong color here since the user is responsible for the nested + // switch switch (ColorVariant::from(color.color_variant()).value()) { case ColorVariant::PINK(): - std::cout << "RED:PINK" << std::endl; + std::cout << "RED:PINK1" << std::endl; break; case ColorVariant::ORANGE(): - std::cout << "RED:ORANGE" << std::endl; + std::cout << "RED:ORANGE1" << std::endl; break; } break; @@ -362,10 +461,10 @@ void do_stuff_with_plain_color(const Color& color) switch (ColorVariant::from(color.color_variant()).value()) { case ColorVariant::CYAN(): - std::cout << "BLUE:CYAN" << std::endl; + std::cout << "BLUE:CYAN1" << std::endl; break; case ColorVariant::AZURE(): - std::cout << "BLUE:AZURE" << std::endl; + std::cout << "BLUE:AZURE1" << std::endl; break; } break; @@ -378,7 +477,10 @@ TEST(NestedEnums, Example) { const Color color = Color::from(ColorBase::BLUE(), ColorVariant::AZURE()).value(); - do_stuff_with_plain_color(color); + flat_switch(color); + automatic_hierarchical_switch(color); + std_visit_with_if_constexpr(color); + manual_hierarchical_switch(color); } } // namespace nested_enums