这是一个完整的代码示例:
// 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 是复合状态。MSM 使用子机状态来实现复合状态。最外层的状态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()
状态 A 和 B 的动作。
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 放入不同的线程并在每个线程中进行阻塞、被动等待(例如通过 unistd.h 的 usleep(useconds_t usec) 可以实现什么)更好的解决方案?我的感觉是,我没有尝试与 Boost.MSM 一起使用的 pthread 将是一个更通用/更少约束的实现?
Boost.MSMprocess_event()
不是线程安全的。所以你需要锁定它。请参阅Boost msm
AFAIK 中的线程安全,sleep()/usleep()/nanosleep() 是阻塞函数。当您在 Boost.MSM 的操作中调用它们时,这意味着它们是从process_event()
. 它需要锁定。最后,阻塞等待会互相阻塞(在本例中为 after2 和 after5)。因此,我认为 Boost.ASIO 的异步方法更好。
- 我不清楚 create 和 process 方法是如何工作的(为什么 create 函数需要可变参数模板?)。特别是,我以前没有使用过智能指针或 std::forward,因此,如果您可以对这些函数中的每一行进行人工解释,那就太好了(我没有时间在为了尝试理解这段代码)。
Boost.MSM 的后端继承了它的前端。前端构造函数是Sm(boost::asio::io_service* ios):ios(ios) {}
. 在这种情况下,构造函数的参数是ios
。但是,它可以根据用例进行更改。该函数create()
创建一个 shared_ptr back
。Andback
的构造函数将所有参数转发到前端。所以参数 ios atauto sm = Sm::create(&ios);
被转发给 Sm 的构造函数。我使用可变参数模板和 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()
使用带有&&
. 它们被称为转发引用(universal-reference)。这是一个成语,称为完美转发。请参阅http://en.cppreference.com/w/cpp/utility/forward
- 与 2 一起,更好地解释 Sm 的 wp 和 ios 成员变量的用途会很好。使用 ios 指针故意满足复制构造函数是什么意思?此外,除了构造函数 Sm(boost::asio::io_service* ios) : ios(ios) {} 之外,我没有看到 ios 被设置在任何地方,您似乎从未调用过它?
到目前为止,Boost.MSM 不支持转发引用。我写了一个拉请求见https://github.com/boostorg/msm/pull/8
所以 forwarding-reference 调用了 Boost.MSM 中的 copy-constructor。这就是我选择 boost::asio::io_service 指针的原因。但是,这不是原始问题的要点。如果我不使用转发引用,我可以使用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_ 前端内部,您在三个 on_entry 方法中有三个 BOOST_STATIC_ASSERT 调用。这些在做什么?
它在运行时什么也不做。只是在编译时检查 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();
}