#include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; template struct ConsoleIO { static auto ReadlineS(std::istream& is, std::function)> f) { return [=,&is] { if (std::string line; std::getline(is, line)) { return f(std::make_optional(line)); } else { return f(std::nullopt); } }; } static std::function Readline(std::function)> f) { return ReadlineS (std::ref(std::cin), f); } static std::function Prompt(std::string prompt, std::function)> f) { return [=] { std::cout << prompt << std::flush; return Readline (f)(); }; } }; template struct AppT { using Model = ModelT; using Message = MessageT; using Command = std::function; using InitResult = std::tuple>; using UpdateResult = InitResult; using ViewResult = std::string; using ReadLine = std::function; using Console = ConsoleIO; using Init = std::function; using Update = std::function; using View = std::function; using Done = std::function; struct Config { Init init; Update update; View view; Done done; }; int run (const Config & cfg) { auto const& [init, update, view, done] = cfg; auto [state, cmds] = init(); while (! done (state)) { std::cout << view(state) << "\n"; if (!cmds.empty()) { auto [newstate, newcmds] = update (state, cmds.front()()); cmds.erase (cmds.begin()); cmds.insert (cmds.end(), std::begin(newcmds), std::end (newcmds)); state = newstate; } std::this_thread::sleep_for (100ms); } std::cout << "app finished with " << cmds.size() << " pending commands and final state: \n" << view(state) << '\n'; return 0; } }; // user defined struct Model { bool done{false}; int value{0}; }; struct SetValue { int s; }; struct Stop { }; using Message = std::variant; auto userInputToMsg(const std::optional& s) -> Message { if (s) { std::stringstream sstr(*s); int r; sstr >> r; return SetValue { r }; } else { return Stop{}; } } int main() { using App = AppT; App app; return app.run(App::Config{ []() -> App::InitResult { return {{false, 23}, {App::Console::Prompt("> ", userInputToMsg)}}; }, // init [](App::Model state, const App::Message &msg) -> App::UpdateResult { if (auto stop = std::get_if(&msg); stop) { state.done = true; return {state, {}}; } else { state.value = std::get(msg).s; return {state, {App::Console::Prompt("> ", userInputToMsg)}}; } }, // update [](const App::Model &state) -> App::ViewResult { return "Model " + std::to_string(state.value); }, // view [](const App::Model &state) -> bool { return state.done; } // done }); }