c++booststate-machineboost-sml

Boost State Machine Language - `post` event from within an `action`


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 actionfor 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?


Solution

  • 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{});
    }
    

    Live demo: https://wandbox.org/permlink/yueELv7SoFbPCEW1