2
0

ReflectableEnums.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <string_view>
  10. // preprocessor sadness. would not have to do that if we had BOOST.PP
  11. #define MSVCFIX_EXPAND(x) x
  12. #define AZ_FOR_EACH_1(pppredicate, val, x) pppredicate(x, val)
  13. #define AZ_FOR_EACH_2(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_1 (pppredicate, val + 1, __VA_ARGS__))
  14. #define AZ_FOR_EACH_3(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_2 (pppredicate, val + 1, __VA_ARGS__))
  15. #define AZ_FOR_EACH_4(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_3 (pppredicate, val + 1, __VA_ARGS__))
  16. #define AZ_FOR_EACH_5(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_4 (pppredicate, val + 1, __VA_ARGS__))
  17. #define AZ_FOR_EACH_6(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_5 (pppredicate, val + 1, __VA_ARGS__))
  18. #define AZ_FOR_EACH_7(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_6 (pppredicate, val + 1, __VA_ARGS__))
  19. #define AZ_FOR_EACH_8(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_7 (pppredicate, val + 1, __VA_ARGS__))
  20. #define AZ_FOR_EACH_9(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_8 (pppredicate, val + 1, __VA_ARGS__))
  21. #define AZ_FOR_EACH_10(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_9 (pppredicate, val + 1, __VA_ARGS__))
  22. #define AZ_FOR_EACH_11(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_10(pppredicate, val + 1, __VA_ARGS__))
  23. #define AZ_FOR_EACH_12(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_11(pppredicate, val + 1, __VA_ARGS__))
  24. #define AZ_FOR_EACH_13(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_12(pppredicate, val + 1, __VA_ARGS__))
  25. #define AZ_FOR_EACH_14(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_13(pppredicate, val + 1, __VA_ARGS__))
  26. #define AZ_FOR_EACH_15(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_14(pppredicate, val + 1, __VA_ARGS__))
  27. #define AZ_FOR_EACH_16(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_15(pppredicate, val + 1, __VA_ARGS__))
  28. #define AZ_FOR_EACH_17(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_16(pppredicate, val + 1, __VA_ARGS__))
  29. #define AZ_FOR_EACH_18(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_17(pppredicate, val + 1, __VA_ARGS__))
  30. #define AZ_FOR_EACH_19(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_18(pppredicate, val + 1, __VA_ARGS__))
  31. #define AZ_FOR_EACH_20(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_19(pppredicate, val + 1, __VA_ARGS__))
  32. #define AZ_FOR_EACH_21(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_20(pppredicate, val + 1, __VA_ARGS__))
  33. #define AZ_FOR_EACH_22(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_21(pppredicate, val + 1, __VA_ARGS__))
  34. #define AZ_FOR_EACH_23(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_22(pppredicate, val + 1, __VA_ARGS__))
  35. #define AZ_FOR_EACH_24(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_23(pppredicate, val + 1, __VA_ARGS__))
  36. #define AZ_FOR_EACH_25(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_24(pppredicate, val + 1, __VA_ARGS__))
  37. #define AZ_FOR_EACH_26(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_25(pppredicate, val + 1, __VA_ARGS__))
  38. #define AZ_FOR_EACH_27(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_26(pppredicate, val + 1, __VA_ARGS__))
  39. #define AZ_FOR_EACH_28(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_27(pppredicate, val + 1, __VA_ARGS__))
  40. #define AZ_FIRST_ARG_(N, ...) N
  41. #define AZ_FIRST_ARG(args) AZ_FIRST_ARG_ args // hack needed for MSVC bug
  42. // Laurent Deniau's method
  43. #define AZ_FOR_EACH_NARG(...) AZ_FOR_EACH_NARG_(__VA_ARGS__, AZ_FOR_EACH_RSEQ_N())
  44. #define AZ_FOR_EACH_NARG_(...) MSVCFIX_EXPAND(AZ_FOR_EACH_ARG_N(__VA_ARGS__))
  45. #define AZ_FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, N, ...) N
  46. #define AZ_FOR_EACH_RSEQ_N() 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
  47. #define AZ_CONCATENATE(x,y) x##y
  48. #define AZ_FOR_EACH_(N, pppredicate, val, ...) MSVCFIX_EXPAND(AZ_CONCATENATE(AZ_FOR_EACH_, N)(pppredicate, val, __VA_ARGS__))
  49. // Final API:
  50. // use as such: AZ_FOR_EACH( LAMBDAMACRO_1ARG, LIST_OF_PARAMETERS* )
  51. #define AZ_FOR_EACH(pppredicate, ...)\
  52. AZ_FOR_EACH_(AZ_FOR_EACH_NARG(__VA_ARGS__), pppredicate, 0, __VA_ARGS__)
  53. #define GEN_ONE_ENUMERATOR_LINE(X, val) X,
  54. #define GEN_ONE_ENUMERATOR_LINE_INIT(X, val) X = val,
  55. #define GEN_ONE_ENUMERATOR_LINE_INIT_PWR(X, val) X = 0x00000001 << (val),
  56. #define GEN_ONE_CASE(X, val) case X: return #X;
  57. #define GEN_TO_STRING_FUNCTION(...)\
  58. static constexpr string_view ToStr(EnumType enumValue)\
  59. {\
  60. switch (enumValue)\
  61. {\
  62. AZ_FOR_EACH( GEN_ONE_CASE, __VA_ARGS__ )\
  63. default: break;\
  64. }\
  65. return "<error>";\
  66. }
  67. #define GEN_ONE_IF_STR_EQ(X, val) if (str == #X) return X;
  68. #define GEN_FROM_STRING_FUNCTION(...)\
  69. static constexpr EnumType FromStr(string_view str)\
  70. {\
  71. AZ_FOR_EACH( GEN_ONE_IF_STR_EQ, __VA_ARGS__ )\
  72. return EndEnumeratorSentinel_;\
  73. }
  74. // public API here
  75. #define MAKE_REFLECTABLE_ENUM_(EnumTypeName, EnumeratorInit, EnumeratorNextOp, ...)\
  76. struct EnumTypeName\
  77. {\
  78. /** the real internal enum type */\
  79. enum EnumType\
  80. {\
  81. AZ_FOR_EACH( EnumeratorInit, __VA_ARGS__ )\
  82. EnumeratorInit(EndEnumeratorSentinel_, AZ_FOR_EACH_NARG(__VA_ARGS__))\
  83. };\
  84. \
  85. template <auto... Values> struct MetaVals{};\
  86. /** meta programming access of enumerators in the form of a template value list */\
  87. using MetaValueList = MetaVals<\
  88. AZ_FOR_EACH( GEN_ONE_ENUMERATOR_LINE, __VA_ARGS__ )\
  89. EndEnumeratorSentinel_\
  90. >;\
  91. /** construction by default and by implicit conversion from the internal enum type */\
  92. EnumTypeName() = default;\
  93. EnumTypeName(EnumType val_) : m_value{val_} {}\
  94. /** stringifiers/serializer */\
  95. GEN_TO_STRING_FUNCTION(__VA_ARGS__)\
  96. GEN_FROM_STRING_FUNCTION(__VA_ARGS__)\
  97. /** currently held enumerator value. defaults constructs to end */\
  98. EnumType m_value = EndEnumeratorSentinel_;\
  99. /** conversion operator to avoid to have to leak .m_value everywhere */\
  100. operator EnumType() const { return m_value; }\
  101. struct Iterator\
  102. {\
  103. EnumType m_i;\
  104. using iterator_category = std::forward_iterator_tag;\
  105. using difference_type = int;\
  106. using value_type = EnumType;\
  107. using reference = EnumType&;\
  108. constexpr EnumType operator*() { return m_i; }\
  109. constexpr Iterator operator++() { m_i = static_cast<EnumType>(EnumeratorNextOp); return {m_i}; }\
  110. constexpr bool operator!=(const Iterator& rhs) const { return m_i != rhs.m_i; }\
  111. };\
  112. /** sub-type to make it clear on client sites */ \
  113. struct Enumerate\
  114. {\
  115. /** ranges API to list enumerators */ \
  116. constexpr Iterator begin() const { return Iterator{EnumType( AZ_FIRST_ARG((__VA_ARGS__)) )}; }\
  117. constexpr Iterator end() const { return Iterator{EndEnumeratorSentinel_}; }\
  118. };\
  119. /** query: "is current value any of these ... ?" */\
  120. bool IsOneOf(EnumType rhs) const { return m_value == rhs; }\
  121. /** query: "is current value any of these ... ?" */\
  122. template <typename... Args> bool IsOneOf(EnumType rhsHead, Args... packTail) const\
  123. {\
  124. return m_value == rhsHead || IsOneOf(packTail...);\
  125. }\
  126. /** direct value checks: avoid to need to access .m_value (to look/behave like a native enum) */\
  127. friend bool operator==(EnumTypeName lhs, EnumTypeName::EnumType rhs) { return lhs.m_value == rhs; }\
  128. friend bool operator==(EnumTypeName::EnumType lhs, EnumTypeName rhs ) { return lhs == rhs.m_value; }\
  129. friend bool operator==(EnumTypeName lhs, EnumTypeName rhs ) { return lhs.m_value == rhs.m_value; }\
  130. friend bool operator!=(EnumTypeName lhs, EnumTypeName::EnumType rhs) { return lhs.m_value != rhs; }\
  131. friend bool operator!=(EnumTypeName::EnumType lhs, EnumTypeName rhs ) { return lhs != rhs.m_value; }\
  132. friend bool operator!=(EnumTypeName lhs, EnumTypeName rhs ) { return lhs.m_value != rhs.m_value; }\
  133. }
  134. #define MAKE_REFLECTABLE_ENUM(EnumTypeName, ...) MAKE_REFLECTABLE_ENUM_(EnumTypeName, GEN_ONE_ENUMERATOR_LINE_INIT, m_i + 1, __VA_ARGS__)
  135. #define MAKE_REFLECTABLE_ENUM_POWER(EnumTypeName, ...) MAKE_REFLECTABLE_ENUM_(EnumTypeName, GEN_ONE_ENUMERATOR_LINE_INIT_PWR, (m_i == 0 ? 1 : m_i << 1), __VA_ARGS__)
  136. // can't use an "enum class" because scopes are not flexible (can't `using`).
  137. // free functions becomes un-implementable because of the need for a "closure" predicate in az_foreach
  138. // test:
  139. #ifndef NDEBUG
  140. #include <cassert>
  141. namespace AZ::Tests
  142. {
  143. MAKE_REFLECTABLE_ENUM(MyEnum,
  144. Enumerand1, Enumerand2, Enumerand3);
  145. static_assert(MyEnum::Enumerand3 == 2);
  146. static_assert(MyEnum::ToStr(MyEnum::Enumerand3) == "Enumerand3");
  147. static_assert(MyEnum::FromStr("Enumerand3") == MyEnum::Enumerand3);
  148. // POWER version
  149. MAKE_REFLECTABLE_ENUM_POWER(MyEnumFlaggable,
  150. Flag1, Flag2, Flag3);
  151. // test executed:
  152. inline void DoAsserts4()
  153. {
  154. int i = 0;
  155. for (auto e : MyEnum::Enumerate{})
  156. {
  157. if (i == 0) assert(e == MyEnum::Enumerand1);
  158. if (i == 1) assert(e == MyEnum::Enumerand2);
  159. if (i == 2) assert(e == MyEnum::Enumerand3);
  160. ++i;
  161. }
  162. MyEnum e;
  163. e = MyEnum::Enumerand2;
  164. assert(e.IsOneOf(MyEnum::Enumerand1, MyEnum::Enumerand2));
  165. assert(!e.IsOneOf(MyEnum::Enumerand3));
  166. assert(!e.IsOneOf(MyEnum::Enumerand1, MyEnum::Enumerand3));
  167. i = 0;
  168. for (auto e : MyEnumFlaggable::Enumerate{})
  169. {
  170. assert(e == 1 << i);
  171. ++i;
  172. }
  173. static_assert(MyEnumFlaggable::ToStr(MyEnumFlaggable::Flag1) == "Flag1");
  174. static_assert(MyEnumFlaggable::FromStr("Flag2") == MyEnumFlaggable::Flag2);
  175. } // builds cleanly with gcc 8 --pedantic, clang 7 --pedantic, msvc 2017 /permissive- /Wall
  176. }
  177. #endif