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

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

我的 (boost.msm) 状态机在使用信号处理程序时出现 'roll-back' 触发事件。但是,当我使用直接调用来触发事件时,状态 机器运行正常。

我查看了 boost 文档并在网上搜索,但似乎 所有示例都使用直接调用来触发事件。我也搜索过, 但找不到解决此主题的任何内容。

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

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

我创建了一个简单但人为设计的示例来对其进行测试 运行 并且 当我看到第一个事件被触发后,状态时感到困惑 机器正确地(但暂时地)改变了状态(在信号处理程序中) 但显然 'rolled back' 回到 main.

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

无可否认,测试状态机旨在执行此操作:

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

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

我在下面包含了测试代码。注意主要的前半部分 函数执行信号处理程序触发和 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

我通过 post 添加行号 - 处理输出文件以便于参考。

输出的第 01 行显示状态机从 初始伪状态为 state_a.

输出的第 14 行显示状态机从 在 on_sa_event 函数内部时 state_a 到 state_b。

然而,第 17 行显示状态机在测试时返回到 state_a 主要 (!)

状态机在 state_a 中保持为剩余的转换 信号处理程序测试(第 18-41 行),导致一些 'No Transition' 错误 消息。

对于直接调用练习(输出行 42-65),状态机转换 正确通过所有州并且没有区别 'current state' 从触发函数内和在 main 中时(触发后 函数调用)。

环境: OS: "Ubuntu 16.04 LTS"

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

提升版本:boost_1_60_0

问题是复制*this引起的。请参见以下代码。 boost::bind 复制 *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)));