-
Notifications
You must be signed in to change notification settings - Fork 0
/
auto.hh
160 lines (130 loc) · 4.68 KB
/
auto.hh
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#pragma once
#include <new>
#include <array>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <utility>
#include <typeinfo>
#include <iostream>
#include "type_utils.hxx"
template<class Auto>
struct NoopAutoLogger {
template<template<class> class NewState,
template<class> class OldState>
void log() {
}
};
template<template<class> class PInterface,
class AllowedTransitions,
class States,
template<class> class Logger = NoopAutoLogger>
class Auto {
using self_t = Auto<PInterface, AllowedTransitions, States, Logger>;
using intf_t = PInterface<self_t>;
template<template <class Proxy> class State>
struct Proxy {
using auto_t = Auto<PInterface, AllowedTransitions, States, Logger>;
using self_t = Proxy<State>;
using map_t = self_t;
using state_t = State<self_t>;
using intf_t = typename Auto::intf_t;
template<template<class> class DestState, class ...Args>
static void transit(Auto &a, Args&& ...args) {
a.free_transit<DestState, State, Args...>(std::forward<Args>(args)...);
}
};
using StatesProxies = decltype(States::template Tmap<Proxy>());
template<class Proxy>
struct ProxyState {
using map_t = typename Proxy::state_t;
};
using ProxiedStates = decltype(StatesProxies::template Tmap<ProxyState>());
struct StateData {
alignas(ProxiedStates::template reduce<AlignofReducer>()())
uint8_t state_data_[ProxiedStates::template reduce<SizeofReducer>()()];
template<class T>
T* get_data() {
T * volatile res = static_cast<T*>(static_cast<void*>(state_data_));
return /*std::launder(*/res/*)*/;
}
template<template <class> class State, class ...Args>
auto enter(Args&& ...args) {
// TODO: move this assert at class construction time
static_assert(std::is_base_of<intf_t, typename Proxy<State>::state_t>::value);
static_assert(StatesProxies::template contains<Proxy<State>>());
using tstate = typename Proxy<State>::state_t;
return new (&state_data_) tstate(std::forward<Args...>(args)...);
}
void leave() {
// The destructor must be virtual
get_data<intf_t>()->~intf_t();
}
};
// there are two banks of state memory.
// The second one is needed to safely build a new state
// while having the former's memory still valid
StateData data_[2];
// the current's state index
bool data_pos_;
Logger<self_t> logger;
StateData &cur_data() {
return data_[data_pos_];
}
StateData &other_data() {
return data_[!data_pos_];
}
// these aren't public to avoid multiple initializations
// constructors cannot be templated, so the initial state
// parameters can't be given here
Auto() : data_pos_(0) {}
Auto(Logger<self_t> &&log)
: data_pos_(0), logger(log) {}
// internal for switching to a state
template<template<class> class NewState, class ...Args>
void enter(Args&& ...args) {
other_data().template enter<NewState, Args...>(
std::forward<Args>(args)...);
cur_data().leave();
data_pos_ = !data_pos_;
}
template<template<class> class NewState,
template<class> class OldState,
class ...Args>
void free_transit(Args&& ...args) {
static_assert(
AllowedTransitions::template contains<TTPair<OldState, NewState>>(),
"forbidden transition");
/* traits on variadic templates would be mandatory,
which is too annoying to be worth it */
logger.template log<NewState, OldState>();
enter<NewState, Args...>(std::forward<Args>(args)...);
}
public:
~Auto() {
cur_data().leave();
}
// constructors cannot be templated, so init() will do
template<template<class> class InitialState, class ...Args>
static Auto init(Args&& ...args) {
Auto res;
res.cur_data().template enter<InitialState, Args...>(
std::forward<Args>(args)...);
return res;
}
// init with a logger.
template<template<class> class InitialState, class ...Args>
static Auto init_logger(Logger<self_t> &&logger, Args&& ...args) {
auto res = Auto{std::move(logger)};
res.cur_data().template enter<InitialState, Args...>(
std::forward<Args>(args)...);
return res;
}
auto &get_state() {
return *cur_data().template get_data<intf_t>();
}
template<template<class> class State>
static std::string state_name() {
return typeid(typename Proxy<State>::state_t).name();
}
};