summaryrefslogtreecommitdiffstats
path: root/common/StateEnum.hpp
blob: 04149016ea4054c4443ee43f5549928a8d8b6718 (plain)
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* -*- 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 <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 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;                                                                        \
    static 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)];                                                  \
    }                                                                                              \
    static std::string toString(NAME e) { return name(e); }                                        \
    enum class NAME : char                                                                         \
    {                                                                                              \
        __VA_ARGS__                                                                                \
    }

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */