I am trying to use a Boost SML state machine to implement a "receiver". As an example, lets say the SM receives ints and is "done" when it gets to a certain number:
- An "idle" state moves to a "reading" state on a "receive" event, accumulate the received data
- If the limit is reached, terminate, or else keep reading
I have done this so far with a guard, gNotDone
, which returns true if the capacity is not reached.
And then while reading, having passed the guard, actually process the data in the aReceive
action.
#include <boost/sml.hpp>
#include <iostream>
namespace sml = boost::sml;
namespace {
struct eReceive {
int data;
};
struct sIdle{};
struct sReading{};
struct Context {
Context(const std::string& name, unsigned capacity):
name(name),
received(0),
capacity(capacity) {
}
std::string name;
unsigned received;
unsigned capacity;
};
struct SM {
auto operator()() const noexcept {
using namespace sml;
const auto gNotDone = [this] (const Context& ctx) -> bool {
return ctx.received < ctx.capacity;
};
const auto aReceive = [this] (const eReceive& e, Context& ctx) {
ctx.received += e.data;
std::cout << "Received " << e.data << ", so far: " << ctx.received << std::endl;
};
const auto aDone = [] {
std::cout << "Done (reach capacity)" << std::endl;
};
// clang-format off
return make_transition_table(
// Start reading
*state<sIdle> + event<eReceive> [gNotDone] / aReceive = state<sReading>
// Keeps reading until "done"
, state<sReading> + event<eReceive> [gNotDone] / aReceive
, state<sReading> + event<eReceive> / aDone = X
);
// clang-format on
}
};
} // namespace
int main() {
Context theContext("The context", 30);
sml::sm<SM> sm{theContext};
sm.process_event(eReceive{11});
sm.process_event(eReceive{22});
}
Received 11, so far: 11
Received 22, so far: 33
However, I don't feel this is quite right: it feels like the next state should somehow be determined by the aReceive
action - if the limit was passed, move into the terminal state.
As it is, you'll only move into the terminal state after the next receive event.
It also doesn't feel right to "test" in guards if the event will pass the limit as this probably entails repeating the calculation (it's just a sum here, but it could be expensive).
What is the standard way in an SML state machine to decide the next state based on what happened with an event in the current state?
[!gGuard] ...`. E.g. https://cpp.godbolt.org/z/7GnhKzr7v– Inductiveload Jul 26 '22 at 20:52