Search code examples
c++booststate-machineboost-sml

How to pass data to an event inside a boost::sml state machine for using the 'direct' process method?


Consider the example from the sml documentation regarding process and defer, rewritten a bit by me to pass a dependency to the state machine to be used in the event processing:


// $CXX -std=c++14 defer_and_process.cpp
#include <boost/sml.hpp>
#include <cassert>
#include <deque>
#include <queue>

namespace sml = boost::sml;

namespace {
struct e1 {};
struct e2 {int dependency = 0;};
struct e3 {};
struct e4 {};

struct defer_and_process {
  auto operator()() const noexcept {
    using namespace sml;
    return make_transition_table(
       *"idle"_s + event<e1> / defer
      , "idle"_s + event<e2> = "s1"_s
      , "s1"_s   + event<e1> / process(e2{/*pass my_dependency here*/}) = "s2"_s // this is the interesting bit
      , "s2"_s   + event<e3> / process(e4{})
      , "s2"_s   + event<e4> = X
    );
  }
};
}  // namespace

int main() {
  int my_dependency = 42;
  using namespace sml;
  sm<defer_and_process, sml::defer_queue<std::deque>, sml::process_queue<std::queue>>
      sm{my_dependency}; 
  assert(sm.is("idle"_s));

  assert(sm.process_event(e1{}));
  assert(sm.is("idle"_s));

  assert(!sm.process_event(e2{}));  /// triggers idle -> s1 and s1 -> s2 (via deferred e1)
  assert(sm.is("s2"_s));

  assert(sm.process_event(e3{}));  /// triggers s2.process(e4) -> X (via processed e4)
  assert(sm.is(sml::X));
}

Is it possible to do that directly or...

Is the lambda way described in Boost State Machine Language - `post` event from within an `action`

The only way to do it? Is inlining not possible?

Like process(e2{some way to pull the data out of the sm dependency list and pass it here }) ?


Solution

  • Apparently the callable object is the way to go. Got it.

    This means that the context of the state machine class/struct does not have access to any injected objects, in the case of the original post, my_dependency.

    On the other hand, actions and guards benefit from the automatic dependency injection. The solution to the original problem is to create an action, lambda or an class object with () operator overload, and use that instead of the built-in process action.

    The difference is that with the built-in process action, the event is initialized in the state machine context, where the dependencies are not available. With a custom action, the dependencies get injected.