Как получить boost.msm для правильного изменения состояния при использовании обработчика сигнала для запуска событий?

Мой (boost.msm) конечный автомат кажется «откат» при использовании обработчиков сигналов
для запуска событий. Тем не менее, когда я использую прямые вызовы для запуска событий состояние
машина ведет себя правильно.

Я посмотрел в документации по бусту и искал в Интернете, но кажется, что
все примеры используют прямые вызовы для запуска событий. Я также искал так,
но не смог найти ничего, касающегося этой темы.

Я нахожусь в процессе изучения библиотеки мета-состояний Boost, чтобы увидеть,
было бы полезно заменить существующую библиотеку конечных автоматов «доморощенного»
в настоящее время используется моей командой разработчиков.

Чтобы это работало, мне нужно иметь возможность запускать события конечного автомата
от обработчиков сигналов (обработка сигналов от 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 вывода показывает, что конечный автомат правильно перешел из
начальное псевдо-состояние для state_a.

Строка 14 вывода показывает, что конечный автомат правильно перешел из
state_a в state_b, когда внутри функции on_sa_event.

Однако строка 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

0

Решение

Проблема вызвана копированием * этого. Смотрите следующий код. 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));

Если вы копируете это указатель следующим образом, ваш код работает, как вы ожидали.

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

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]