Skip to content

Commit 8e19ef4

Browse files
Rich enum combos
1 parent ba7bb88 commit 8e19ef4

File tree

3 files changed

+276
-0
lines changed

3 files changed

+276
-0
lines changed

.clang-tidy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Checks: >
7474
-bugprone-easily-swappable-parameters,
7575
-bugprone-switch-missing-default-case,
7676
-bugprone-unchecked-optional-access,
77+
-performance-avoid-endl,
7778
7879
# Turn all the warnings from the checks above into errors.
7980
WarningsAsErrors: "*"

BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,7 @@ cc_test(
11111111
deps = [
11121112
":concepts",
11131113
":consteval_compare",
1114+
":enum_set",
11141115
":enum_utils",
11151116
":enums_test_common",
11161117
"@com_google_googletest//:gtest",

test/enum_utils_test.cpp

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,20 @@
44

55
#include "fixed_containers/concepts.hpp"
66
#include "fixed_containers/consteval_compare.hpp"
7+
#include "fixed_containers/enum_map.hpp"
8+
#include "fixed_containers/enum_set.hpp"
9+
#include "fixed_containers/fixed_map.hpp"
10+
#include "fixed_containers/optional_reference.hpp"
11+
#include "fixed_containers/pair.hpp"
712

813
#include <gtest/gtest.h>
914

1015
#include <cstddef>
1116
#include <functional>
17+
#include <iostream>
1218
#include <optional>
1319
#include <type_traits>
20+
#include <variant>
1421

1522
namespace fixed_containers::rich_enums_detail
1623
{
@@ -117,6 +124,273 @@ static_assert(consteval_compare::equal<2, sizeof(TestRichEnumBool)>);
117124
static_assert(consteval_compare::equal<sizeof(TestRichEnumBool),
118125
sizeof(detail::TestRichEnumBoolBackingEnum) + 1>);
119126

127+
namespace nested_enums
128+
{
129+
130+
enum class ColorBaseBackingEnum
131+
{
132+
RED,
133+
BLUE,
134+
};
135+
136+
class ColorBase : public SkeletalRichEnum<ColorBase, ColorBaseBackingEnum>
137+
{
138+
friend SkeletalRichEnum::ValuesFriend;
139+
using SkeletalRichEnum::SkeletalRichEnum;
140+
141+
public:
142+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorBase, RED)
143+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorBase, BLUE)
144+
};
145+
146+
enum class ColorVariantRed
147+
{
148+
PINK,
149+
ORANGE,
150+
};
151+
enum class ColorVariantBlue
152+
{
153+
CYAN,
154+
AZURE,
155+
};
156+
157+
enum class ColorVariantAll
158+
{
159+
INVALID,
160+
};
161+
162+
template <auto COLOR = ColorVariantAll{}>
163+
class ColorVariant;
164+
165+
// This rich enum specialization is not strictly needed, but it gets us:
166+
// - Optional semantics
167+
// - Ability to reference to variants with the original color via ColorVariant<U>
168+
// This is show-cased with RED, whereas BLUE uses a normal enum.
169+
template <>
170+
class ColorVariant<ColorBase::RED()>
171+
: public SkeletalRichEnum<ColorVariant<ColorBase::RED()>, ColorVariantRed>
172+
{
173+
friend SkeletalRichEnum::ValuesFriend;
174+
using SkeletalRichEnum::SkeletalRichEnum;
175+
176+
public:
177+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::RED()>, PINK)
178+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::RED()>, ORANGE)
179+
};
180+
template <>
181+
class ColorVariant<ColorBase::BLUE()>
182+
: public SkeletalRichEnum<ColorVariant<ColorBase::BLUE()>, ColorVariantBlue>
183+
{
184+
friend SkeletalRichEnum::ValuesFriend;
185+
using SkeletalRichEnum::SkeletalRichEnum;
186+
187+
public:
188+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::BLUE()>, CYAN)
189+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(ColorVariant<ColorBase::BLUE()>, AZURE)
190+
};
191+
192+
template <>
193+
class ColorVariant<ColorVariantAll{}>
194+
{
195+
public:
196+
using ColorStdVariant =
197+
std::variant<ColorVariant<ColorBase::RED()>, ColorVariant<ColorBase::BLUE()>>;
198+
199+
private:
200+
ColorStdVariant variant_{};
201+
202+
public:
203+
template <typename... Args>
204+
constexpr ColorVariant(Args&&... args)
205+
: variant_{std::forward<Args>(args)...}
206+
{
207+
}
208+
209+
constexpr bool operator==(const ColorVariant<>& other) const = default;
210+
constexpr auto operator<=>(const ColorVariant<>& other) const = default;
211+
212+
[[nodiscard]] const ColorStdVariant& as_std_variant() const { return variant_; }
213+
ColorStdVariant& as_std_variant() { return variant_; }
214+
215+
template <typename U>
216+
constexpr OptionalReference<const U> to()
217+
{
218+
if (const U* valid_variant = std::get_if<U>(&as_std_variant()); valid_variant != nullptr)
219+
{
220+
return OptionalReference<const U>{*valid_variant};
221+
}
222+
return std::nullopt;
223+
}
224+
};
225+
226+
enum class ColorBackingEnum
227+
{
228+
RED_PINK,
229+
RED_ORANGE,
230+
BLUE_CYAN,
231+
BLUE_AZURE,
232+
};
233+
234+
struct ColorData
235+
{
236+
ColorBase plain_color{};
237+
ColorVariant<> color_variant{};
238+
};
239+
240+
struct ColorValues
241+
{
242+
using BE = ColorBackingEnum;
243+
static constexpr auto VALUES = EnumMap<BE, ColorData>::create_with_all_entries({
244+
Pair<BE, ColorData>{BE::RED_PINK,
245+
{ColorBase::RED(), ColorVariant<ColorBase::RED()>::PINK()}},
246+
Pair<BE, ColorData>{BE::RED_ORANGE,
247+
ColorData{ColorBase::RED(), ColorVariant<ColorBase::RED()>::ORANGE()}},
248+
Pair<BE, ColorData>{BE::BLUE_CYAN,
249+
ColorData{ColorBase::BLUE(), ColorVariant<ColorBase::BLUE()>::CYAN()}},
250+
Pair<BE, ColorData>{BE::BLUE_AZURE,
251+
ColorData{ColorBase::BLUE(), ColorVariant<ColorBase::BLUE()>::AZURE()}},
252+
});
253+
254+
static constexpr EnumMap<ColorBase, FixedMap<ColorVariant<>, BE, 15>> REVERSE_MAP = []()
255+
{
256+
EnumMap<ColorBase, FixedMap<ColorVariant<>, BE, 15>> output{};
257+
258+
for (const auto& [backing_enum, v] : VALUES)
259+
{
260+
output[v.plain_color][v.color_variant] = backing_enum;
261+
}
262+
263+
return output;
264+
}();
265+
};
266+
267+
class Color : public SkeletalRichEnum<Color, ColorBackingEnum>
268+
{
269+
private:
270+
friend SkeletalRichEnum::ValuesFriend;
271+
using SkeletalRichEnum::SkeletalRichEnum;
272+
273+
public:
274+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, RED_PINK)
275+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, RED_ORANGE)
276+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, BLUE_CYAN)
277+
FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(Color, BLUE_AZURE)
278+
279+
static constexpr OptionalReference<const Color> from(const ColorBase& plain_color,
280+
const ColorVariant<>& color_variant)
281+
{
282+
auto it1 = ColorValues::REVERSE_MAP.find(plain_color);
283+
if (it1 == ColorValues::REVERSE_MAP.end())
284+
{
285+
return std::nullopt;
286+
}
287+
288+
const FixedMap<ColorVariant<>, BackingEnum, 15>& variant_map = it1->second;
289+
auto it2 = variant_map.find(color_variant);
290+
if (it2 == variant_map.end())
291+
{
292+
return std::nullopt;
293+
}
294+
295+
return value_of(it2->second);
296+
}
297+
298+
public:
299+
[[nodiscard]] constexpr ColorBase plain_color() const
300+
{
301+
return ColorValues::VALUES.at(backing_enum()).plain_color;
302+
}
303+
[[nodiscard]] constexpr ColorVariant<> color_variant() const
304+
{
305+
return ColorValues::VALUES.at(backing_enum()).color_variant;
306+
}
307+
308+
template <typename... Args>
309+
constexpr void variant_switch(Args&&... args) const
310+
{
311+
std::visit(Overloaded{std::forward<Args>(args)...}, color_variant().as_std_variant());
312+
}
313+
};
314+
315+
static constexpr EnumSet<Color> ALL_RED_VARIANTS = []()
316+
{
317+
EnumSet<Color> out{};
318+
for (const auto& color : Color::values())
319+
{
320+
if (color.plain_color() == ColorBase::RED())
321+
{
322+
out.insert(color);
323+
}
324+
}
325+
return out;
326+
}();
327+
328+
static_assert(ALL_RED_VARIANTS.size() == 2);
329+
static_assert(ALL_RED_VARIANTS.contains(Color::RED_ORANGE()));
330+
static_assert(ALL_RED_VARIANTS.contains(Color::RED_PINK()));
331+
332+
namespace
333+
{
334+
void flat_switch(const Color& color)
335+
{
336+
switch (color)
337+
{
338+
case Color::RED_PINK():
339+
std::cout << "RED_PINK" << std::endl;
340+
break;
341+
case Color::RED_ORANGE():
342+
std::cout << color.to_string() << std::endl;
343+
break;
344+
case Color::BLUE_CYAN():
345+
std::cout << "BLUE_CYAN" << std::endl;
346+
break;
347+
case Color::BLUE_AZURE():
348+
std::cout << "BLUE_AZURE" << std::endl;
349+
break;
350+
}
351+
}
352+
353+
void automatic_hierarchical_switch(const Color& color)
354+
{
355+
color.variant_switch(
356+
[](const ColorVariant<ColorBase::RED()>& variant)
357+
{
358+
switch (variant)
359+
{
360+
case ColorVariant<ColorBase::RED()>::PINK():
361+
std::cout << "RED:PINK2" << std::endl;
362+
break;
363+
case ColorVariant<ColorBase::RED()>::ORANGE():
364+
std::cout << "RED:ORANGE2" << std::endl;
365+
break;
366+
}
367+
},
368+
[](const ColorVariant<ColorBase::BLUE()>& variant)
369+
{
370+
switch (variant)
371+
{
372+
case ColorVariant<ColorBase::BLUE()>::CYAN():
373+
std::cout << "BLUE:CYAN2" << std::endl;
374+
break;
375+
case ColorVariant<ColorBase::BLUE()>::AZURE():
376+
std::cout << "BLUE:AZURE2" << std::endl;
377+
break;
378+
}
379+
});
380+
}
381+
382+
} // namespace
383+
384+
TEST(NestedEnums, Example)
385+
{
386+
const Color color =
387+
Color::from(ColorBase::BLUE(), ColorVariant<ColorBase::BLUE()>::AZURE()).value();
388+
flat_switch(color);
389+
automatic_hierarchical_switch(color);
390+
}
391+
392+
} // namespace nested_enums
393+
120394
TEST(BuiltinEnumAdapter, Ordinal)
121395
{
122396
{

0 commit comments

Comments
 (0)