-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmsfsm.hpp
100 lines (82 loc) · 2.41 KB
/
msfsm.hpp
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
// Copyright (C) 2019, 2023 Michal Sojka <ms@2x.cz>
//
// SPDX-License-Identifier: MIT
#ifndef MSFSM_HPP
#define MSFSM_HPP
#include <iostream>
#include <stdexcept>
#include <boost/type_index.hpp> // Only needed if StateName is used
namespace msfsm {
class noncopyable
{
protected:
constexpr noncopyable() = default;
~noncopyable() = default;
noncopyable( const noncopyable& ) = delete;
noncopyable& operator=( const noncopyable& ) = delete;
};
template <class FSM>
class Fsm : noncopyable
{
protected:
// Base class for all states
class State : noncopyable {
public:
State(FSM *fsm) : fsm(*fsm) {}
virtual ~State() {}
private:
friend Fsm;
virtual void exit() {}
protected:
FSM &fsm;
// Convenience method
template<class S, class ... Types>
void transition(S &nextState, Types... args) { fsm.Fsm<FSM>::transition(nextState, args...); }
};
template<class S, class ... Types>
void transition(S &nextState, Types... args) {
if (m_state)
m_state->exit();
static_cast<FSM*>(this)->onTransition(nextState);
m_state = &nextState;
nextState.entry(args...);
static_cast<FSM*>(this)->afterTransition();
}
// Call this from derived class destructor if needed. We cannot
// make this in our destructor, because the states are already
// destoyed when it runs.
void destroy() {
if (m_state)
m_state->exit();
m_state = nullptr;
}
private:
State *m_state = nullptr;
// Prevent compile error if derived class does not have its own onTransition() method
void onTransition(State &nextState) {}
void afterTransition() {}
public:
State* state() const { return m_state; }
template <typename EV>
void handle(EV event) {
if (!m_state)
throw std::domain_error(__PRETTY_FUNCTION__);
static_cast<typename FSM::State*>(m_state)->event(event);
}
};
// Mix-in class for named states
template <class S>
class Named {
std::string m_name;
public:
// Unfortunately, type_id does not correspond to derived class
// in constructor, so we must retrieve (and cache) the name
// later.
const std::string& name() {
if (m_name.empty())
m_name = boost::typeindex::type_id_runtime(static_cast<S&>(*this)).pretty_name();
return m_name;
}
};
}
#endif // MSFSM_HPP