Bring over the FSM and then use it to make the calculator demo better.

main
Zed A. Shaw 4 days ago
parent 4b07ecac45
commit c0c63e5b2c
  1. 105
      demos/calc.cpp
  2. 28
      fsm.hpp

@ -6,6 +6,9 @@
#include "constants.hpp"
#include <fmt/xchar.h>
#define FSM_DEBUG 1
#include "fsm.hpp"
constexpr const int WINDOW_WIDTH=300;
constexpr const int WINDOW_HEIGHT=400;
@ -17,22 +20,85 @@ const std::unordered_map<string, wstring> LABELS {
{"btn5", L"5"}, {"btn6", L"6"}, {"btn7", L"7"},
{"btn8", L"8"}, {"btn9", L"9"}, {"mult", L"*"},
{"minus", L"-"}, {"plus", L"+"}, {"neg", L"!"},
{"dot", L"."}, {"eq", L"="}
{"div", L"/"}, {"eq", L"="}
};
enum class Event {
NUMBER=0,
OP=1,
CLR=2,
NOT=3,
EQ=4
};
enum class State {
START=0,
CALCULATING=1,
CLEARED=3,
DISPLAYED=4
};
struct Calculator {
struct Calculator : DeadSimpleFSM<State, Event> {
wstring input;
double value = 0.0;
void event(Event ev, wchar_t op) {
switch($state) {
FSM_STATE(State, START, ev, op);
FSM_STATE(State, CALCULATING, ev, op);
FSM_STATE(State, CLEARED, ev, op);
FSM_STATE(State, DISPLAYED, ev, op);
}
}
void START(Event ev, wchar_t op) {
if(ev == Event::NUMBER) {
input += op;
state(State::CALCULATING);
}
}
void CALCULATING(Event ev, wchar_t op) {
if(ev == Event::NUMBER) {
input += op;
} else if(ev == Event::OP) {
} else if(ev == Event::NOT) {
} else if(ev == Event::EQ) {
} else if(ev == Event::CLR) {
input = L"";
state(State::CLEARED);
}
}
void CLEARED(Event ev, wchar_t op) {
if(ev == Event::NUMBER) {
input += op;
state(State::CALCULATING);
}
}
void DISPLAYED(Event ev, wchar_t op) {
fmt::println(L"ev={}, op={}", (int)ev, op);
state(State::CALCULATING);
}
};
struct CalculatorUI {
guecs::UI $gui;
wstring $input;
double $value = 0.0;
Calculator $fsm;
Calculator() {
CalculatorUI() {
$gui.position(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
$gui.layout(
"[*%(400)readout|_|_|_|clear]"
"[btn7|btn8|btn9|mult]"
"[btn4|btn5|btn6|minus]"
"[btn1|btn2|btn3|plus]"
"[neg|btn0|dot|eq]");
"[neg|btn0|eq|div]");
}
void init() {
@ -79,39 +145,26 @@ struct Calculator {
case L'7':
case L'8':
case L'9':
case L'.':
if($input.size() <= 10) {
$input += op;
}
$fsm.event(Event::NUMBER, op);
break;
case L'*':
$value = $value * std::stof($input);
$input = L"";
break;
case L'-':
$value = $value - std::stof($input);
$input = L"";
break;
case L'+':
$value = $value + std::stof($input);
$input = L"";
break;
case L'/':
case L'!':
$value = $value * -1.0;
$input = L"";
$fsm.event(Event::OP, op);
break;
case L'=':
$input = fmt::format(L"{}", $value);
$value = 0.0;
$fsm.event(Event::EQ, op);
break;
case L'C':
$input = L"";
$fsm.event(Event::CLR, op);
break;
}
auto readout = $gui.entity("readout");
auto& label = $gui.get<guecs::Textual>(readout);
label.update($input);
label.update($fsm.input);
}
};
@ -126,7 +179,7 @@ int main() {
window.setFramerateLimit(FRAME_LIMIT);
window.setVerticalSyncEnabled(VSYNC);
Calculator calc;
CalculatorUI calc;
calc.init();
while(window.isOpen()) {

@ -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;
}
};
Loading…
Cancel
Save