Skip to content

Instantly share code, notes, and snippets.

@saltzm
Created November 18, 2019 16:26
Show Gist options
  • Save saltzm/9b4bddb0f07535e5b2e790003324e202 to your computer and use it in GitHub Desktop.
Save saltzm/9b4bddb0f07535e5b2e790003324e202 to your computer and use it in GitHub Desktop.
#include <memory>
#include <iostream>
#include <variant>
#include <optional>
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template <typename... Ts>
inline constexpr bool isVariant = false;
template <typename... Ts>
inline constexpr bool isVariant<std::variant<Ts...>> = true;
template<typename InternalStateMachine>
class StateMachine {
public:
StateMachine() : _behavior(typename InternalStateMachine::InitialBehavior{}), _self() {}
void receive(typename InternalStateMachine::Message&& m) {
_self.prereceive();
if constexpr (isVariant<typename InternalStateMachine::Message>) {
std::visit(overloaded {
[this](auto&& arg) {
if constexpr (isVariant<typename InternalStateMachine::Behavior>) {
std::visit(overloaded {
[this, &arg] (auto &behavior) {
auto newBehavior = behavior.receive(_self, arg);
if (newBehavior) {
_behavior = *newBehavior;
}
}
}, _behavior);
} else {
auto newBehavior = _behavior.receive(_self, arg);
if (newBehavior) {
_behavior = *newBehavior;
}
}
}
}, std::move(m));
} else {
auto newBehavior = _behavior.receive(_self, m);
if (newBehavior) {
_behavior = *newBehavior;
}
}
_self.postreceive();
}
InternalStateMachine& operator*() {
return _self;
}
typename InternalStateMachine::Behavior _behavior;
InternalStateMachine _self;
};
struct Msg1 {};
struct Msg2 {};
class MyStateMachine {
public:
// Behaviors
struct Start; struct Running; struct Stopped;
using Message = std::variant<Msg1, Msg2>;
using Behavior = std::variant<Start, Running, Stopped>;
using InitialBehavior = Start;
void prereceive() {
globalCounter++;
}
void postreceive() {
}
struct Start {
std::optional<Behavior> receive(MyStateMachine& self, const Msg1& m) {
std::cout << "Start: received msg 1, globalCounter: " << self.globalCounter << std::endl;
return Running{};
}
std::optional<Behavior> receive(MyStateMachine& self, const Msg2& m) {
return std::nullopt;
}
};
struct Running {
std::optional<Behavior> receive(MyStateMachine& self, const Msg1& m) {
std::cout << "Running: received msg 1, globalCounter: " << self.globalCounter << ", localCounter: " << localCounter << std::endl;
++localCounter;
if (localCounter >= 2) {
return Stopped{};
} else {
return std::nullopt;
}
}
std::optional<Behavior> receive(MyStateMachine& self, const Msg2& m) {
std::cout << "Running: received msg 2, globalCounter: " << self.globalCounter << ", localCounter: " << localCounter << std::endl;
++localCounter;
if (localCounter >= 2) {
return Stopped{};
} else {
return std::nullopt;
}
return std::nullopt;
}
int localCounter{0};
};
struct Stopped {
std::optional<Behavior> receive(MyStateMachine& self, const Msg1& m) {
std::cout << "Stopped: received msg 1, globalCounter: " << self.globalCounter << std::endl;
return std::nullopt;
}
std::optional<Behavior> receive(MyStateMachine& self, const Msg2& m) {
std::cout << "Stopped: received msg 2, globalCounter: " << self.globalCounter << std::endl;
return std::nullopt;
}
};
int globalCounter{0};
};
class SingleStateSingleMessageMachine {
public:
// Behaviors
struct Running;
// Works even without variant :)
using Message = Msg1;
using Behavior = Running;
using InitialBehavior = Running;
void prereceive() {
globalCounter++;
}
void postreceive() {
}
struct Running {
std::optional<Behavior> receive(SingleStateSingleMessageMachine& self, const Msg1& m) {
std::cout << "Running: received msg 1, globalCounter: " << self.globalCounter << std::endl;
return std::nullopt;
}
};
int globalCounter{0};
};
int main() {
StateMachine<MyStateMachine> myStateMachine;
myStateMachine.receive(Msg1{});
myStateMachine.receive(Msg1{});
myStateMachine.receive(Msg2{});
myStateMachine.receive(Msg1{});
myStateMachine.receive(Msg2{});
/* Outputs:
Start: received msg 1, globalCounter: 1
Running: received msg 1, globalCounter: 2, localCounter: 0
Running: received msg 2, globalCounter: 3, localCounter: 1
Stopped: received msg 1, globalCounter: 4
Stopped: received msg 2, globalCounter: 5
*/
StateMachine<SingleStateSingleMessageMachine> boringStateMachine;
boringStateMachine.receive(Msg1{});
boringStateMachine.receive(Msg1{});
/* Outputs:
Running: received msg 1, globalCounter: 1
Running: received msg 1, globalCounter: 2
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment