当使用信号处理程序触发事件时,如何使boost.msm正确更改状态

How do I get boost.msm to properly change state when using a signal handler to trigger events?

本文关键字:msm boost 何使 状态 信号处理 程序 事件      更新时间:2023-10-16

My(boost.msm)状态机在使用信号处理程序时显示为"回滚"以触发事件。但是,当我使用直接调用来触发事件时机器运行正常。

我查看了boost文档并搜索了网络,但似乎所有的示例都使用了对事件触发的直接调用。我还搜索了SO,但找不到任何关于这个话题的内容。

我正在学习boost元状态机库,看看是否替换现有的"国产"状态机库将是有用的目前由我的开发团队使用。

为了使其工作,我需要能够触发状态机事件来自信号处理器(处理来自boost.signals2的信号)。

我创建了一个简单但做作的示例来进行测试,结果当我看到第一个事件被触发后机器正确(但暂时)改变了状态(在信号处理程序中)但很明显,在回到主赛道后"倒退"了。

当我绕过信号处理程序时(通过使用对process_event的直接调用)一切正常。

公认是人为设计的测试状态机就是这样设计的:

[state_a]--event_a-->[state_b]--event_b-->[state_c]--event_c-->{back-to-state_a}

我想知道如何使这个设计(或类似的设计)发挥作用使用信号处理程序正确触发状态机事件。使用direct打电话对我来说不是一个选择,因为我只接收要处理的信号。

我在下面包含了测试代码。注意主体的前半部分函数练习信号处理程序的触发和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行显示状态机从从初始伪状态到statea。

输出的第14行显示状态机从当在on_sa_event函数内部时,从statea到stateb。

然而,第17行显示了从主(!)

对于信号处理程序测试(第18-41行),导致一些"无转换"错误消息。

对于直接调用练习(输出线42-65),状态机转换正确地通过所有状态,并且其"当前状态"没有差异在触发功能内,以及在主要情况下(触发后函数调用)。

环境:操作系统:"Ubuntu 16.04 LTS"

g++版本:(Ubuntu 5.3.1-14ubuntu2)5.3.1 20160413

助推版本:boost_1_6_0

问题是由复制*this引起的。请参阅以下代码。boost::绑定副本*this。每个复制的*this都处于初始状态(state_a)。这就是您经历回滚的原因。

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));

如果按如下方式复制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));

您也可以将*this的引用绑定如下:

s.get_sa().get_sig().connect(boost::bind(&Controller::on_sa_event, boost::ref(*this)));
s.get_sb().get_sig().connect(boost::bind(&Controller::on_sb_event, boost::ref(*this)));
s.get_sc().get_sig().connect(boost::bind(&Controller::on_sc_event, boost::ref(*this)));