使用信号处理程序时,我的(boost.msm)状态机似乎“回滚”
触发事件。但是,当我使用直接调用触发事件时,状态
机器行为正常。

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

我正在学习boost元状态机库,以查看是否
替换现有的“本地增长”状态机库将很有用
目前由我的开发团队使用。

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

我创建了一个简单但人为的示例以对其进行测试,
当我看到触发第一个事件后,状态就变得困惑
正确(但暂时)改变状态(在信号处理程序中)
但显然返回主站后会“回滚”。

当我绕过信号处理程序时(通过直接调用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


我通过对输出文件进行后期处理来添加行号,以便于参考。

输出的第01行显示状态机已正确从
初始伪状态为state_a。

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

但是,第17行显示了从以下位置进行测试时状态机返回到state_a
主(!)

状态机在state_a的其余过渡中保持不变
信号处理程序测试(第18-41行),导致出现一些“无过渡”错误
消息。

对于直接呼叫练习(输出线42-65),状态机转换
在所有状态下正确显示,并且“当前状态”没有区别
从触发功能内部以及在主状态时(触发之后)
函数调用)。

环境:
操作系统:“ Ubuntu 16.04 LTS”

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

增强版:boost_1_60_0

最佳答案

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


如果按以下方式复制此指针,则代码将按预期工作。

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

10-07 19:30
查看更多