Я использую Boost MSM (базовые и функторные интерфейсы) и пытаюсь реализовать следующий конечный автомат:
Прописью:
Я хотел бы знать, как создать этот конечный автомат в Boost MSM. Здесь есть две хитрости, которые я не могу понять, как это сделать:
Большое спасибо за помощь.
редактировать
Ответ @TakatoshiKondo делает то, что мне нужно, но я бы хотел получить более подробное объяснение некоторых частей ответа, чтобы полностью его понять.
usleep(useconds_t usec)
из unistd.h
)? Мне кажется, что pthreads, которые я не пробовал использовать с Boost.MSM, будет более общей / менее ограниченной реализацией?create
а также process
методы работы (почему create
функции нужен шаблон с переменной частотой?). В частности, я раньше не работал с умными указателями или std::forward
, поэтому, если бы вы могли дать человеческое объяснение каждой строки в этих функциях, было бы здорово (у меня мало времени, чтобы в общих чертах прочитать об этих функциях, чтобы попытаться понять этот код).wp
а также ios
переменные-члены Sm
было бы замечательно. Что вы имеете в виду, используя ios
указатель на намеренно встретить копию конструктора? Я кроме того не вижу ios
устанавливается где угодно, но не в конструкторе Sm(boost::asio::io_service* ios) : ios(ios) {}
Что, кажется, ты никогда не звонишь?State1_
передний конец, у вас есть три BOOST_STATIC_ASSERT
звонки в три on_entry
методы. Что они делают?main()
функция, я смог удалить строку auto t = std::make_shared<boost::asio::deadline_timer>(ios);
без изменения поведения — было ли это излишним?Вот полный пример кода для этого:
// g++ example.cpp -lboost_system
#include <iostream>
#include <boost/asio.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;// ----- State machine
struct Sm : msmf::state_machine_def<Sm> {
using back = msm::back::state_machine<Sm>;
template <typename... T>
static std::shared_ptr<back> create(T&&... t) {
auto p = std::make_shared<back>(std::forward<T>(t)...);
p->wp = p; // set wp after creation.
return p;
}
template <typename Ev>
void process(Ev&& ev) {
// process_event via backend weak_ptr
wp.lock()->process_event(std::forward<Ev>(ev));
}
// ----- Events
struct EvSetParent {};
struct After2 {};
struct After5 {};
Sm(boost::asio::io_service* ios):ios(ios) {}
struct State1_:msmf::state_machine_def<State1_> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, Sm>::value));
std::cout << "State1::on_entry()" << std::endl;
f.process(EvSetParent());
}
struct Action {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm&, SourceState&, TargetState&) const {
std::cout << "Trying again..." << std::endl;
}
};
struct A:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "A::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(*f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(2));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After2());
}
);
}
};
struct B:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "B::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(*f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(5));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After5());
}
);
}
};
// Set initial state
typedef mpl::vector<A, B> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < A, After2, A, Action, msmf::none >,
msmf::Row < B, After5, B, Action, msmf::none >
> {};
Sm* parent;
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State1 initial_state;
struct ActSetParent {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm& f, SourceState& s, TargetState&) const {
std::cout << "ActSetIos" << std::endl;
s.parent = &f; // set parent state machine to use process() in A and B.
}
};
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, EvSetParent, msmf::none, ActSetParent, msmf::none >
> {};
// front-end can access to back-end via wp.
std::weak_ptr<back> wp;
boost::asio::io_service* ios; // use pointer intentionally to meet copy constructible
};int main() {
boost::asio::io_service ios;
auto t = std::make_shared<boost::asio::deadline_timer>(ios);
auto sm = Sm::create(&ios);
ios.post(
[&]{
sm->start();
}
);
ios.run();
}
Давайте копать код.
Boost.MSM не поддерживает механизм запуска отложенных событий. Итак, нам нужен механизм обработки таймера. Я выбираю Boost.Asio таймер крайнего срока. Он хорошо работает с управляемой событиями библиотекой, такой как Boost.MSM.
Чтобы вызвать process_event () во внешнем интерфейсе конечного автомата, ему необходимо знать его внутренний интерфейс. Так я написала create()
функция.
template <typename... T>
static std::shared_ptr<back> create(T&&... t) {
auto p = std::make_shared<back>(std::forward<T>(t)...);
p->wp = p; // set wp after creation.
return p;
}
Он создает shared_ptr серверной части, а затем назначает его для weak_ptr.
Если weak_ptr установлен правильно, то я могу позвонить process_event()
следующее. Я написал обертку process()
,
template <typename Ev>
void process(Ev&& ev) {
// process_event via backend weak_ptr
wp.lock()->process_event(std::forward<Ev>(ev));
}
Код клиента вызывает функцию create () следующим образом:
auto sm = Sm::create(&ios);
Sm имеет переменную-член ios для установки таймера крайнего срока. Интерфейс конечного автомата требуется для копирования MSM. Таким образом, ios является указателем io_service, а не ссылкой.
Состояния A и B являются ортогональными областями. Чтобы реализовать ортогональные области, определите несколько начальных состояний как mpl :: vector.
typedef mpl::vector<A, B> initial_state;
Состояние A и B является составным состоянием. МСМ использует состояние подсхемы для реализации составных состояний. Наружный самый государственный Sm
это конечный автомат и State1_
это тоже конечный автомат. Я устанавливаю таймер в действии ввода состояний A и B. И когда таймер срабатывает, звоните process()
, Тем не мение, processs()
является функцией-членом Sm
не State1_
, Поэтому мне нужно реализовать какой-то механизм доступа Sm
от Stete1_
,
Я добавил переменную-член parent
в State1_
, Это указатель Sm
, Во вступительном действии State1_
, Я звоню process()
и событие PEvSetParent. It simply invokes
ActSetParent. In the action, SourceState is
State1_`. Я устанавливаю родительскую переменную-член в родительский указатель следующим образом:
struct ActSetParent {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm& f, SourceState& s, TargetState&) const {
std::cout << "ActSetIos" << std::endl;
s.parent = &f; // set parent state machine to use process() in A and B.
}
};
Наконец я могу позвонить process()
в действии государства А и Б.
struct A:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "A::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(*f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(2));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After2());
}
);
}
};
редактировать
- Как это соотносится с реализацией pthreads? Как вы думаете, Boost.Asio — лучшее решение, чем помещать состояния A и B в разные потоки и иметь блокирующие пассивные ожидания в каждом (например, что может быть достигнуто с помощью usleep (useconds_t usec) из unistd.h)? Мне кажется, что pthreads, которые я не пробовал использовать с Boost.MSM, будет более общей / менее ограниченной реализацией?
Boost.MSM-х process_event()
НЕ является потокобезопасным. Так что вам нужно заблокировать это. Увидеть Безопасность потоков в Boost msm
AFAIK, sleep () / usleep () / nanosleep () являются блокирующими функциями. Когда вы вызываете их в действии Boost.MSM, это означает, что они вызываются (ogirinally) из process_event()
, И это требует блокировки. Наконец, блокировка wait блокирует друг друга (в данном случае after2 и after5). Поэтому я считаю, что асинхронный подход Boost.ASIO лучше.
- Мне не ясно, как работают методы создания и обработки (зачем функции создания нужен шаблон с переменным числом аргументов?). В частности, я ранее не работал со смарт-указателями или std :: forward, поэтому, если бы вы могли дать человеческое объяснение каждой строки в этих функциях, было бы здорово (у меня мало времени, чтобы прочитать об этих функциях в общих чертах в Для того, чтобы попытаться понять этот код).
Бэкэнд Boost.MSM наследует свой внешний интерфейс. Конструктор внешнего интерфейса Sm(boost::asio::io_service* ios):ios(ios) {}
, В этом случае параметром конструктора является ios
, Тем не менее, это может быть изменено в зависимости от варианта использования. Функция create()
создает shared_ptr из back
, А также back
Конструктор перенаправляет все параметры во внешний интерфейс. Таким образом, аргумент IOS в auto sm = Sm::create(&ios);
пересылается конструктору См. Причина, по которой я использую шаблоны variadic и std :: forward, заключается в максимальной гибкости. Если параметры конструктора Sm изменяются, мне не нужно менять create()
функция.
Вы можете изменить create()
функционировать следующим образом:
static std::shared_ptr<back> create(boost::asio::io_service* ios) {
auto p = std::make_shared<back>(ios);
p->wp = p; // set wp after creation.
return p;
}
К тому же, create()
а также process()
использовать параметры шаблона, которые с &&
, Они называются пересылка-ссылка (универсальная ссылка). Это идиома называется совершенной пересылкой.
Увидеть http://en.cppreference.com/w/cpp/utility/forward
- В случае со 2 лучше было бы лучше объяснить назначение переменных-членов wp и ios для Sm. Что вы имеете в виду, используя указатель ios, чтобы преднамеренно встретить конструктор копирования? Более того, я не вижу, чтобы ios был установлен нигде, кроме как в конструкторе Sm (boost :: asio :: io_service * ios): ios (ios) {}, который, кажется, никогда не вызывается?
Boost.MSM пока не поддерживает переадресацию. Я написал запрос на просмотр. https://github.com/boostorg/msm/pull/8
Таким образом, forwarding-reference вызывает конструктор копирования в Boost.MSM. По этой причине я выбираю указатель boost :: asio :: io_service. Тем не менее, это не является существенным пунктом оригинального вопроса. Если я не использую forwarding-reference, я могу использовать ссылочные типы в Sm
, Поэтому я обновляю код следующим образом:
static std::shared_ptr<back> create(boost::asio::io_service& ios) {
auto p = std::make_shared<back>(std::ref(ios));
p->wp = p; // set wp after creation.
return p;
}
std::ref
не для make_shared. Это для Boost.MSM. Конструктор Boost.MSM требует указывать ссылку или нет из-за отсутствия поддержки пересылки ссылок.
- В интерфейсе State1_ у вас есть три вызова BOOST_STATIC_ASSERT в трех методах on_entry. Что они делают?
Это ничего не делает во время выполнения. Просто проверять тип Fsm во время компиляции. Иногда я путался с типом Fsm. Я думаю, что читатели также могут запутаться, поэтому я оставляю это в коде.
- В функции main () я смог удалить строку auto t = std :: make_shared (ios); без изменения поведения — было ли это излишним?
Ага, я забыл стереть это. Я обновляю код.
Вот обновленный код:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;// ----- State machine
struct Sm : msmf::state_machine_def<Sm> {
using back = msm::back::state_machine<Sm>;
static std::shared_ptr<back> create(boost::asio::io_service& ios) {
auto p = std::make_shared<back>(std::ref(ios));
p->wp = p; // set wp after creation.
return p;
}
template <typename Ev>
void process(Ev&& ev) {
// process_event via backend weak_ptr
wp.lock()->process_event(std::forward<Ev>(ev));
}
// ----- Events
struct EvSetParent {};
struct After2 {};
struct After5 {};
Sm(boost::asio::io_service& ios):ios(ios) {}
struct State1_:msmf::state_machine_def<State1_> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, Sm>::value));
std::cout << "State1::on_entry()" << std::endl;
f.process(EvSetParent());
}
struct Action {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm&, SourceState&, TargetState&) const {
std::cout << "Trying again..." << std::endl;
}
};
struct A:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "A::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(2));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After2());
}
);
}
};
struct B:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& f) const {
BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
std::cout << "B::on_entry()" << std::endl;
auto t = std::make_shared<boost::asio::deadline_timer>(f.parent->ios);
t->expires_from_now(boost::posix_time::seconds(5));
t->async_wait([t, &f](boost::system::error_code const) {
f.parent->process(After5());
}
);
}
};
// Set initial state
typedef mpl::vector<A, B> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < A, After2, A, Action, msmf::none >,
msmf::Row < B, After5, B, Action, msmf::none >
> {};
Sm* parent;
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State1 initial_state;
struct ActSetParent {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const&, Fsm& f, SourceState& s, TargetState&) const {
std::cout << "ActSetIos" << std::endl;
s.parent = &f; // set parent state machine to use process() in A and B.
}
};
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, EvSetParent, msmf::none, ActSetParent, msmf::none >
> {};
// front-end can access to back-end via wp.
std::weak_ptr<back> wp;
boost::asio::io_service& ios;
};int main() {
boost::asio::io_service ios;
auto sm = Sm::create(ios);
ios.post(
[&]{
sm->start();
}
);
ios.run();
}
Других решений пока нет …