シグナル ハンドラを使用してイベントをトリガーすると、 ( boost.msm
) ステート マシンが「ロールバック」しているように見えます。ただし、直接呼び出しを使用してイベントをトリガーすると、ステート マシンは正しく動作します。
boost のドキュメントを参照して Web を検索したところ、すべての例でイベント トリガーに直接呼び出しが使用されているようです。SOも検索しましたが、このトピックに対処するものは見つかりませんでした。
開発チームが現在使用している既存の「自作」ステート マシン ライブラリを置き換えるのに役立つかどうかを確認するために、ブースト メタ ステート マシン ライブラリを学習中です。
これを機能させるには、シグナル ハンドラー (からのシグナルの処理) からステート マシン イベントをトリガーできる必要がありますboost.signals2
。
テストを実行するために単純だが不自然な例を作成し、最初のイベントがトリガーされた後、ステート マシンが (シグナル ハンドラー内で) 状態を正しく (ただし一時的に) 変更したものの、明らかに「ロールした」ことを確認して困惑しました。メインに戻った後。
(process_event への直接呼び出しを使用して) シグナル ハンドラーをバイパスすると、すべてが正しく機能しました。
確かに考案されたテスト ステート マシンは、これを行うように設計されています。
[state_a]--event_a-->[state_b]--event_b-->[state_c]--event_c-->{back-to-state_a}
シグナル ハンドラーを使用してこのデザイン (または同様のもの) を機能させ、ステート マシン イベントを正しくトリガーする方法を知りたいです。私は操作する信号しか受信しないため、直接呼び出しを使用することはできません。
以下のテストコードを含めました。main 関数の前半はシグナル ハンドラのトリガーを実行し、main の後半は直接呼び出しのトリガーを実行することに注意してください ( を使用してコンパイルg++ main.cpp -omain' or 'clang++ main.cpp -omain
)。
#include <iostream>
#include <boost/signals2.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/back/tools.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
typedef boost::signals2::signal<void()> sig1_t;
//================================================================================
// ------- Sensors section
struct sensor_a {
sig1_t& get_sig() { return sig; }
void emit() { sig(); }
private:
sig1_t sig;
};
struct sensor_b {
sig1_t& get_sig() { return sig; }
void emit() { sig(); }
private:
sig1_t sig;
};
struct sensor_c {
sig1_t& get_sig() { return sig; }
void emit() { sig(); }
private:
sig1_t sig;
};
//========================================
// Sensors class
struct Sensors {
sensor_a& get_sa() {
return sa;
}
sensor_b& get_sb() {
return sb;
}
sensor_c& get_sc() {
return sc;
}
private:
sensor_a sa;
sensor_b sb;
sensor_c sc;
};
// ----- Events
struct event_a {
std::string name() const { return "event_a"; }
};
struct event_b {
std::string name() const { return "event_b"; }
};
struct event_c {
std::string name() const { return "event_c"; }
};
struct exit {
std::string name() const { return "exit"; }
};
//================================================================================
// ----- State machine section
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
class Controller; // forward declaration
//========================================
// testmachine class (the state machine)
struct testmachine : msmf::state_machine_def<testmachine>
{
testmachine(Controller& c) : controller(c) {}
template <class Fsm,class Event>
void no_transition(Event const& e, Fsm& ,int state) {
std::cout << "testmachine::no_transition -- No transition for event: '"
<< e.name() << "'" << " on state: " << state << std::endl;
}
//---------
struct state_a : msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "state_a::on_entry() " << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "state_a::on_exit()" << std::endl;
}
};
//---------
struct state_b : msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const& e, Fsm&) const {
std::cout << "state_b::on_entry() -- event: " << e.name() << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const& e, Fsm&) const {
std::cout << "state_b::on_exit() -- event: " << e.name() << std::endl;
}
};
//---------
struct state_c : msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const& e, Fsm&) const {
std::cout << "state_c::on_entry() -- event: " << e.name() << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const& e, Fsm&) const {
std::cout << "state_c::on_exit() -- event: " << e.name() << std::endl;
}
};
//---------
// Set initial state
typedef mpl::vector<state_a> initial_state;
//---------
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < state_a, event_a, state_b, msmf::none, msmf::none >,
msmf::Row < state_b, event_b, state_c, msmf::none, msmf::none >,
msmf::Row < state_c, event_c, state_a, msmf::none, msmf::none >
> {};
private:
Controller& controller;
};
// state-machine back-end
typedef msm::back::state_machine<testmachine> TestMachine;
//================================================================================
// --------- controller section
namespace msm = boost::msm;
namespace mpl = boost::mpl;
// debug print helper:
std::string demangle(const std::string& mangled) {
int status;
char* c_name = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status);
if(c_name){
std::string retval(c_name);
free((void*)c_name);
return retval;
}
return mangled;
}
// debug print helper (from boost msm documentation):
void pstate(TestMachine const& sm) {
typedef TestMachine::stt Stt;
typedef msm::back::generate_state_set<Stt>::type all_states;
static char const* state_names[mpl::size<all_states>::value];
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
(msm::back::fill_state_names<Stt>(state_names));
for (unsigned int i=0;i<TestMachine::nr_regions::value;++i){
std::cout << " -> " << demangle(state_names[sm.current_state()[i]])
<< std::endl;
}
}
//========================================
// Controller class
struct Controller {
Controller(Sensors& s) :
sensors(s),
tm(boost::ref(*this)) {
s.get_sa().get_sig().connect(boost::bind(&Controller::on_sa_event, *this));
s.get_sb().get_sig().connect(boost::bind(&Controller::on_sb_event, *this));
s.get_sc().get_sig().connect(boost::bind(&Controller::on_sc_event, *this));
tm.start();
}
void on_sa_event() {
std::cout << "Controller::on_sa_event function entered ++++++++" << std::endl;
current_state(__FUNCTION__);
trigger_event_a();
current_state(__FUNCTION__);
std::cout << "Controller::on_sa_event function exiting --------" << std::endl;
};
void on_sb_event() {
std::cout << "Controller::on_sb_event function entered ++++++++" << std::endl;
current_state(__FUNCTION__);
trigger_event_b();
current_state(__FUNCTION__);
std::cout << "Controller::on_sb_event function exiting --------" << std::endl;
};
void on_sc_event() {
std::cout << "Controller::on_sc_event function entered ++++++++" << std::endl;
current_state(__FUNCTION__);
trigger_event_c();
current_state(__FUNCTION__);
std::cout << "Controller::on_sc_event function exiting --------" << std::endl;
};
// debug print function
void current_state(const std::string& f) {
std::cout << "\nController::current_state ("
<< "called from function: " << f
<<")" << std::endl;
pstate(tm);
std::cout << std::endl;
}
void trigger_event_a() {
std::cout << "Controller::trigger_event_a" << std::endl;
tm.process_event(event_a());
current_state(__FUNCTION__);
}
void trigger_event_b() {
std::cout << "Controller::trigger_event_b" << std::endl;
tm.process_event(event_b());
current_state(__FUNCTION__);
}
void trigger_event_c() {
std::cout << "Controller::trigger_event_c" << std::endl;
tm.process_event(event_c());
current_state(__FUNCTION__);
}
private:
Sensors& sensors;
TestMachine tm;
};
//================================================================================
// --------- main
int main() {
Sensors sensors;
Controller controller(sensors);
std::cout << "Exercise state machine using signal handlers (fails):" << std::endl;
controller.current_state("***** main");
sensors.get_sa().emit();
controller.current_state("***** main");
sensors.get_sb().emit();
controller.current_state("***** main");
sensors.get_sc().emit();
controller.current_state("***** main");
std::cout << "\nExercise state machine using direct calls (works):" << std::endl;
controller.current_state("***** main");
controller.trigger_event_a();
controller.current_state("***** main");
controller.trigger_event_b();
controller.current_state("***** main");
controller.trigger_event_c();
controller.current_state("***** main");
}
出力は次のとおりです。
1 state_a::on_entry()
2 Exercise state machine using signal handlers (fails):
3 Controller::current_state (called from function: ***** main)
4 -> testmachine::state_a
5 Controller::on_sa_event function entered ++++++++
6 Controller::current_state (called from function: on_sa_event)
7 -> testmachine::state_a
8 Controller::trigger_event_a
9 state_a::on_exit()
10 state_b::on_entry() -- event: event_a
11 Controller::current_state (called from function: trigger_event_a)
12 -> testmachine::state_b
13 Controller::current_state (called from function: on_sa_event)
14 -> testmachine::state_b
15 Controller::on_sa_event function exiting --------
16 Controller::current_state (called from function: ***** main)
17 -> testmachine::state_a
18 Controller::on_sb_event function entered ++++++++
19 Controller::current_state (called from function: on_sb_event)
20 -> testmachine::state_a
21 Controller::trigger_event_b
22 testmachine::no_transition -- No transition for event: 'event_b' on state: 0
23 Controller::current_state (called from function: trigger_event_b)
24 -> testmachine::state_a
25 Controller::current_state (called from function: on_sb_event)
26 -> testmachine::state_a
27 Controller::on_sb_event function exiting --------
28 Controller::current_state (called from function: ***** main)
29 -> testmachine::state_a
30 Controller::on_sc_event function entered ++++++++
31 Controller::current_state (called from function: on_sc_event)
32 -> testmachine::state_a
33 Controller::trigger_event_c
34 testmachine::no_transition -- No transition for event: 'event_c' on state: 0
35 Controller::current_state (called from function: trigger_event_c)
36 -> testmachine::state_a
37 Controller::current_state (called from function: on_sc_event)
38 -> testmachine::state_a
39 Controller::on_sc_event function exiting --------
40 Controller::current_state (called from function: ***** main)
41 -> testmachine::state_a
42 Exercise state machine using direct calls (works):
43 Controller::current_state (called from function: ***** main)
44 -> testmachine::state_a
45 Controller::trigger_event_a
46 state_a::on_exit()
47 state_b::on_entry() -- event: event_a
48 Controller::current_state (called from function: trigger_event_a)
49 -> testmachine::state_b
50 Controller::current_state (called from function: ***** main)
51 -> testmachine::state_b
52 Controller::trigger_event_b
53 state_b::on_exit() -- event: event_b
54 state_c::on_entry() -- event: event_b
55 Controller::current_state (called from function: trigger_event_b)
56 -> testmachine::state_c
57 Controller::current_state (called from function: ***** main)
58 -> testmachine::state_c
59 Controller::trigger_event_c
60 state_c::on_exit() -- event: event_c
61 state_a::on_entry()
62 Controller::current_state (called from function: trigger_event_c)
63 -> testmachine::state_a
64 Controller::current_state (called from function: ***** main)
65 -> testmachine::state_a
参照しやすいように、出力ファイルを後処理して行番号を追加しました。
出力の 01 行目は、ステート マシンが最初の疑似状態から state_a に正しく遷移したことを示しています。
出力の 14 行目は、on_sa_event 関数内でステート マシンが state_a から state_b に正しく遷移したことを示しています。
ただし、17 行目は、メインからテストしたときにステート マシンが state_a に戻ったことを示しています (!)
ステート マシンは、シグナル ハンドラー テストの残りの遷移 (18 行目から 41 行目) の間、state_a のままであり、いくつかの「No Transition」エラー メッセージが表示されます。
直接呼び出しの演習 (出力行 42 ~ 65) の場合、ステート マシンはすべての状態を正しく遷移し、トリガー関数内とメインのとき (トリガー関数呼び出し後) の「現在の状態」に違いはありません。
環境:OS:「Ubuntu 16.04 LTS」
g++ バージョン: (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413
ブースト バージョン: boost_1_60_0