stamen

Stamen - static menu generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE

commit cff8002912e2fbd52d9904edabc930a9aa077fe5
parent 7a6fd17c3d31fd728820bd3aa259894d7efbedb3
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Thu,  9 Feb 2023 22:33:28 +0100

Ability to create multiple independent menus

- Menu is no longer static
- Extract display logic to separate cpp file

Diffstat:
M.ccls | 2+-
MMakefile | 2+-
Minclude/menu.h | 44+++++++++++++++++++++++++++-----------------
Asrc/display.cpp | 32++++++++++++++++++++++++++++++++
Msrc/main.cpp | 12+++++-------
Msrc/menu.cpp | 35+----------------------------------
6 files changed, 67 insertions(+), 60 deletions(-)

diff --git a/.ccls b/.ccls @@ -1,4 +1,4 @@ clang++ -style=file -%c++ -std=c++17 +-std=c++17 -Iinclude diff --git a/Makefile b/Makefile @@ -5,7 +5,7 @@ SRC = src INC = include BUILD = build -CFLAGS = -I$(INC) +CFLAGS = -I$(INC) --std=c++17 LDFLAGS = SRCS := $(wildcard $(SRC)/*.cpp) diff --git a/include/menu.h b/include/menu.h @@ -3,10 +3,13 @@ #include "json.hpp" #include <exception> +#include <fstream> #include <string> #include <unordered_map> #include <vector> + using json = nlohmann::json; +using std::string, std::vector; class EMenu_callback : std::exception { virtual const char *what() const noexcept override { @@ -23,33 +26,40 @@ class EMenu_call : std::exception { class Menu { public: typedef int (*Menu_f)(void); - static void Register(const std::string &s, Menu_f f) { + + Menu() : function_lookup() {} + Menu(const string &s) : Menu() { + std::fstream f(s); + Read(f); + } + + void Start() const { get_callback("main")(); } + + void Read(std::istream &is); + Menu &Register(const string &s, Menu_f f) { function_lookup.insert({s, f}); + return *this; } - static void Start() { get_callback("main")(); } - static void Read(std::istream &is); private: - Menu() = delete; - struct Menu_item { - const std::string prompt; - const std::string callback; + const string prompt; + const string callback; - Menu_item(const std::string &p, const std::string &c) - : prompt(p), callback(c) {} + Menu_item(const string &p, const string &c) : prompt(p), callback(c) {} Menu_item(const json &j) : Menu_item(j["prompt"], j["callback"]) {} }; struct Menu_function { - Menu_function(const std::string &n, const std::vector<Menu_item> &i) - : name(n), items(i) {} + Menu_function(const Menu &m, const string &n, const vector<Menu_item> &i) + : menu(m), name(n), items(i) {} - int call() const; + int display() const; private: - const std::string name; - const std::vector<Menu_item> items; + const Menu &menu; + const string name; + const vector<Menu_item> items; }; struct Menu_callback { @@ -61,17 +71,17 @@ private: int operator()() const { if (!func && !menu_func) throw EMenu_callback(); - return func ? func() : menu_func->call(); + return func ? func() : menu_func->display(); } }; - static const Menu_callback &get_callback(const std::string &s) { + const Menu_callback &get_callback(const string &s) const { auto it = function_lookup.find(s); if (it == function_lookup.end()) throw EMenu_call(); return (*it).second; } - static std::unordered_map<std::string, Menu_callback> function_lookup; + std::unordered_map<string, Menu_callback> function_lookup; }; #endif diff --git a/src/display.cpp b/src/display.cpp @@ -0,0 +1,32 @@ +#include "menu.h" +#include <iostream> + +int Menu::Menu_function::display() const { + unsigned long choice; + while (true) { + std::cout << name << std::endl; + + for (auto i = 0ul; i < items.size(); i++) + std::cout << i << ". " << items[i].prompt << std::endl; + + while (true) { + std::cout << "Choose an option: "; + if (std::cin >> choice && choice < items.size()) { + std::cout << "Chosen: " << items[choice].prompt << "\n\n"; + int res = menu.get_callback(items[choice].callback)(); + if (--res) return res; + break; + } else if (std::cin.eof()) { + std::cerr << "encountered end of input!\n"; + return std::numeric_limits<int>::max(); + } else { + std::cin.clear(); + std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); + } + std::cout << "Invalid option, please choose again!" << std::endl; + } + std::cout << std::endl; + } + + return 1; +} diff --git a/src/main.cpp b/src/main.cpp @@ -1,5 +1,4 @@ #include "menu.h" -#include <fstream> #include <iostream> int algorithms(void) { @@ -18,11 +17,10 @@ int finish(void) { } int main(void) { - std::fstream f("menu.json"); - Menu::Register("algorithms", algorithms); - Menu::Read(f); - Menu::Register("settings", settings); - Menu::Register("finish", finish); - Menu::Start(); + Menu menu("menu.json"); + menu.Register("algorithms", algorithms) + .Register("settings", settings) + .Register("finish", finish); + menu.Start(); return 0; } diff --git a/src/menu.cpp b/src/menu.cpp @@ -1,44 +1,11 @@ #include "menu.h" -#include <iostream> - -std::unordered_map<std::string, Menu::Menu_callback> Menu::function_lookup; void Menu::Read(std::istream &is) { json file = json::parse(is); for (auto &menu : file) { const json &items = menu["items"]; const Menu_function *mf = - new Menu_function(menu["name"], {items.begin(), items.end()}); + new Menu_function(*this, menu["name"], {items.begin(), items.end()}); function_lookup.insert({menu["code"], Menu_callback(mf)}); } } - -int Menu::Menu_function::call() const { - unsigned long choice; - while (true) { - std::cout << name << std::endl; - - for (auto i = 0ul; i < items.size(); i++) - std::cout << i << ". " << items[i].prompt << std::endl; - - while (true) { - std::cout << "Choose an option: "; - if (std::cin >> choice && choice < items.size()) { - std::cout << "Chosen: " << items[choice].prompt << "\n\n"; - int res = get_callback(items[choice].callback)(); - if (--res) return res; - break; - } else if (std::cin.eof()) { - std::cerr << "encountered end of input!\n"; - return std::numeric_limits<int>::max(); - } else { - std::cin.clear(); - std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); - } - std::cout << "Invalid option, please choose again!" << std::endl; - } - std::cout << std::endl; - } - - return 1; -}