zeusUnnamed repository; edit this file 'description' to name the repository. |
git clone Unknown |
Log | Files | Refs |
enum_strong.cpp (22160B)
0 // Adapted from:
1 // https://gist.github.com/HertzDevil/43e8acbe5d73aba7610dcc1e79f17f3d
3 #include <concepts>
4 #include <limits>
5 #include <type_traits>
7 // The default enumeration category. Conversion is equivalent to static_cast.
8 // Unspecialized enumeration traits use this category.
9 struct enum_default {};
11 // The standard-layout enumeration category. Values outside the given range are
12 // mapped to a given none-element. Requires that:
13 // - If both EnumT::min and EnumT::max are given, then EnumT::min <= EnumT::max
14 // - EnumT::none must be given, and !(EnumT::min <= EnumT::none <= EnumT::max)
15 // EnumT::min and EnumT::max default to the minimum / maximum representable
16 // value of the underlying type if not given.
17 // Standard enum class types additionally support operator!.
18 struct enum_standard {};
20 // The linear enumeration category. Values not equal to the given none-element
21 // are clipped to the given range. Requires that:
22 // - If both EnumT::min and EnumT::max are given, then EnumT::min <= EnumT::max
23 // - EnumT::none must be given, and !(EnumT::min <= EnumT::none <= EnumT::max)
24 // EnumT::min and EnumT::max default to the minimum / maximum representable
25 // value of the underlying type respectively if not given.
26 // Linear enum class types additionally support !, ++, and --.
27 struct enum_linear {};
29 // The bitmask enumeration category. Bits common to the given range limits are
30 // kept constant. Requires that:
31 // - If both EnumT::min and EnumT::max are given, then all of the set bits of
32 // EnumT::min must also be set in EnumT::max
33 // - The underlying type of EnumT must be unsigned
34 // EnumT::min and EnumT::max default to the minimum / maximum representable
35 // value of the underlying type respectively if not given.
36 // Bitmask enum class types additionally support &, &=, |, |=, ^, ^=, and ~.
37 struct enum_bitmask {};
39 // The discrete enumeration category. Values not equal to any of the given
40 // template parameters are mapped to the given none-element.
41 // zero. Requires that:
42 // - EnumT::none must be given, and Vals... must not contain EnumT::none
43 // Discrete enum class types additionally support operator!.
44 template <class EnumT, EnumT... Vals> struct enum_discrete {};
46 // The enumeration traits class template. Contains only one member typedef:
47 // - category: The enumeration category to use for this enumeration type
48 // User-defined enumeration types may specialize this template.
49 template <class EnumT> struct enum_traits {
50 using category = enum_default;
51 };
53 // The enumeration category traits class template. Contains:
54 // - typedef CatT category;
55 // - template <class EnumT> static bool valid()
56 // Checks during compile-time whether the given enumeration type satisfies
57 // the category's constraints. (will become a concept bool later)
58 // - template <class EnumT, class ValT> static EnumT enum_cast(ValT x)
59 // Maps a given value to the enumeration type.
60 template <class CatT> struct enum_category_traits;
62 namespace details {
64 template <class EnumT>
65 constexpr bool is_scoped_enum_f(std::false_type) noexcept {
66 return false;
67 }
69 template <class EnumT>
70 constexpr bool is_scoped_enum_f(std::true_type) noexcept {
71 return !std::is_convertible_v<EnumT, std::underlying_type_t<EnumT>>;
72 }
74 template <class EnumT>
75 struct is_scoped_enum
76 : std::integral_constant<bool,
77 is_scoped_enum_f<EnumT>(std::is_enum<EnumT>())> {};
79 template <class EnumT>
80 inline constexpr bool is_scoped_enum_v = is_scoped_enum<EnumT>::value;
82 template <class EnumT> constexpr auto value_cast_impl(EnumT x) noexcept {
83 return static_cast<std::underlying_type_t<EnumT>>(x);
84 }
86 template <class CatT> struct is_enum_category_discrete : std::false_type {};
88 template <class EnumT, EnumT... Vals>
89 struct is_enum_category_discrete<enum_discrete<EnumT, Vals...>>
90 : std::true_type {};
92 template <class CatT>
93 concept is_enum_category =
94 requires { typename enum_category_traits<CatT>::category; };
96 template <class EnumT>
97 concept has_enum_category = requires {
98 typename enum_traits<EnumT>::category;
99 requires(is_enum_category<typename enum_traits<EnumT>::category>);
100 };
102 template <class EnumT> struct get_enum_category {
103 using type = enum_default;
104 };
106 template <class EnumT>
107 requires has_enum_category<EnumT>
108 struct get_enum_category<EnumT> {
109 using type = typename enum_traits<EnumT>::category;
110 };
112 } // namespace details
114 // Obtains the category for a given enumeration type, defaulting to enum_default
115 // if a valid category cannot be found.
116 template <class EnumT>
117 using get_enum_category_t = typename details::get_enum_category<EnumT>::type;
119 template <class EnumT, class category>
120 concept is_enum_category = std::same_as<get_enum_category_t<EnumT>, category>;
122 namespace details {
124 template <class T> constexpr T clamp(T x, T lo, T hi) noexcept {
125 return x < lo ? lo : (x > hi ? hi : x);
126 }
128 template <class T>
129 requires std::is_unsigned_v<std::decay_t<T>>
130 constexpr T bitwise_clamp(T x, T lo, T hi) noexcept {
131 return (x & hi) | lo;
132 }
134 template <class EnumT>
135 concept enum_has_none_member = requires {
136 requires(std::is_enum_v<EnumT>);
137 { EnumT::none } -> std::same_as<EnumT>;
138 };
140 template <class EnumT>
141 concept enum_has_min_member = requires {
142 requires(std::is_enum_v<EnumT>);
143 { EnumT::min } -> std::same_as<EnumT>;
144 };
146 template <class EnumT>
147 concept enum_has_max_member = requires {
148 requires(std::is_enum_v<EnumT>);
149 { EnumT::max } -> std::same_as<EnumT>;
150 };
152 } // namespace details
154 template <class EnumT>
155 concept is_enum = std::is_enum_v<EnumT>;
157 // Checks whether the given enumeration type has a none-element.
158 template <class EnumT>
159 concept enum_has_none =
160 requires { requires(details::enum_has_none_member<EnumT>); };
162 // Obtains the none-element of a given enumeration type.
163 template <enum_has_none EnumT> constexpr EnumT enum_none() noexcept {
164 if constexpr (details::enum_has_none_member<EnumT>) {
165 return EnumT::none;
166 }
167 }
169 // Checks whether the given enumeration type has a minimum element.
170 template <class EnumT>
171 concept enum_has_min = requires {
172 requires(is_enum<EnumT>);
173 requires(
174 !details::is_enum_category_discrete<get_enum_category_t<EnumT>>::value);
175 };
177 // Obtains the minimum element of a given enumeration type.
178 template <enum_has_min EnumT> constexpr EnumT enum_min() noexcept {
179 if constexpr (details::enum_has_min_member<EnumT>) {
180 return EnumT::min;
181 }
183 return EnumT{std::numeric_limits<std::underlying_type_t<EnumT>>::min()};
184 }
186 // Checks whether the given enumeration type has a maximum element.
187 template <class EnumT>
188 concept enum_has_max = requires {
189 requires(is_enum<EnumT>);
190 requires(
191 !details::is_enum_category_discrete<get_enum_category_t<EnumT>>::value);
192 };
194 // Obtains the maximum element of a given enumeration type.
195 template <enum_has_max EnumT> constexpr EnumT enum_max() noexcept {
196 if constexpr (details::enum_has_max_member<EnumT>) {
197 return EnumT::max;
198 }
200 return EnumT{std::numeric_limits<std::underlying_type_t<EnumT>>::max()};
201 }
203 // Casts an enumeration value to its underlying type.
204 template <is_enum EnumT,
205 class CatTraits = enum_category_traits<get_enum_category_t<EnumT>>>
206 constexpr auto value_cast(EnumT x) noexcept {
207 static_assert(CatTraits::template valid<EnumT>());
208 return details::value_cast_impl(x);
209 }
211 template <class ValT, class EnumT>
212 concept can_cast = requires {
213 requires(std::is_convertible_v<ValT, std::underlying_type_t<EnumT>>);
214 };
216 // Casts a value to the given enumeration type.
217 template <is_enum EnumT, can_cast<EnumT> ValT,
218 typename CatTraits = enum_category_traits<get_enum_category_t<EnumT>>>
219 constexpr EnumT enum_cast(ValT x) noexcept {
220 static_assert(CatTraits::template valid<EnumT>());
221 return CatTraits::template enum_cast<EnumT>(x);
222 }
224 // Constrains a given value by the given enumeration type, as if by casting it
225 // to and from the enumeration type.
226 template <is_enum EnumT, can_cast<EnumT> ValT>
227 constexpr std::underlying_type_t<EnumT> value_cast(ValT x) noexcept {
228 return value_cast(enum_cast<EnumT>(x));
229 }
231 // Constrains the given enumeration type, as if by casting it to and from its
232 // underlying type.
233 template <is_enum EnumT,
234 typename CatTraits = enum_category_traits<get_enum_category_t<EnumT>>>
235 constexpr EnumT enum_cast(EnumT x) noexcept {
236 return enum_cast<EnumT>(value_cast(x));
237 }
239 inline namespace enum_operators {
241 // Returns true if lhs is equal to EnumT::none. Only supports enum class types
242 // that have a none-element.
243 template <class EnumT>
244 requires(details::is_scoped_enum_v<EnumT> && enum_has_none<EnumT>)
245 constexpr bool operator!(const EnumT &lhs) noexcept {
246 return lhs == enum_none<EnumT>();
247 }
249 template <class EnumT>
250 concept is_linear_enum_class = requires {
251 requires(details::is_scoped_enum_v<EnumT>);
252 requires(is_enum_category<EnumT, enum_linear>);
253 };
255 template <class EnumT>
256 concept is_bitmask_enum_class = requires {
257 requires(details::is_scoped_enum_v<EnumT>);
258 requires(is_enum_category<EnumT, enum_bitmask>);
259 };
261 // Pre-increments lhs if it is equal to neither the none-element nor the maximum
262 // element. Only supports linear enum class types.
263 template <is_linear_enum_class EnumT>
264 constexpr EnumT &operator++(EnumT &lhs) noexcept {
265 if (!(!lhs || lhs == enum_max<EnumT>())) {
266 lhs = enum_cast<EnumT>(value_cast(lhs) + 1);
267 }
268 return lhs;
269 }
271 // Post-increments lhs if it is equal to neither the none-element nor the
272 // maximum element. Only supports linear enum class types.
273 template <is_linear_enum_class EnumT>
274 constexpr EnumT operator++(const EnumT &lhs, int) noexcept {
275 EnumT ret = lhs;
276 ++lhs;
277 return ret;
278 }
280 // Pre-decrements lhs if it is equal to neither the none-element nor the maximum
281 // element. Only supports linear enum class types.
282 template <is_linear_enum_class EnumT>
283 constexpr EnumT &operator--(EnumT &lhs) noexcept {
284 if (!(!lhs || lhs == enum_min<EnumT>())) {
285 lhs = enum_cast<EnumT>(value_cast(lhs) - 1);
286 }
287 return lhs;
288 }
290 // Post-decrements lhs if it is equal to neither the none-element nor the
291 // maximum element. Only supports linear enum class types.
292 template <is_linear_enum_class EnumT>
293 constexpr EnumT operator--(const EnumT &lhs, int) noexcept {
294 EnumT ret = lhs;
295 --lhs;
296 return ret;
297 }
299 // If neither operand is EnumT::None, returns the union of the two bit masks.
300 // Otherwise returns EnumT::None. Only supports bitmask enum class types.
301 template <is_bitmask_enum_class EnumT>
302 constexpr EnumT operator|(const EnumT &lhs, const EnumT &rhs) noexcept {
303 if constexpr (enum_has_none<EnumT>) {
304 if (!lhs || !rhs) {
305 return enum_none<EnumT>();
306 }
307 }
308 return enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
309 value_cast(lhs) | value_cast(rhs)));
310 }
312 // If neither operand is EnumT::None, assigns (lhs | rhs) to lhs. The operands
313 // are not interchangeable because (lhs |= EnumT::None) != (lhs | EnumT::None).
314 // Only supports bitmask enum class types.
315 template <is_bitmask_enum_class EnumT>
316 constexpr EnumT &operator|=(EnumT &lhs, const EnumT &rhs) noexcept {
317 if constexpr (enum_has_none<EnumT>) {
318 if (!(!lhs || !rhs)) {
319 lhs = enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
320 value_cast(lhs) | value_cast(rhs)));
321 }
322 } else {
323 lhs = enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
324 value_cast(lhs) | value_cast(rhs)));
325 }
326 return lhs;
327 }
329 // If neither operand is EnumT::None, returns the intersection of the two bit
330 // masks. Otherwise returns EnumT::None. Only supports bitmask enum class types.
331 template <is_bitmask_enum_class EnumT>
332 constexpr EnumT operator&(const EnumT &lhs, const EnumT &rhs) noexcept {
333 if constexpr (enum_has_none<EnumT>) {
334 if (!lhs || !rhs) {
335 return enum_none<EnumT>();
336 }
337 }
338 return enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
339 value_cast(lhs) & value_cast(rhs)));
340 }
342 // If neither operand is EnumT::None, assigns (lhs & rhs) to lhs. The operands
343 // are not interchangeable because (lhs &= EnumT::None) != (lhs & EnumT::None).
344 // Only supports bitmask enum class types.
345 template <is_bitmask_enum_class EnumT>
346 constexpr EnumT &operator&=(EnumT &lhs, const EnumT &rhs) noexcept {
347 if constexpr (enum_has_none<EnumT>) {
348 if (!(!lhs || !rhs)) {
349 lhs = enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
350 value_cast(lhs) & value_cast(rhs)));
351 }
352 } else {
353 lhs = enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
354 value_cast(lhs) & value_cast(rhs)));
355 }
356 return lhs;
357 }
359 // If neither operand is EnumT::None, returns the symmetric difference of the
360 // two bit masks. Otherwise returns EnumT::None. Only supports bitmask enum
361 // class types.
362 template <is_bitmask_enum_class EnumT>
363 constexpr EnumT operator^(const EnumT &lhs, const EnumT &rhs) noexcept {
364 if constexpr (enum_has_none<EnumT>) {
365 if (!lhs || !rhs) {
366 return enum_none<EnumT>();
367 }
368 }
369 return enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
370 value_cast(lhs) ^ value_cast(rhs)));
371 }
373 // If neither operand is EnumT::None, assigns (lhs ^ rhs) to lhs. The operands
374 // are not interchangeable because (lhs ^= EnumT::None) != (lhs ^ EnumT::None).
375 // Only supports bitmask enum class types.
376 template <is_bitmask_enum_class EnumT>
377 constexpr EnumT &operator^=(EnumT &lhs, const EnumT &rhs) noexcept {
378 if constexpr (enum_has_none<EnumT>) {
379 if (!(!lhs || !rhs)) {
380 lhs = enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
381 value_cast(lhs) ^ value_cast(rhs)));
382 }
383 } else {
384 lhs = enum_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(
385 value_cast(lhs) ^ value_cast(rhs)));
386 }
387 return lhs;
388 }
390 // If lhs is not equal to EnumT::None, toggles all bits in lhs. Otherwise
391 // returns EnumT::None. Only supports bitmask enum class types.
392 template <is_bitmask_enum_class EnumT>
393 constexpr EnumT operator~(const EnumT &lhs) noexcept {
394 if constexpr (enum_has_none<EnumT>) {
395 if (!lhs) {
396 return enum_none<EnumT>();
397 }
398 }
399 return enum_cast<EnumT>(~value_cast(lhs));
400 }
402 } // namespace enum_operators
404 template <> struct enum_category_traits<enum_default> {
405 using category = enum_default;
407 template <is_enum_category<category> EnumT>
408 static constexpr bool valid() noexcept {
409 return true;
410 }
412 template <is_enum_category<category> EnumT, class ValT>
413 static constexpr EnumT enum_cast(ValT x) noexcept {
414 return static_cast<EnumT>(x);
415 }
416 };
418 template <> struct enum_category_traits<enum_standard> {
419 using category = enum_standard;
421 template <is_enum_category<category> EnumT>
422 static constexpr bool valid() noexcept {
423 if constexpr (enum_has_none<EnumT>) {
424 constexpr auto xnone = details::value_cast_impl(enum_none<EnumT>());
425 constexpr auto xmin = details::value_cast_impl(enum_min<EnumT>());
426 constexpr auto xmax = details::value_cast_impl(enum_max<EnumT>());
427 return xmin <= xmax && xnone != details::clamp(xnone, xmin, xmax);
428 } else {
429 return false;
430 }
431 }
433 template <is_enum_category<category> EnumT, class ValT>
434 static constexpr EnumT enum_cast(ValT x) noexcept {
435 constexpr auto xnone = details::value_cast_impl(enum_none<EnumT>());
436 constexpr auto xmin = details::value_cast_impl(enum_min<EnumT>());
437 constexpr auto xmax = details::value_cast_impl(enum_max<EnumT>());
438 return static_cast<EnumT>(x == details::clamp(x, xmin, xmax) ? x : xnone);
439 }
440 };
442 template <> struct enum_category_traits<enum_linear> {
443 using category = enum_linear;
445 template <is_enum_category<category> EnumT>
446 static constexpr bool valid() noexcept {
447 if constexpr (enum_has_none<EnumT>) {
448 constexpr auto xnone = details::value_cast_impl(enum_none<EnumT>());
449 constexpr auto xmin = details::value_cast_impl(enum_min<EnumT>());
450 constexpr auto xmax = details::value_cast_impl(enum_max<EnumT>());
451 return xmin <= xmax && xnone != details::clamp(xnone, xmin, xmax);
452 } else
453 return false;
454 }
456 template <is_enum_category<category> EnumT, class ValT>
457 static constexpr EnumT enum_cast(ValT x) noexcept {
458 constexpr auto xnone = details::value_cast_impl(enum_none<EnumT>());
459 constexpr auto xmin = details::value_cast_impl(enum_min<EnumT>());
460 constexpr auto xmax = details::value_cast_impl(enum_max<EnumT>());
461 return static_cast<EnumT>(x == xnone ? xnone
462 : details::clamp(x, xmin, xmax));
463 }
464 };
466 template <> struct enum_category_traits<enum_bitmask> {
467 using category = enum_bitmask;
469 template <is_enum_category<category> EnumT>
470 static constexpr bool valid() noexcept {
471 constexpr auto xmin = details::value_cast_impl(enum_min<EnumT>());
472 constexpr auto xmax = details::value_cast_impl(enum_max<EnumT>());
473 if constexpr (std::is_unsigned_v<std::underlying_type_t<EnumT>> &&
474 (xmin & xmax) == xmin) {
475 if constexpr (enum_has_none<EnumT>) {
476 constexpr auto xnone = details::value_cast_impl(enum_none<EnumT>());
477 return xnone != details::bitwise_clamp(xnone, xmin, xmax);
478 } else {
479 return true;
480 }
481 } else {
482 return false;
483 }
484 }
486 template <is_enum_category<category> EnumT, class ValT>
487 static constexpr EnumT enum_cast(ValT x) noexcept {
488 constexpr auto xmin = details::value_cast_impl(enum_min<EnumT>());
489 constexpr auto xmax = details::value_cast_impl(enum_max<EnumT>());
490 if constexpr (!enum_has_none<EnumT>)
491 return static_cast<EnumT>(
492 details::bitwise_clamp<decltype(xmin)>(x, xmin, xmax));
493 else {
494 constexpr auto xnone = details::value_cast_impl(enum_none<EnumT>());
495 return static_cast<EnumT>(
496 x == xnone ? xnone
497 : details::bitwise_clamp<decltype(xmin)>(x, xmin, xmax));
498 }
499 }
500 };
502 template <class EnumT, EnumT... Vals>
503 struct enum_category_traits<enum_discrete<EnumT, Vals...>> {
504 using category = enum_discrete<EnumT, Vals...>;
506 template <is_enum_category<category> EnumT_>
507 static constexpr bool valid() noexcept {
508 if constexpr (enum_has_none<EnumT_>) {
509 return std::is_same_v<EnumT_, EnumT> && !(... || (Vals == EnumT_::none));
510 } else {
511 return false;
512 }
513 }
515 template <is_enum_category<category> EnumT_, class ValT>
516 requires std::is_convertible_v<EnumT_, EnumT>
517 static constexpr EnumT enum_cast(ValT x) noexcept {
518 if ((... || (details::value_cast_impl(Vals) == x))) {
519 return static_cast<EnumT>(x);
520 }
521 return EnumT::none;
522 }
523 };
525 #define ENUM_CLASS_WITH_CATEGORY(NAME, TYPE, CATEGORY) \
526 enum class NAME : TYPE; \
527 template <> struct enum_traits<NAME> { \
528 using category = CATEGORY; \
529 }; \
530 enum class NAME : TYPE
532 // Defines a standard-layout enum class as by:
533 // enum class NAME : TYPE
534 // with a specialization of enum_traits<NAME>.
535 #define ENUM_CLASS_STANDARD(NAME, TYPE) \
536 ENUM_CLASS_WITH_CATEGORY(NAME, TYPE, enum_standard)
538 // Defines a linear enum class as by:
539 // enum class NAME : TYPE
540 // with a specialization of enum_traits<NAME>.
541 #define ENUM_CLASS_LINEAR(NAME, TYPE) \
542 ENUM_CLASS_WITH_CATEGORY(NAME, TYPE, enum_linear)
544 // Defines a bitmask enum class as by:
545 // enum class NAME : TYPE
546 // with a specialization of enum_traits<NAME>.
547 #define ENUM_CLASS_BITMASK(NAME, TYPE) \
548 ENUM_CLASS_WITH_CATEGORY(NAME, TYPE, enum_bitmask)
550 enum StructDefault : int { Default0, Default1, Default2 };
552 void a() {
553 value_cast(Default0); // (int)0
554 enum_cast<StructDefault>(0); // Default0
555 enum_cast<StructDefault>(3); // (StructDefault)3
556 }
558 ENUM_CLASS_STANDARD(StructStandard, unsigned){
559 s0, s1, s2, s3, none = (unsigned)-1, min = s0, max = s3,
560 };
562 void b() {
563 value_cast(StructStandard::s0); // (unsigned)0
564 enum_cast<StructStandard>(1u); // StructStandard::s1
565 enum_cast<StructStandard>((unsigned)-1); // StructStandard::none
566 enum_cast<StructStandard>(4u); // StructStandard::none
567 value_cast<StructStandard>(4u); // (unsigned)-1
568 enum_cast(StructStandard{4u}); // StructStandard::none
569 }
571 ENUM_CLASS_LINEAR(StructLinear, int){
572 l0, l1, l2, l3, none = -1, min = l0, max = l3,
573 };
575 void c() {
576 enum_cast<StructLinear>(0); // StructLinear::l0
577 enum_cast<StructLinear>(-1); // StructLinear::none
578 enum_cast<StructLinear>(3); // StructLinear::l3
579 enum_cast<StructLinear>(4); // StructLinear::l3
580 enum_cast<StructLinear>(-128); // StructLinear::l0
581 auto l = StructLinear::l2;
582 ++l; // StructLinear::l3
583 ++l; // StructLinear::l3
584 --l;
585 --l;
586 --l;
587 --l; // StructLinear::l0
588 }
590 ENUM_CLASS_BITMASK(StructBitmask, unsigned){
591 b0 = 1, b1 = 2, b3 = 8, min = 0, max = b0 | b1 | b3,
592 };
594 void d() {
595 enum_cast<StructBitmask>(0); // StructBitmask::min
596 enum_cast<StructBitmask>(2); // StructBitmask::b1
597 enum_cast<StructBitmask>(4); // StructBitmask::min
598 enum_cast<StructBitmask>(13); // StructBitmask::b0 | StructBitmask::b3
599 auto b = ~StructBitmask::b0; // StructBitmask::b1 | StructBitmask::b3
600 b ^= StructBitmask::max; // StructBitmask::b0
601 }
603 enum class StructDiscrete {
604 d0 = 123,
605 d1 = 456,
606 d2 = 789,
607 none = -1,
608 };
609 template <> struct enum_traits<StructDiscrete> {
610 using category = enum_discrete<StructDiscrete, StructDiscrete::d0,
611 StructDiscrete::d1, StructDiscrete::d2>;
612 };
614 void e() {
615 enum_cast<StructDiscrete>(0); // StructDiscrete::none
616 enum_cast<StructDiscrete>(123); // StructDiscrete::d0
617 enum_cast<StructDiscrete>(456); // StructDiscrete::d1
618 enum_cast<StructDiscrete>(789); // StructDiscrete::d2
619 enum_cast<StructDiscrete>(788); // StructDiscrete::none
620 }
622 #include <iostream>
624 void test(StructStandard d) {
625 std::cout << value_cast(d) << std::endl;
626 const auto t = enum_cast<StructStandard>(d);
627 std::cout << value_cast(t) << std::endl;
628 }
630 int main() { test(static_cast<StructStandard>(150)); }