简体   繁体   中英

Understanding hello_world boost sml state machine library

Here's a hello world example for boost sml C++ library from here: https://boost-ext.github.io/sml/examples.html#hello-world

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

namespace sml = boost::sml;

namespace {
struct release {};
struct ack {};
struct fin {};
struct timeout {};

const auto is_ack_valid = [](const ack&) { return true; };
const auto is_fin_valid = [](const fin&) { return true; };

const auto send_fin = [] {};
const auto send_ack = [] {};

#if !defined(_MSC_VER)
struct hello_world {
  auto operator()() const {
    using namespace sml;
    return make_transition_table(
      *"established"_s + event<release> / send_fin = "fin wait 1"_s,
       "fin wait 1"_s + event<ack> [ is_ack_valid ] = "fin wait 2"_s,
       "fin wait 2"_s + event<fin> [ is_fin_valid ] / send_ack = "timed wait"_s,
       "timed wait"_s + event<timeout> / send_ack = X
    );
  }
};
}

int main() {
  using namespace sml;

  sm<hello_world> sm;
  static_assert(1 == sizeof(sm), "sizeof(sm) != 1b");
  assert(sm.is("established"_s));

  sm.process_event(release{});
  assert(sm.is("fin wait 1"_s));

  sm.process_event(ack{});
  assert(sm.is("fin wait 2"_s));

  sm.process_event(fin{});
  assert(sm.is("timed wait"_s));

  sm.process_event(timeout{});
  assert(sm.is(X));  // released
}
#else
class established;
class fin_wait_1;
class fin_wait_2;
class timed_wait;

struct hello_world {
  auto operator()() const {
    using namespace sml;
    return make_transition_table(
      *state<established> + event<release> / send_fin = state<fin_wait_1>,
       state<fin_wait_1> + event<ack> [ is_ack_valid ] = state<fin_wait_2>,
       state<fin_wait_2> + event<fin> [ is_fin_valid ] / send_ack = state<timed_wait>,
       state<timed_wait> + event<timeout> / send_ack = X
    );
  }
};
}

int main() {
  using namespace sml;

  sm<hello_world> sm;
  assert(sm.is(state<established>));

  sm.process_event(release{});
  assert(sm.is(state<fin_wait_1>));

  sm.process_event(ack{});
  assert(sm.is(state<fin_wait_2>));

  sm.process_event(fin{});
  assert(sm.is(state<timed_wait>));

  sm.process_event(timeout{});
  assert(sm.is(X));  // released
}
#endif

I understand that _s creates a state with a name, and events can happen on states, which send the state machine to another state.

Let's look at the hello_world state machine:

struct hello_world {
  auto operator()() const {
    using namespace sml;
    return make_transition_table(
      *"established"_s + event<release> / send_fin = "fin wait 1"_s,
       "fin wait 1"_s + event<ack> [ is_ack_valid ] = "fin wait 2"_s,
       "fin wait 2"_s + event<fin> [ is_fin_valid ] / send_ack = "timed wait"_s,
       "timed wait"_s + event<timeout> / send_ack = X
    );
  }
};

If I understood correctly, it means that when the state machine is in the state established and receives an event release , it goes to state fin wait 1 . What is send_fin though? What is the * in *state<established> + event<release> / send_fin = state<fin_wait_1> and what is this crazy syntax? I think + , / and = are simply overwritten operators that make it easy to create the transition tables. Also, what is is_ack_valid ?

My guess for * is that it specifies the beggining state of the state machine.

Events are things that happen.

If I understood correctly, it means that when the state machine is in the state established and receives an event release, it goes to state fin wait 1.

True

What is send_fin though?

An action that is triggered when release is received in state established. It's a lambda that does absolutely nothing in the example. :shrug:

What is the * in *state + event / send_fin = state<fin_wait_1> and what is this crazy syntax? I think +, / and = are simply overwritten operators that make it easy to create the transition tables.

The * is the initial state, you are correct. Worth noting, a complex transition table can actually have multiple submachines being tracked, therefore multiple *s for each section.

I very much suspect you are correct that operator overloading drove the syntax a bit. Speaking of design, it's also heavily compile time optimized via TMP.

Also, what is is_ack_valid?

An expression wrapping in brackets after an event is called a guard. A guard that evaluates false will prevent the any action or transition from happening even if the event is received.

In this simple example the lambda always returns true, so it's useless as written. A more complete example would actually check something on the ack.

My guess for * is that it specifies the beginning state of the state machine.

True

Events are things that happen.

Sure. Worth nothing they are strongly typed and intentionally dispatched to the state machine.

Unsolicited commentary below.

I've used SML pretty heavily now at work. At first, it was one more thing to learn as I was already drinking from a fire hose.

After living with it for a while, the positive things I'd say are it can very concisely represent complex flow control and it is really efficient.

If you had a chance to watch Kris's 2019 c++ now talk, his TCP connection state management is a really good example of where this shines.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM