parent
740e30cb2b
commit
7228bdf210
@ -0,0 +1,28 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <fmt/core.h> |
||||||
|
|
||||||
|
#ifndef FSM_DEBUG |
||||||
|
#define FSM_STATE(C, S, E, ...) case C::S: S(E, ##__VA_ARGS__); break |
||||||
|
#else |
||||||
|
#define FSM_STATE(C, S, E, ...) case C::S: fmt::println(">> " #C " " #S " event={}, state={}", int(E), int($state)); S(E, ##__VA_ARGS__); fmt::println("<< " #C " state={}", int($state)); break |
||||||
|
#endif |
||||||
|
|
||||||
|
template<typename S, typename E> |
||||||
|
class DeadSimpleFSM { |
||||||
|
protected: |
||||||
|
// BUG: don't put this in your class because state() won't work
|
||||||
|
S $state = S::START; |
||||||
|
|
||||||
|
public: |
||||||
|
template<typename... Types> |
||||||
|
void event(E event, Types... args); |
||||||
|
|
||||||
|
void state(S next_state) { |
||||||
|
$state = next_state; |
||||||
|
} |
||||||
|
|
||||||
|
bool in_state(S state) { |
||||||
|
return $state == state; |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,67 @@ |
|||||||
|
#include <catch2/catch_test_macros.hpp> |
||||||
|
#include <fmt/core.h> |
||||||
|
#include <string> |
||||||
|
#include "../fsm.hpp" |
||||||
|
|
||||||
|
using namespace fmt; |
||||||
|
using std::string; |
||||||
|
|
||||||
|
enum class MyState { |
||||||
|
START, RUNNING, END |
||||||
|
}; |
||||||
|
|
||||||
|
enum class MyEvent { |
||||||
|
STARTED, PUSH, QUIT |
||||||
|
}; |
||||||
|
|
||||||
|
class MyFSM : public DeadSimpleFSM<MyState, MyEvent> { |
||||||
|
public: |
||||||
|
void event(MyEvent ev, string data="") { |
||||||
|
switch($state) { |
||||||
|
FSM_STATE(MyState, START, ev); |
||||||
|
FSM_STATE(MyState, RUNNING, ev, data); |
||||||
|
FSM_STATE(MyState, END, ev); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void START(MyEvent ev) { |
||||||
|
println("<<< START {}", (int)ev); |
||||||
|
state(MyState::RUNNING); |
||||||
|
} |
||||||
|
|
||||||
|
void RUNNING(MyEvent ev, string &data) { |
||||||
|
if(ev == MyEvent::QUIT) { |
||||||
|
println("<<< QUITTING {}", data); |
||||||
|
state(MyState::END); |
||||||
|
} else { |
||||||
|
println("<<< RUN: {}", data); |
||||||
|
state(MyState::RUNNING); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void END(MyEvent ev) { |
||||||
|
println("<<< STOP {}", (int)ev); |
||||||
|
state(MyState::END); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_CASE("confirm fsm works with optional data", "[utils]") { |
||||||
|
MyFSM fsm; |
||||||
|
|
||||||
|
REQUIRE(fsm.in_state(MyState::START)); |
||||||
|
|
||||||
|
fsm.event(MyEvent::STARTED); |
||||||
|
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||||
|
|
||||||
|
fsm.event(MyEvent::PUSH); |
||||||
|
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||||
|
|
||||||
|
fsm.event(MyEvent::PUSH); |
||||||
|
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||||
|
|
||||||
|
fsm.event(MyEvent::PUSH); |
||||||
|
REQUIRE(fsm.in_state(MyState::RUNNING)); |
||||||
|
|
||||||
|
fsm.event(MyEvent::QUIT, "DONE!"); |
||||||
|
REQUIRE(fsm.in_state(MyState::END)); |
||||||
|
} |
Loading…
Reference in new issue