I am using boost sml for a few days, and I've got to the point where I need to post / process an event from within an action.
I can see that it can be done directly from the transition table:
using namespace sml;
return make_transition_table(
*"s1"_s + event<my_event> / process_event(other_event{}) = "s2"_s,
"s2"_s + event<other_event> = X
);
But my use-case is different:
I have a state that has an action
for on-entry
event, which does some work, and eventually, trigger an event (from within
the state action
).
For example, with boost statechart
, each state had access to the context
and could post_event
.
Is this possible with sml
?
It is possible. You need to do 2 things.
One is setting boost::sml::queue
template argument to boost::sml::sm
.
// sml::process_queue is for post event
sml::sm<table, sml::process_queue<std::queue>> sm;
The posted event need to be pushed into a queue. When the current transition is finished, then the event popd from the queue and processed. In order to do that, Boost.SML require some queue. sml::process_queue<std::queue>
provides the queue type. You can pass any types that satisfies queue concept that behaves as std::queue
.
The other is setting sml::back::process<comma_separated_list_of_processed_event_types>
parameter to the action handler as follows:
[](auto const& /*ev*/, sml::back::process<other_event /*, my_event*/ > process) {
The argument process is callable. So you can call process
as a function. When you call process(event)
then the event is posted to the queue.
Here is an example code:
#include <iostream>
#include <queue>
#include <boost/sml.hpp>
int main() {
namespace sml = boost::sml;
struct my_event {};
struct other_event {};
struct table {
auto operator()() const noexcept {
using namespace sml;
return make_transition_table(
*"s1"_s + event<my_event> /
// In order to post event in the action,
// you need to define the action handler as follows.
// The key is the second parameter.
// `sml::back::process` is posting event callable type.
// You need to pass all event types that are posted in the action handler
// as the template argument.
// Unfortunately you cannot write `sml::back::process<_>` for all events.
[](auto const& /*ev*/, sml::back::process<other_event /*, my_event*/ > process) {
std::cout << "before post" << std::endl;
process(other_event{});
std::cout << "after post" << std::endl;
} = "s2"_s,
"s2"_s + event<other_event> = X,
// entry exit log
"s1"_s + sml::on_entry<_> / [] { std::cout << "s1 on entry" << std::endl; },
"s1"_s + sml::on_exit<_> / [] { std::cout << "s1 on exit" << std::endl; },
"s2"_s + sml::on_entry<_> / [] { std::cout << "s2 on entry" << std::endl; },
"s2"_s + sml::on_exit<_> / [] { std::cout << "s2 on exit" << std::endl; }
);
};
};
// sml::process_queue is for post event
sml::sm<table, sml::process_queue<std::queue>> sm;
sm.process_event(my_event{});
}