Skip to content

Commit

Permalink
Rich enum combos
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkaratarakis committed Sep 15, 2024
1 parent ba7bb88 commit 6dfdc1e
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 0 deletions.
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -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: "*"
Expand Down
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,7 @@ cc_test(
deps = [
":concepts",
":consteval_compare",
":enum_set",
":enum_utils",
":enums_test_common",
"@com_google_googletest//:gtest",
Expand Down
272 changes: 272 additions & 0 deletions test/enum_utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <gtest/gtest.h>

#include <cstddef>
#include <functional>
#include <iostream>
#include <optional>
#include <type_traits>

Expand Down Expand Up @@ -117,6 +122,273 @@ static_assert(consteval_compare::equal<2, sizeof(TestRichEnumBool)>);
static_assert(consteval_compare::equal<sizeof(TestRichEnumBool),
sizeof(detail::TestRichEnumBoolBackingEnum) + 1>);

namespace nested_enums
{

enum class ColorBaseBackingEnum
{
RED,
BLUE,
};

class ColorBase : public SkeletalRichEnum<ColorBase, ColorBaseBackingEnum>
{
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 <auto COLOR = ColorVariantAll{}>
class ColorVariant : public SkeletalRichEnum<ColorVariant<>, 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<BE, ColorData>::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<ColorBase, EnumMap<ColorVariant<>, BE>> REVERSE_MAP = []()
{
EnumMap<ColorBase, EnumMap<ColorVariant<>, BE>> output{};

for (const auto& [backing_enum, v] : VALUES)
{
output[v.plain_color][v.color_variant] = backing_enum;
}

return output;
}();
};

class Color : public SkeletalRichEnum<Color, ColorBackingEnum>
{
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<const Color> 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<ColorVariant<>, 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<Color> ALL_RED_VARIANTS = []()
{
EnumSet<Color> 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<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)
{
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<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)
{
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<ColorBase::RED()>::from(color.color_variant()).value())
{
case ColorVariant<ColorBase::RED()>::PINK():
std::cout << "RED:PINK" << std::endl;
break;
case ColorVariant<ColorBase::RED()>::ORANGE():
std::cout << "RED:ORANGE" << std::endl;
break;
}
break;
}
case ColorBase::BLUE():
{
switch (ColorVariant<ColorBase::BLUE()>::from(color.color_variant()).value())
{
case ColorVariant<ColorBase::BLUE()>::CYAN():
std::cout << "BLUE:CYAN" << std::endl;
break;
case ColorVariant<ColorBase::BLUE()>::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)
{
{
Expand Down

0 comments on commit 6dfdc1e

Please sign in to comment.