Skip to content

Commit

Permalink
variant<> contains the individual variants
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkaratarakis committed Sep 15, 2024
1 parent 68ffd5f commit 0e6336a
Showing 1 changed file with 48 additions and 80 deletions.
128 changes: 48 additions & 80 deletions test/enum_utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,29 @@ enum class ColorVariantAll
};

template <auto COLOR = ColorVariantAll{}>
class ColorVariant
class ColorVariant;

// This rich enum specialization is not strictly needed, but it gets us:
// - Optional semantics
// - Ability to reference to variants with the original color via ColorVariant<U>
// This is show-cased with RED, whereas BLUE uses a normal enum.
template <>
class ColorVariant<ColorBase::RED()>
: public SkeletalRichEnum<ColorVariant<ColorBase::RED()>, ColorVariantRed>
{
friend SkeletalRichEnum::ValuesFriend;
using SkeletalRichEnum::SkeletalRichEnum;

public:
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::RED()>, PINK)
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::RED()>, ORANGE)
};

template <>
class ColorVariant<ColorVariantAll{}>
{
public:
using ColorStdVariant = std::variant<ColorVariantRed, ColorVariantBlue>;
using ColorStdVariant = std::variant<ColorVariant<ColorBase::RED()>, ColorVariantBlue>;

private:
ColorStdVariant variant_{};
Expand All @@ -180,6 +199,16 @@ class ColorVariant

[[nodiscard]] const ColorStdVariant& as_std_variant() const { return variant_; }
ColorStdVariant& as_std_variant() { return variant_; }

template <typename U>
constexpr OptionalReference<const U> to()
{
if (const U* valid_variant = std::get_if<U>(&as_std_variant()); valid_variant != nullptr)
{
return OptionalReference<const U>{*valid_variant};
}
return std::nullopt;
}
};

enum class ColorBackingEnum
Expand All @@ -200,8 +229,10 @@ struct ColorValues
{
using BE = ColorBackingEnum;
static constexpr auto VALUES = EnumMap<BE, ColorData>::create_with_all_entries({
Pair<BE, ColorData>{BE::RED_PINK, {ColorBase::RED(), ColorVariantRed::PINK}},
Pair<BE, ColorData>{BE::RED_ORANGE, ColorData{ColorBase::RED(), ColorVariantRed::ORANGE}},
Pair<BE, ColorData>{BE::RED_PINK,
{ColorBase::RED(), ColorVariant<ColorBase::RED()>::PINK()}},
Pair<BE, ColorData>{BE::RED_ORANGE,
ColorData{ColorBase::RED(), ColorVariant<ColorBase::RED()>::ORANGE()}},
Pair<BE, ColorData>{BE::BLUE_CYAN, ColorData{ColorBase::BLUE(), ColorVariantBlue::CYAN}},
Pair<BE, ColorData>{BE::BLUE_AZURE, ColorData{ColorBase::BLUE(), ColorVariantBlue::AZURE}},
});
Expand Down Expand Up @@ -284,68 +315,6 @@ 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<ColorBase::RED()>
: public SkeletalRichEnum<ColorVariant<ColorBase::RED()>, ColorVariantRed>
{
friend SkeletalRichEnum::ValuesFriend;
using SkeletalRichEnum::SkeletalRichEnum;

public:
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::RED()>, PINK)
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::RED()>, ORANGE)

static constexpr OptionalReference<const ColorVariant<ColorBase::RED()>> from(
const ColorVariant<>& flat_variant)
{
if (const ColorVariantRed* valid_variant =
std::get_if<ColorVariantRed>(&flat_variant.as_std_variant());
valid_variant != nullptr)
{
switch (std::get<ColorVariantRed>(flat_variant.as_std_variant()))
{
case ColorVariantRed::PINK:
return OptionalReference{PINK()};
case ColorVariantRed::ORANGE:
return OptionalReference{ORANGE()};
}
}

return std::nullopt;
}
};

template <>
class ColorVariant<ColorBase::BLUE()>
: public SkeletalRichEnum<ColorVariant<ColorBase::BLUE()>, ColorVariantBlue>
{
friend SkeletalRichEnum::ValuesFriend;
using SkeletalRichEnum::SkeletalRichEnum;

public:
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::BLUE()>, CYAN)
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::BLUE()>, AZURE)

static constexpr OptionalReference<const ColorVariant<ColorBase::BLUE()>> from(
const ColorVariant<>& flat_variant)
{
if (const ColorVariantBlue* valid_variant =
std::get_if<ColorVariantBlue>(&flat_variant.as_std_variant());
valid_variant != nullptr)
{
switch (std::get<ColorVariantBlue>(flat_variant.as_std_variant()))
{
case ColorVariantBlue::CYAN:
return OptionalReference{CYAN()};
case ColorVariantBlue::AZURE:
return OptionalReference{AZURE()};
}
}

return std::nullopt;
}
};

namespace
{
void flat_switch(const Color& color)
Expand All @@ -370,14 +339,14 @@ void flat_switch(const Color& color)
void automatic_hierarchical_switch(const Color& color)
{
color.variant_switch(
[](const ColorVariantRed& variant)
[](const ColorVariant<ColorBase::RED()>& variant)
{
switch (variant)
{
case ColorVariantRed::PINK:
case ColorVariant<ColorBase::RED()>::PINK():
std::cout << "RED:PINK2" << std::endl;
break;
case ColorVariantRed::ORANGE:
case ColorVariant<ColorBase::RED()>::ORANGE():
std::cout << "RED:ORANGE2" << std::endl;
break;
}
Expand All @@ -389,7 +358,7 @@ void automatic_hierarchical_switch(const Color& color)
case ColorVariantBlue::CYAN:
std::cout << "BLUE:CYAN2" << std::endl;
break;
case ColorVariant<ColorBase::BLUE()>::AZURE():
case ColorVariantBlue::AZURE:
std::cout << "BLUE:AZURE2" << std::endl;
break;
}
Expand All @@ -401,14 +370,14 @@ void std_visit_with_if_constexpr(const Color& color)
std::visit(
[]<typename T>(const T& variant)
{
if constexpr (std::same_as<T, ColorVariantRed>)
if constexpr (std::same_as<T, ColorVariant<ColorBase::RED()>>)
{
switch (variant)
{
case ColorVariantRed::PINK:
case ColorVariant<ColorBase::RED()>::PINK():
std::cout << "RED:PINK3" << std::endl;
break;
case ColorVariantRed::ORANGE:
case ColorVariant<ColorBase::RED()>::ORANGE():
std::cout << "RED:ORANGE3" << std::endl;
break;
}
Expand All @@ -420,7 +389,7 @@ void std_visit_with_if_constexpr(const Color& color)
case ColorVariantBlue::CYAN:
std::cout << "BLUE:CYAN3" << std::endl;
break;
case ColorVariant<ColorBase::BLUE()>::AZURE():
case ColorVariantBlue::AZURE:
std::cout << "BLUE:AZURE3" << std::endl;
break;
}
Expand All @@ -445,7 +414,7 @@ void manual_hierarchical_switch(const Color& color)
{
// It is possible to have the wrong color here since the user is responsible for the nested
// switch
switch (ColorVariant<ColorBase::RED()>::from(color.color_variant()).value())
switch (color.color_variant().to<ColorVariant<ColorBase::RED()>>().value())
{
case ColorVariant<ColorBase::RED()>::PINK():
std::cout << "RED:PINK1" << std::endl;
Expand All @@ -458,12 +427,12 @@ void manual_hierarchical_switch(const Color& color)
}
case ColorBase::BLUE():
{
switch (ColorVariant<ColorBase::BLUE()>::from(color.color_variant()).value())
switch (color.color_variant().to<ColorVariantBlue>().value())
{
case ColorVariant<ColorBase::BLUE()>::CYAN():
case ColorVariantBlue::CYAN:
std::cout << "BLUE:CYAN1" << std::endl;
break;
case ColorVariant<ColorBase::BLUE()>::AZURE():
case ColorVariantBlue::AZURE:
std::cout << "BLUE:AZURE1" << std::endl;
break;
}
Expand All @@ -475,8 +444,7 @@ void manual_hierarchical_switch(const Color& color)

TEST(NestedEnums, Example)
{
const Color color =
Color::from(ColorBase::BLUE(), ColorVariant<ColorBase::BLUE()>::AZURE()).value();
const Color color = Color::from(ColorBase::BLUE(), ColorVariantBlue::AZURE).value();
flat_switch(color);
automatic_hierarchical_switch(color);
std_visit_with_if_constexpr(color);
Expand Down

0 comments on commit 0e6336a

Please sign in to comment.