-
Notifications
You must be signed in to change notification settings - Fork 73
/
Copy path215.md
290 lines (236 loc) · 8.01 KB
/
215.md
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
<details open><summary>Info</summary><p>
* **Did you know C++2X Pattern Matching can be used for run-time dispatching?**
* http://wg21.link/p1371
</p></details><details open><summary>Example</summary><p>
```cpp
template<auto...> struct ids{};
template<auto N, auto... Ns>
auto dispatch(auto value, ids<N, Ns...>) -> decltype(value) {
return inspect (value) {
N => value;
_ => [] {
if constexpr (sizeof...(Ns) > 0) {
return dispatch(value, ids<Ns...>{});
} else {
return {};
}
}()
};
}
int main() {
std::cout << dispatch(0, ids<1, 2, 3>{}); // prints 0
std::cout << dispatch(4, ids<1, 2, 3>{}); // prints 0
std::cout << dispatch(1, ids<1, 2, 3>{}); // prints 1
std::cout << dispatch(2, ids<1, 2, 3>{}); // prints 2
std::cout << dispatch(3, ids<1, 2, 3>{}); // prints 3
}
```
> https://godbolt.org/z/6349xe
</p></details><details open><summary>Puzzle</summary><p>
* **Can you implement different run-time dispatching policies and apply them to process events?**
* Policies ideas
* if/else
* switch
* inspect
* fold expressions
* jump table
* goto table
* ...
* Double points for the most innovative policy
```cpp
template <class TPolicy, class TMsg, class... Ts>
concept dispatch_policy = requires(TPolicy policy, const TMsg& msg,
std::size_t& state) {
policy.template operator()<Ts...>(msg, state);
};
constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state) {
/*TODO*/
};
template <class... Transitions>
struct sm {
template <class TMsg>
constexpr auto process(const TMsg& msg,
dispatch_policy<TMsg, Transitions...> auto policy) {
/*TODO*/
return state_;
}
constexpr explicit(false) sm(const Transitions&...) { }
private:
std::size_t state_{};
};
template <class TSrc, class TMsg, class TDst>
struct transition {
using src = TSrc;
using msg = TMsg;
using dst = TDst;
};
struct msg0 {};
struct msg1 {};
struct msg2 {};
struct msg3 {};
struct msg4 {};
int main() {
using namespace boost::ut;
"state machine"_test =
[](const auto& dispatch_policy) {
sm sm{transition<class State1, msg1, class State2>{},
transition<class State2, msg2, class State1>{}};
should("state in the same state on unexpected message") = [&] {
expect(0_i == sm.process(msg0{}, dispatch_policy));
expect(0_i == sm.process(msg2{}, dispatch_policy));
};
should("transition to destination state on expected message") = [&] {
expect(1_i == sm.process(msg1{}, dispatch_policy));
};
should("stay in the same state on unexpected message") = [&] {
expect(1_i == sm.process(msg0{}, dispatch_policy));
expect(1_i == sm.process(msg1{}, dispatch_policy));
};
should("transition to source state on expected message") = [&] {
expect(0_i == sm.process(msg2{}, dispatch_policy));
};
}
// policies
| std::tuple{example_policy};
}
```
> https://godbolt.org/z/obr4r3
</p></details><details><summary>Solutions</summary><p>
```cpp
constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state) {
using states_t = boost::mp11::mp_unique<
boost::mp11::mp_list<typename Ts::src..., typename Ts::dst...>>;
(
[&state] {
if constexpr (std::is_same_v<std::decay_t<decltype(msg)>,
typename Ts::msg>) {
if (state == boost::mp11::mp_find<states_t, typename Ts::src>{}) {
state = boost::mp11::mp_find<states_t, typename Ts::dst>{};
}
}
}(),
...);
};
template <class... Transitions>
struct sm {
template <class TMsg>
constexpr auto process(const TMsg& msg,
dispatch_policy<TMsg, Transitions...> auto policy) {
policy.template operator()<Transitions...>(msg, state_);
return state_;
}
constexpr explicit(false) sm(const Transitions&...) {}
private:
int state_{};
};
```
> https://godbolt.org/z/v4Ked7
```cpp
constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state){
using states_t = boost::mp11::mp_unique<boost::mp11::mp_list<typename Ts::src..., typename Ts::dst...>>;
([&state] {
if constexpr (std::is_same_v<std::decay_t<decltype(msg)>, typename Ts::msg>) {
if (state == boost::mp11::mp_find<states_t, typename Ts::src>{}) {
state = boost::mp11::mp_find<states_t, typename Ts::dst>{};
}
}
}(), ...);
};
template <class... Transitions>
struct sm {
template <class TMsg>
constexpr auto process(const TMsg& msg,
dispatch_policy<TMsg, Transitions...> auto policy) {
policy.template operator()<Transitions...>(msg, state_);
return state_;
}
constexpr explicit(false) sm(const Transitions&...) {}
private:
std::size_t state_{};
};
```
> https://godbolt.org/z/WG48Pv
```cpp
constexpr auto only_thursdays_policy = []<class... TTransitions>(const auto& msg, auto& state){
using msg_t = std::decay_t<decltype(msg)>;
([&] <typename TTransition> () {
if constexpr (std::is_same_v<msg_t, typename TTransition::msg>) {
if (boost::mp11::mp_find<boost::mp11::mp_list<TTransitions...>, TTransition>{} == state) {
using namespace std::chrono;
const auto d = floor<days>(system_clock::now());
if (const year_month_weekday ymd{d}; ymd.weekday() == Thursday) {
state = boost::mp11::mp_find<boost::mp11::mp_list<typename TTransitions::src...>,
typename TTransition::dst>{};
return true;
}
}
}
return false;
}.template operator()<TTransitions>() or ...);
};
template <class... Transitions>
struct sm {
template <class TMsg>
constexpr auto process(const TMsg& msg,
dispatch_policy<TMsg, Transitions...> auto policy) {
policy.template operator()<Transitions...>(msg, state_);
return state_;
}
constexpr explicit(false) sm(const Transitions&...) {}
private:
std::size_t state_{};
};
```
> https://godbolt.org/z/h9PWfa
```cpp
constexpr auto example_policy = []<class... Ts>(const auto& msg, auto& state){
using Msgs = mp_list<typename Ts::msg...>;
using MsgI = mp_find<Msgs, std::remove_cvref_t<decltype(msg)>>;
if constexpr (mp_less<MsgI, mp_size<Msgs>>::value) {
mp_with_index<sizeof...(Ts)>(state, [&]<class SrcI>(const SrcI&){
if constexpr (mp_same<SrcI, MsgI>::value) {
using Srcs = mp_list<typename Ts::src...>;
using Dsts = mp_list<typename Ts::dst...>;
using DstI = mp_at<Dsts, MsgI>;
state = mp_find<Srcs, DstI>::value;
}
});
}
};
template <class... Transitions>
struct sm {
template <class TMsg>
constexpr auto process(const TMsg& msg,
dispatch_policy<TMsg, Transitions...> auto policy) {
policy.template operator()<Transitions...>(msg, state_);
return state_;
}
constexpr explicit(false) sm(const Transitions&...) {}
private:
std::size_t state_{};
};
```
> https://godbolt.org/z/exEYEE
```cpp
constexpr auto example_policy = []<class... Ts, class T>(T const & msg, auto & state){
return mp_with_index<sizeof...(Ts)>( state
, [&]( auto I ) {
using TMsg = mp_at_c<mp_list<typename Ts::msg...>,I>;
using TDst = mp_at_c<mp_list<typename Ts::dst...>,I>;
return std::is_same_v<T,TMsg>? mp_find<mp_list<typename Ts::src...>, TDst>{}: state;
});
};
template <class... Transitions>
struct sm {
template <class TMsg>
constexpr auto process(const TMsg& msg,
dispatch_policy<TMsg, Transitions...> auto policy) {
state_ = policy.template operator()<Transitions...> (msg,state_);
return state_;
}
constexpr explicit(false) sm(const Transitions&...) {}
private:
std::size_t state_{};
};
```
> https://godbolt.org/z/v4oE8o