/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once #include #include #include /// Enum macro specifically for state-machines. /// Has several limitations, some intentional. For example, /// the states must have automatic, sequential, values. /// But also has some advantages, for example it can be used inside classes. /// Some ideas from https://stackoverflow.com/questions/28828957/enum-to-string-in-modern-c11-c14-c17-and-future-c20 /// and from https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms #define STRINGIFY1(_, e) #e, #define STRINGIFY2(NAME, e) #NAME "::" #e, #define CONCAT(X, Y) X##Y #define CALL(X, ...) X(__VA_ARGS__) #define APPLY1(MACRO, NAME, e) MACRO(NAME, e) #define APPLY2(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY1(MACRO, NAME, __VA_ARGS__) #define APPLY3(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY2(MACRO, NAME, __VA_ARGS__) #define APPLY4(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY3(MACRO, NAME, __VA_ARGS__) #define APPLY5(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY4(MACRO, NAME, __VA_ARGS__) #define APPLY6(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY5(MACRO, NAME, __VA_ARGS__) #define APPLY7(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY6(MACRO, NAME, __VA_ARGS__) #define APPLY8(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY7(MACRO, NAME, __VA_ARGS__) #define APPLY9(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY8(MACRO, NAME, __VA_ARGS__) #define APPLY10(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY9(MACRO, NAME, __VA_ARGS__) #define APPLY11(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY10(MACRO, NAME, __VA_ARGS__) #define APPLY12(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY11(MACRO, NAME, __VA_ARGS__) #define APPLY13(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY12(MACRO, NAME, __VA_ARGS__) #define APPLY14(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY13(MACRO, NAME, __VA_ARGS__) #define APPLY15(MACRO, NAME, e, ...) MACRO(NAME, e) APPLY14(MACRO, NAME, __VA_ARGS__) // Credit to Anton Bachin for this trick. #define GET_COUNT(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, c, ...) c #define COUNT_ARGS(...) GET_COUNT(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) #define APPLY(MACRO, NAME, ...) \ CALL(CONCAT, APPLY, COUNT_ARGS(__VA_ARGS__))(MACRO, NAME, __VA_ARGS__) #define FOR_EACH(MACRO, NAME, ...) APPLY(MACRO, NAME, __VA_ARGS__) /// Define a state-machine with various independent states. /// NAME is the name of the state enum followed by the state names. #define STATE_ENUM(NAME, ...) \ enum class NAME : char; \ /* Returns the state name only, without the namespace. */ \ static inline const char* nameShort(NAME e) \ { \ static const char* const NAME##_names[] = { FOR_EACH(STRINGIFY1, NAME, __VA_ARGS__) }; \ assert(static_cast(e) < sizeof(NAME##_names) / sizeof(NAME##_names[0]) && \ "Enum value is out of range."); \ return NAME##_names[static_cast(e)]; \ } \ /* Returns the state name only, without the namespace, as a std::string. */ \ static inline std::string toStringShort(NAME e) \ { \ return nameShort(e); \ } \ /* Returns the state name with the namespace. */ \ static inline const char* name(NAME e) \ { \ static const char* const NAME##_names[] = { FOR_EACH(STRINGIFY2, NAME, __VA_ARGS__) }; \ assert(static_cast(e) < sizeof(NAME##_names) / sizeof(NAME##_names[0]) && \ "Enum value is out of range."); \ return NAME##_names[static_cast(e)]; \ } \ /* Returns the state name, with the namespace, as a std::string. */ \ static inline std::string toString(NAME e) \ { \ return name(e); \ } \ enum class NAME : char \ { \ __VA_ARGS__ \ } /// Support seamless serialization of STATE_ENUM to ostream. template ::value>::type* = nullptr> inline std::ostream& operator<<(std::ostream& os, const T state) { os << name(state); return os; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */