Created
November 18, 2019 16:26
-
-
Save saltzm/9b4bddb0f07535e5b2e790003324e202 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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