Как получить boost.msm для правильного изменения состояния при использовании обработчика сигнала для запуска событий?
Мой (boost.msm
) конечный автомат выглядит как "откат" при использовании обработчиков сигналов для запуска событий. Однако, когда я использую прямые вызовы для запуска событий, конечный автомат ведет себя правильно.
Я посмотрел в документации по бусту и искал в Интернете, но кажется, что во всех примерах используются прямые вызовы для запуска событий. Я также искал SO, но не смог найти ничего, касающегося этой темы.
Я нахожусь в процессе изучения библиотеки автомата мета-состояний наддува, чтобы выяснить, было бы полезно заменить существующую библиотеку автоматов состояния, которая в настоящее время используется моей командой разработчиков.
Чтобы это работало, мне нужно иметь возможность запускать события конечного автомата из обработчиков сигналов (обработки сигналов из boost.signals2
).
Я создал простой, но надуманный пример для его тестирования и был сбит с толку, когда увидел, что после запуска первого события конечный автомат правильно (но временно) изменил состояния (находясь в обработчике сигналов), но, по-видимому, "покатился" назад после возвращения на главную.
Когда я обошел обработчики сигналов (используя прямые вызовы process_event), все работало правильно.
Предположительно изобретенный тестовый конечный автомат предназначен для этого:
[state_a]--event_a-->[state_b]--event_b-->[state_c]--event_c-->{back-to-state_a}
Я хотел бы знать, как я могу заставить этот дизайн (или что-то подобное) работать, используя обработчики сигналов для правильного запуска событий конечного автомата. Использование прямых вызовов для меня не вариант, так как я получаю только сигналы для работы.
Я включил тестовый код ниже. Обратите внимание, что первая половина основной функции выполняет запуск обработчика сигнала, а вторая половина основной функции - прямой вызов вызова (скомпилировано с использованием 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 вывода показывает, что конечный автомат правильно перешел из исходного псевдосостояния в состояние_a.
Строка 14 вывода показывает, что конечный автомат правильно перешел из state_a в state_b внутри функции on_sa_event.
Тем не менее, строка 17 показывает конечный автомат, возвращенный в state_a при тестировании из main (!)
Конечный автомат остается в state_a для остальных переходов тестов обработчика сигналов (строки 18-41), что приводит к нескольким сообщениям об ошибках "Нет перехода".
Для упражнения с прямым вызовом (выходные строки 42-65) конечный автомат корректно проходит через все состояния, и нет разницы в его "текущем состоянии" изнутри функции запуска и когда в основном (после вызова функции запуска).
Окружающая среда: ОС: "Ubuntu 16.04 LTS"
Версия g++: (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413
буст версия: boost_1_60_0
1 ответ
Проблема вызвана копированием * этого. Смотрите следующий код. Boost:: Bind копии * это. Каждый скопированный * это в начальном состоянии (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)));