|
4 | 4 |
|
5 | 5 | #include "fixed_containers/concepts.hpp" |
6 | 6 | #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" |
7 | 12 |
|
8 | 13 | #include <gtest/gtest.h> |
9 | 14 |
|
10 | 15 | #include <cstddef> |
11 | 16 | #include <functional> |
| 17 | +#include <iostream> |
12 | 18 | #include <optional> |
13 | 19 | #include <type_traits> |
| 20 | +#include <variant> |
14 | 21 |
|
15 | 22 | namespace fixed_containers::rich_enums_detail |
16 | 23 | { |
@@ -117,6 +124,273 @@ static_assert(consteval_compare::equal<2, sizeof(TestRichEnumBool)>); |
117 | 124 | static_assert(consteval_compare::equal<sizeof(TestRichEnumBool), |
118 | 125 | sizeof(detail::TestRichEnumBoolBackingEnum) + 1>); |
119 | 126 |
|
| 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 | + |
120 | 394 | TEST(BuiltinEnumAdapter, Ordinal) |
121 | 395 | { |
122 | 396 | { |
|
0 commit comments