6b29335ccf
In some cases we don't really need the full name of an enum, with the enum as namespace. For those cases, we now support nameShort and toStringShort. Change-Id: Iddd383e15d900bb2d2f3d77e3de6a09470d51b62 Signed-off-by: Ashod Nakashian <ashod.nakashian@collabora.co.uk>
94 lines
6.1 KiB
C++
94 lines
6.1 KiB
C++
/* -*- 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 <iosfwd>
|
|
#include <type_traits>
|
|
#include <string>
|
|
|
|
/// 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<unsigned>(e) < sizeof(NAME##_names) / sizeof(NAME##_names[0]) && \
|
|
"Enum value is out of range."); \
|
|
return NAME##_names[static_cast<int>(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<unsigned>(e) < sizeof(NAME##_names) / sizeof(NAME##_names[0]) && \
|
|
"Enum value is out of range."); \
|
|
return NAME##_names[static_cast<int>(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 <typename T, typename std::enable_if<
|
|
std::is_same<decltype(name(T())), const char*>::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: */
|