123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #pragma once
- #include <string_view>
- // preprocessor sadness. would not have to do that if we had BOOST.PP
- #define MSVCFIX_EXPAND(x) x
- #define AZ_FOR_EACH_1(pppredicate, val, x) pppredicate(x, val)
- #define AZ_FOR_EACH_2(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_1 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_3(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_2 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_4(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_3 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_5(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_4 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_6(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_5 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_7(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_6 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_8(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_7 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_9(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_8 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_10(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_9 (pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_11(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_10(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_12(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_11(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_13(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_12(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_14(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_13(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_15(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_14(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_16(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_15(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_17(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_16(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_18(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_17(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_19(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_18(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_20(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_19(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_21(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_20(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_22(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_21(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_23(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_22(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_24(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_23(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_25(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_24(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_26(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_25(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_27(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_26(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FOR_EACH_28(pppredicate, val, x, ...) pppredicate(x, val) MSVCFIX_EXPAND(AZ_FOR_EACH_27(pppredicate, val + 1, __VA_ARGS__))
- #define AZ_FIRST_ARG_(N, ...) N
- #define AZ_FIRST_ARG(args) AZ_FIRST_ARG_ args // hack needed for MSVC bug
- // Laurent Deniau's method
- #define AZ_FOR_EACH_NARG(...) AZ_FOR_EACH_NARG_(__VA_ARGS__, AZ_FOR_EACH_RSEQ_N())
- #define AZ_FOR_EACH_NARG_(...) MSVCFIX_EXPAND(AZ_FOR_EACH_ARG_N(__VA_ARGS__))
- #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
- #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
- #define AZ_CONCATENATE(x,y) x##y
- #define AZ_FOR_EACH_(N, pppredicate, val, ...) MSVCFIX_EXPAND(AZ_CONCATENATE(AZ_FOR_EACH_, N)(pppredicate, val, __VA_ARGS__))
- // Final API:
- // use as such: AZ_FOR_EACH( LAMBDAMACRO_1ARG, LIST_OF_PARAMETERS* )
- #define AZ_FOR_EACH(pppredicate, ...)\
- AZ_FOR_EACH_(AZ_FOR_EACH_NARG(__VA_ARGS__), pppredicate, 0, __VA_ARGS__)
- #define GEN_ONE_ENUMERATOR_LINE(X, val) X,
- #define GEN_ONE_ENUMERATOR_LINE_INIT(X, val) X = val,
- #define GEN_ONE_ENUMERATOR_LINE_INIT_PWR(X, val) X = 0x00000001 << (val),
- #define GEN_ONE_CASE(X, val) case X: return #X;
- #define GEN_TO_STRING_FUNCTION(...)\
- static constexpr string_view ToStr(EnumType enumValue)\
- {\
- switch (enumValue)\
- {\
- AZ_FOR_EACH( GEN_ONE_CASE, __VA_ARGS__ )\
- default: break;\
- }\
- return "<error>";\
- }
- #define GEN_ONE_IF_STR_EQ(X, val) if (str == #X) return X;
- #define GEN_FROM_STRING_FUNCTION(...)\
- static constexpr EnumType FromStr(string_view str)\
- {\
- AZ_FOR_EACH( GEN_ONE_IF_STR_EQ, __VA_ARGS__ )\
- return EndEnumeratorSentinel_;\
- }
- // public API here
- #define MAKE_REFLECTABLE_ENUM_(EnumTypeName, EnumeratorInit, EnumeratorNextOp, ...)\
- struct EnumTypeName\
- {\
- /** the real internal enum type */\
- enum EnumType\
- {\
- AZ_FOR_EACH( EnumeratorInit, __VA_ARGS__ )\
- EnumeratorInit(EndEnumeratorSentinel_, AZ_FOR_EACH_NARG(__VA_ARGS__))\
- };\
- \
- template <auto... Values> struct MetaVals{};\
- /** meta programming access of enumerators in the form of a template value list */\
- using MetaValueList = MetaVals<\
- AZ_FOR_EACH( GEN_ONE_ENUMERATOR_LINE, __VA_ARGS__ )\
- EndEnumeratorSentinel_\
- >;\
- /** construction by default and by implicit conversion from the internal enum type */\
- EnumTypeName() = default;\
- EnumTypeName(EnumType val_) : m_value{val_} {}\
- /** stringifiers/serializer */\
- GEN_TO_STRING_FUNCTION(__VA_ARGS__)\
- GEN_FROM_STRING_FUNCTION(__VA_ARGS__)\
- /** currently held enumerator value. defaults constructs to end */\
- EnumType m_value = EndEnumeratorSentinel_;\
- /** conversion operator to avoid to have to leak .m_value everywhere */\
- operator EnumType() const { return m_value; }\
- struct Iterator\
- {\
- EnumType m_i;\
- using iterator_category = std::forward_iterator_tag;\
- using difference_type = int;\
- using value_type = EnumType;\
- using reference = EnumType&;\
- constexpr EnumType operator*() { return m_i; }\
- constexpr Iterator operator++() { m_i = static_cast<EnumType>(EnumeratorNextOp); return {m_i}; }\
- constexpr bool operator!=(const Iterator& rhs) const { return m_i != rhs.m_i; }\
- };\
- /** sub-type to make it clear on client sites */ \
- struct Enumerate\
- {\
- /** ranges API to list enumerators */ \
- constexpr Iterator begin() const { return Iterator{EnumType( AZ_FIRST_ARG((__VA_ARGS__)) )}; }\
- constexpr Iterator end() const { return Iterator{EndEnumeratorSentinel_}; }\
- };\
- /** query: "is current value any of these ... ?" */\
- bool IsOneOf(EnumType rhs) const { return m_value == rhs; }\
- /** query: "is current value any of these ... ?" */\
- template <typename... Args> bool IsOneOf(EnumType rhsHead, Args... packTail) const\
- {\
- return m_value == rhsHead || IsOneOf(packTail...);\
- }\
- /** direct value checks: avoid to need to access .m_value (to look/behave like a native enum) */\
- friend bool operator==(EnumTypeName lhs, EnumTypeName::EnumType rhs) { return lhs.m_value == rhs; }\
- friend bool operator==(EnumTypeName::EnumType lhs, EnumTypeName rhs ) { return lhs == rhs.m_value; }\
- friend bool operator==(EnumTypeName lhs, EnumTypeName rhs ) { return lhs.m_value == rhs.m_value; }\
- friend bool operator!=(EnumTypeName lhs, EnumTypeName::EnumType rhs) { return lhs.m_value != rhs; }\
- friend bool operator!=(EnumTypeName::EnumType lhs, EnumTypeName rhs ) { return lhs != rhs.m_value; }\
- friend bool operator!=(EnumTypeName lhs, EnumTypeName rhs ) { return lhs.m_value != rhs.m_value; }\
- }
- #define MAKE_REFLECTABLE_ENUM(EnumTypeName, ...) MAKE_REFLECTABLE_ENUM_(EnumTypeName, GEN_ONE_ENUMERATOR_LINE_INIT, m_i + 1, __VA_ARGS__)
- #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__)
- // can't use an "enum class" because scopes are not flexible (can't `using`).
- // free functions becomes un-implementable because of the need for a "closure" predicate in az_foreach
- // test:
- #ifndef NDEBUG
- #include <cassert>
- namespace AZ::Tests
- {
- MAKE_REFLECTABLE_ENUM(MyEnum,
- Enumerand1, Enumerand2, Enumerand3);
- static_assert(MyEnum::Enumerand3 == 2);
- static_assert(MyEnum::ToStr(MyEnum::Enumerand3) == "Enumerand3");
- static_assert(MyEnum::FromStr("Enumerand3") == MyEnum::Enumerand3);
- // POWER version
- MAKE_REFLECTABLE_ENUM_POWER(MyEnumFlaggable,
- Flag1, Flag2, Flag3);
- // test executed:
- inline void DoAsserts4()
- {
- int i = 0;
- for (auto e : MyEnum::Enumerate{})
- {
- if (i == 0) assert(e == MyEnum::Enumerand1);
- if (i == 1) assert(e == MyEnum::Enumerand2);
- if (i == 2) assert(e == MyEnum::Enumerand3);
- ++i;
- }
- MyEnum e;
- e = MyEnum::Enumerand2;
- assert(e.IsOneOf(MyEnum::Enumerand1, MyEnum::Enumerand2));
- assert(!e.IsOneOf(MyEnum::Enumerand3));
- assert(!e.IsOneOf(MyEnum::Enumerand1, MyEnum::Enumerand3));
- i = 0;
- for (auto e : MyEnumFlaggable::Enumerate{})
- {
- assert(e == 1 << i);
- ++i;
- }
- static_assert(MyEnumFlaggable::ToStr(MyEnumFlaggable::Flag1) == "Flag1");
- static_assert(MyEnumFlaggable::FromStr("Flag2") == MyEnumFlaggable::Flag2);
- } // builds cleanly with gcc 8 --pedantic, clang 7 --pedantic, msvc 2017 /permissive- /Wall
- }
- #endif
|