一、前言
boost库提供了两个状态机的库Meta State Machine和Statechart,我写的例程基于boost 1.60.0版本的Statechart库。在boost_1_60_0/libs/statechart/example/源码目录下提供了7个例程,在官方网站的文档中The Boost Statechart Library - Tutorial - 1.60.0提供了一些详细的介绍。但是这7个例程都不能很好地满足我的需求,于是我自己写了一个比较切合我需求的例程,希望对大家也能有所帮助。
二、代码说明
- 示例程序包含3个源文件main.cpp、key_handler.hpp和key_handler.cpp,示例程序包含1个状态机(KeyHandler类型)和3个状态(Start、Work和Stop),main函数循环向状态机发送包含布尔值的事件,状态机的初始状态为Start,状态机收到第一个事件后会从Start状态跳转到Work状态,在Wrok状态中若收到布尔值为1的事件则忽略该事件,若收到布尔值为0的事件则将状态机中的stop_flag设置为true,然后跳转到Stop状态,main函数读取到状态机的stop_flag为true时则退出循环和程序;
- main.cpp中创建了一个KeyHandler类型的状态机,并且循环从键盘输入布尔值、创建一个包含布尔值的事件、向状态机发送事件,然后读取状态机的flag,若flag为1则退出循环;
- key_handler.hpp中定义了EvAddKey事件类和KeyHandler状态机类,以及Start、Work和Stop状态类,EvAddKey事件类中有一个用于存储输入布尔值的变量,KeyHandler状态机类中有一个用于存储是否需要退出程序的布尔变量,Start、Work和Stop状态类中各有一个用于响应EvAddKey事件的自定义react函数;
- key_handler.cpp中实现了三个状态类的构造函数、析构函数和react函数,在react函数中返回
transit<Work>()
可以实现状态跳转,在react函数中使用context<KeyHandler>().set_flag(true)
可以调用状态机类或者状态类中的函数,在react函数中返回discard_event()
可以忽略该事件。
三、代码
main.cpp
#include <iostream>
#include "key_handler.hpp"
bool get_key()
{
char key;
std::cout << "Please input 0 or 1: ";
std::cin >> key;
if (key == '0')
{
return false;
}
return true;
}
int main(void)
{
KeyHandler handler;
handler.initiate();
for(int i = 0; i < 5; i++)
{
handler.process_event(EvAddKey(get_key()));
if (handler.get_flag())
{
break;
}
}
std::cout << "Exit main." << std::endl;
return 0;
}
key_handler.hpp
#ifndef KEY_HANDLER_HPP
#define KEY_HANDLER_HPP
#include <boost/statechart/event.hpp>
#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/custom_reaction.hpp>
namespace sc = boost::statechart;
struct EvAddKey : sc::event<EvAddKey>
{
public:
EvAddKey(bool val) : val_(val) {};
bool get_val() const { return val_; };
private:
bool val_;
};
struct Start;
struct KeyHandler : sc::state_machine<KeyHandler, Start>
{
public:
KeyHandler() : stop_flag_(false) {};
void set_flag(bool flag) { stop_flag_ = flag; };
bool get_flag() { return stop_flag_; };
private:
bool stop_flag_;
};
struct Start : sc::simple_state<Start, KeyHandler>
{
Start();
~Start();
typedef sc::custom_reaction<EvAddKey> reactions;
sc::result react(const EvAddKey &);
};
struct Work : sc::simple_state<Work, KeyHandler>
{
Work();
~Work();
typedef sc::custom_reaction<EvAddKey> reactions;
sc::result react(const EvAddKey &);
};
struct Stop : sc::simple_state<Stop, KeyHandler>
{
Stop();
~Stop();
typedef sc::custom_reaction<EvAddKey> reactions;
sc::result react(const EvAddKey &);
};
#endif
key_handler.cpp
#include <iostream>
#include "key_handler.hpp"
Start::Start()
{
std::cout << "Entering Start" << std::endl;
}
Start::~Start()
{
std::cout << "Exiting Start" << std::endl;
}
sc::result Start::react(const EvAddKey & event)
{
std::cout << "Reacting Start" << std::endl;
std::cout << "Ignore " << event.get_val() << std::endl;
return transit<Work>();
}
Work::Work()
{
std::cout << "Entering Work" << std::endl;
}
Work::~Work()
{
std::cout << "Exiting Work" << std::endl;
}
sc::result Work::react(const EvAddKey & event)
{
std::cout << "Reacting Work" << std::endl;
if (event.get_val() == false)
{
context<KeyHandler>().set_flag(true);
return transit<Stop>();
}
return discard_event();
}
Stop::Stop()
{
std::cout << "Entering Stop" << std::endl;
}
Stop::~Stop()
{
std::cout << "Exiting Stop" << std::endl;
}
sc::result Stop::react(const EvAddKey &)
{
std::cout << "Reacting Stop" << std::endl;
return discard_event();
}
四、编译、运行
charming@ubuntu:~/Documents/fsm/handle_key$ ls
key_handler.cpp key_handler.hpp main.cpp
charming@ubuntu:~/Documents/fsm/handle_key$ g++ *.* -o test
charming@ubuntu:~/Documents/fsm/handle_key$ ./test
Entering Start
Please input 0 or 1: 1
Reacting Start
Ignore 1
Exiting Start
Entering Work
Please input 0 or 1: 1
Reacting Work
Please input 0 or 1: 1
Reacting Work
Please input 0 or 1: 0
Reacting Work
Exiting Work
Entering Stop
Exit main.
Exiting Stop
charming@ubuntu:~/Documents/fsm/handle_key$