MSM Meta State Machine,是boost库的元状态机,使用了大量的模板元编程,让使用者轻松创建出好用,可阅读,效率高的状态机。它的特点是使用状态转移表 transition_table 来定义状态机转移的过程,非常直观,易于阅读,修改,排错和修改。状态转移表的要素是源状态 Sourcce State,目标状态 Target State,触发事件Event,守卫条件Guard和执行动作Action。
本文代码示例在boost_msm_guide
从一个简单的状态机开始
使用boost msm定义一个简单的状态机分为6步:
- 定义事件Event
- 定义状态机front-end
boost::msm::front::state_machine_def
- 定义状态
- 设置初始状态
- 定义状态转移表
- 生成状态机back-end
状态机使用时
- 定义状态机实例fsm
- 开始状态机fsm.start()
- 处理事件fsm.process_event()
首先从一个只有状态转移的简单状态机开始

代码如下:
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
|
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <iostream>
namespace {
// ---Events
struct Event1 {};
// ---State Machine front-end
struct Sm : boost::msm::front::state_machine_def<Sm>
{
// States
struct State1 : boost::msm::front::state<> {
// Entry Action
template<class Event, class Fsm>
void on_entry(Event const&, Fsm&) {
std::cout << "Entering State1\n";
}
// Exit Action
template<class Event, class Fsm>
void on_exit(Event const&, Fsm&) {
std::cout << "Leaving State1\n";
}
};
struct End : boost::msm::front::terminate_state<> {};
// Set initial state
using initial_state = State1;
// Transition Table
struct transition_table : boost::mpl::vector<
// Start | Event | Next | Action | Guard
_row< State1, Event1, End >
> {};
};
// Pick a back-end
using Fsm = boost::msm:🔙:state_machine<Sm>;
void test() {
Fsm fsm;
fsm.start();
std::cout << "> Send Event1\n";
fsm.process_event(Event1{});
}
}
int main() {
test();
return 0;
}
|
编译运行此状态,打印是
1
2
3
|
Entering State1
> Send Event1
Leaving State1
|
State
状态机的第一要素当然是状态,一个状态机的一生就是在各个状态间来来去去。状态间的迁移由Event事件触发,迁移中间可以执行Action,是否能迁移取决于Guard。每个状态带有on_entry和on_exit两个方法,顾名思义,在进入和离开时触发。
为了后续更好的演示,我们定义一个base class执行一些基础的打印。
首先,获取type的名称。我们知道标准库中的typeid(var).name()
方法可以获取到变量的类型名称,但是是内部的符号,我们可以通过boost::core::demangle方法来获取更易读的名称,同时把命名空间的前缀去掉。
1
2
3
4
5
6
7
8
9
|
template<typename T>
std::string get_typename(const T &x) {
std::string name = boost::core::demangle(typeid(x).name());
auto pos = name.rfind("::");
if (pos != std::string::npos) {
name = name.substr(pos + 2);
}
return name;
}
|
BaseState负责在on_entry和on_exit时打印日志,这两个方法由状态机在进入和离开状态时自动调用。为了继承的子类可以定制一些动作,定义了虚函数prepare和clean,参数为状态机内的ctx。实际上,这里和on_entry一样传入template Event和Fsm参数更好,但是由于c++语言的限制,模板函数不能为虚函数,所以退而求其次,传入一个新的Context变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
struct BaseState : boost::msm::front::state<> {
virtual ~BaseState() {}
template<class Event, class Fsm>
void on_entry(Event const &event, Fsm &fsm) {
std::cout << "[state]...Entering " << get_typename(*this)
<< " by " << get_typename(event) << "\n";
prepare(fsm.ctx);
}
template<class Event, class Fsm>
void on_exit(Event const &event, Fsm &fsm) {
std::cout << "[state]...Leaving " << get_typename(*this)
<< " by " << get_typename(event) << "\n";
clean(fsm.ctx);
}
virtual void prepare(Context &) {}
virtual void clean(Context &) {}
};
|
现在,我们的状态机得以精简
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
struct Sm : boost::msm::front::state_machine_def<Sm>
{
// ---States
struct State1 : BaseState {};
struct End : boost::msm::front::terminate_state<> {};
// ---Set initial state
using initial_state = State1;
// ---Transition Table
struct transition_table : boost::mpl::vector<
// Start | Event | Next | Action | Guard
_row< State1, Event1, End >
> {};
Context ctx;
};
// ---Pick a back-end
using Fsm = boost::msm:🔙:state_machine<Sm>;
|
打印是
1
2
3
|
[state]...Entering State1 by InitEvent
> Send Event1
[state]...Leaving State1 by Event1
|
值得注意的是InitEvent,它是从哪来的?我们的transition_table并没有定义这个事件!实际上,这是Fsm进入initial_state的默认触发事件。
Action
Action发生在离开状态后,进入下一状态前。可以使用一个Functor class来表示。和State一样,这里定义一个BaseAction的类。
1
2
3
4
5
6
7
8
9
10
11
|
struct BaseAction {
virtual ~BaseAction() {}
template<class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const &event, Fsm &fsm, SourceState const &from, TargetState const &to) {
std::cout >> "[action] Do " >> get_typename(*this) >> " from " >>
>> get_typename(from) >> " to " >> get_typename(to) >>
>> " by " >> get_typename(event) >> "\n";
execute(fsm.ctx);
}
virtual void execute(Context &) {}
};
|
Guard
Guard发生在离开状态前,返回一个bool值,如果为true,则可以转移,否则保持在原状态。
1
2
3
4
5
6
7
8
9
10
11
12
|
struct BaseGuard {
virtual ~BaseGuard() {}
template<class Event, class Fsm, class SourceState, class TargetState>
bool operator()(Event const &event, Fsm &fsm, SourceState const &from, TargetState const &to) {
bool ok = execute(fsm.ctx);
std::cout << "[guard] " << get_typename(*this) << " -> " << std::boolalpha << ok
<< " from " << get_typename(from) << " to " << get_typename(to)
<< " by " << get_typename(event) << "\n";
return ok;
}
virtual bool execute(Context &) {return true;}
};
|
transition_table
状态转移表的完整形态是5个元素:
fromState, Event, ToState, Action, Guard
一般使用functor_row里的boost::msm::front::Row来定义转移表的每一行。其中空的要素使用boost::msm::front::none来表示。
可以使用using来简化这个两个类型
1
2
3
4
|
template<typename... T>
using Row = boost::msm::front::Row<T...>;
using None = boost::msm::front::none;
|
至此我们可以写一个完整的状态了。

代码为
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
|
// ---State Machine front-end
struct Sm : boost::msm::front::state_machine_def<Sm>
{
// ---States
struct State1 : BaseState {};
struct State2 : BaseState {};
struct Init : BaseState {};
struct End : boost::msm::front::terminate_state<> {};
// ---Events
struct Event1 {};
struct Event2 {};
// ---Actions
struct Action1 : BaseAction {};
struct Action2 : BaseAction {};
// ---Guard
struct Guard1 : BaseGuard {};
struct GTrue : BaseGuard {};
struct GFalse : BaseGuard {
bool execute(Context &) override {
return false;
}
};
// ---Set initial state
using initial_state = Init;
template<typename... T>
using Row = boost::msm::front::Row<T...>;
using None = boost::msm::front::none;
// ---Transition Table
struct transition_table : boost::mpl::vector<
// Start * Event * Next * Action * Guard
Row< Init, None, State1, None, None >,
Row< State1, Event1, State2, Action1, Guard1 >,
Row< State2, Event2, End, Action2, None >
> {};
Context ctx;
};
// ---Pick a back-end
using Fsm = boost::msm:🔙:state_machine<Sm>;
void test() {
Fsm fsm;
fsm.start();
std::cout << "> Send Event1\n";
fsm.process_event(Event1{});
std::cout << "> Send Event2\n";
fsm.process_event(Event2{});
}
|
输出为
1
2
3
4
5
6
7
8
9
10
11
|
[state]...Entering Init by InitEvent
[state]...Leaving Init by none
[state]...Entering State1 by none
> Send Event1
[guard] Guard1 -> true from State1 to State2 by Event1
[state]...Leaving State1 by Event1
[action] Do Action1 from State1 to State2 by Event1
[state]...Entering State2 by Event1
> Send Event2
[state]...Leaving State2 by Event2
[action] Do Action2 from State2 to End by Event2
|
if-else
在transition_table中,在下方的row优先于上方的row先执行。
这一节通过实现一个if-else的逻辑来说明在transition_table里row的优先级。

两个Action
1
2
3
4
5
6
7
8
9
10
11
|
// ---Actions
struct SetVal1 : BaseAction {
void execute(Context &ctx) override {
ctx.val = 1;
}
};
struct SetVal2 : BaseAction {
void execute(Context &ctx) override {
ctx.val = 2;
}
};
|
Guard
1
2
3
4
5
|
struct IfGuard : BaseGuard {
bool execute(Context &ctx) override {
return ctx.val == 1;
}
};
|
transition_table
1
2
3
4
5
6
7
8
9
10
|
// ---Transition Table
struct transition_table : boost::mpl::vector<
// Start * Event * Next * Action * Guard
Row< Init , Event1 , State1 , SetVal1 , None >,
Row< Init , Event2 , State1 , SetVal2 , None >,
Row< State1 , None , ElseState , None , None >,
Row< State1 , None , IfState , None , IfGuard>,
Row< IfState, None , Init , None , None >,
Row< ElseState, None , Init , None , None >
> {};
|
输出为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
[state]...Entering Init by InitEvent
> Send Event1
[state]...Leaving Init by Event1
[action] Do SetVal1 from Init to State1 by Event1
[state]...Entering State1 by Event1
[guard] IfGuard -> true from State1 to IfState by none
[state]...Leaving State1 by none
[state]...Entering IfState by none
[state]...Leaving IfState by none
[state]...Entering Init by none
> Send Event2
[state]...Leaving Init by Event2
[action] Do SetVal2 from Init to State1 by Event2
[state]...Entering State1 by Event2
[guard] IfGuard -> false from State1 to IfState by none
[state]...Leaving State1 by none
[state]...Entering ElseState by none
[state]...Leaving ElseState by none
[state]...Entering Init by none
|
状态自转移
转移到自己本身有两种方式,可以称为外部自转移和内部自转移。如
1
2
3
4
5
6
7
|
// ---Transition Table
struct transition_table : boost::mpl::vector<
// Start * Event * Next * Action * Guard
Row< Init, None, State1, None, None >,
Row< State1, Event1, State1, Action1, Guard1 >,
Row< State1, Event2, None , Action2, GTrue >
> {};
|
- start和next都是State1,此时会执行State1的on_exit和on_entry。
- next时None,不会执行on_exit和on_entry。
1
2
3
4
5
6
7
8
9
10
11
|
[state]...Entering Init by InitEvent
[state]...Leaving Init by none
[state]...Entering State1 by none
> Send Event1
[guard] Guard1 -> true from State1 to State1 by Event1
[state]...Leaving State1 by Event1
[action] Do Action1 from State1 to State1 by Event1
[state]...Entering State1 by Event1
> Send Event2
[guard] GTrue -> true from State1 to State1 by Event2
[action] Do Action2 from State1 to State1 by Event2
|
exception
状态机的on_entry, on_exit, Action, Guard都可能抛出异常,不自己处理的话,程序会异常退出。可以通过自己定义exception_caught方法来自定义异常处理。
另外我们可以重载no_transition的方法来处理没有对应状态转移规则的情况。
例如:
1
2
3
4
5
6
7
8
9
10
|
template<class Event, class Fsm>
void exception_caught (Event const& evt, Fsm& fsm,std::exception& e) {
std::cout << "catch exception " << e.what() << " while " << get_typename(evt)
<< '\n';
}
template<class Event, class Fsm>
void no_transition(Event const& evt, Fsm &fsm, int x) {
std::cout << "No transition item for " << x
<< " while " << get_typename(evt) << '\n';
}
|
针对产生异常的不同时机,其表现有所不同。我们通过一个out_of_range的异常来看一下实际结果。
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
|
void sth_wrong() {
std::vector<int> a(1);
int i = a.at(2);
}
struct ExceptionOnEntry : BaseState {
void prepare(Context &) override {
sth_wrong();
}
};
struct ExceptionOnExit : BaseState {
void clean(Context &) override {
sth_wrong();
}
};
struct ExceptionAction : BaseAction {
void execute(Context &) override {
sth_wrong();
}
};
struct ExceptionGuard : BaseGuard {
bool execute(Context &) override {
sth_wrong();
return true;
}
};
// ---Transition Table
struct transition_table : boost::mpl::vector<
// Start * Event * Next * Action * Guard
Row< Init, None, State1, None, None >,
Row< State1, Event1, ExceptionOnEntry, Action1, GTrue >,
Row< State1, Event2, State2, ExceptionAction, GTrue >,
Row< State1, Event3, State2, Action1, ExceptionGuard >,
Row< State1, Event4, ExceptionOnExit, Action1, GTrue >,
Row< ExceptionOnExit, Event5, State1, Action1, GTrue >
> {};
|
运行结果
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
|
[state]...Entering Init by InitEvent
[state]...Leaving Init by none
[state]...Entering State1 by none
> Send Event1
[guard] GTrue -> true from State1 to ExceptionOnEntry by Event1
[state]...Leaving State1 by Event1
[action] Do Action1 from State1 to ExceptionOnEntry by Event1
[state]...Entering ExceptionOnEntry by Event1
catch exception vector::_M_range_check: __n (which is 2) >= this->size() (which is 1) while Event1
> Send Event2
[guard] GTrue -> true from State1 to State2 by Event2
[state]...Leaving State1 by Event2
[action] Do ExceptionAction from State1 to State2 by Event2
catch exception vector::_M_range_check: __n (which is 2) >= this->size() (which is 1) while Event2
> Send Event3
catch exception vector::_M_range_check: __n (which is 2) >= this->size() (which is 1) while Event3
> Send Event4
[guard] GTrue -> true from State1 to ExceptionOnExit by Event4
[state]...Leaving State1 by Event4
[action] Do Action1 from State1 to ExceptionOnExit by Event4
[state]...Entering ExceptionOnExit by Event4
> Send Event5
[guard] GTrue -> true from ExceptionOnExit to State1 by Event5
[state]...Leaving ExceptionOnExit by Event5
catch exception vector::_M_range_check: __n (which is 2) >= this->size() (which is 1) while Event5
> Send Event1
No transition item for 2 while Event1
|
可以总结:
一次状态迁移的过程分为guard->on_exit->action->on_entry。其中某一步抛出异常时,状态都会回到sourceState。