commit b833ffadb09c2e07d744ac2e30ecb0562d8b89be
parent 1d14de3dcbe573ba1a156facdc0d477558f3c97e
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Thu, 9 Nov 2023 18:53:33 +0000
Refactoring
* Back to static menu interface
* Greatly simplified the implementation without loss of functionality
Diffstat:
6 files changed, 97 insertions(+), 128 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(
Menu
- VERSION 0.0.2
+ VERSION 0.0.3
DESCRIPTION "Experimentation with dinamic menus"
LANGUAGES CXX
)
diff --git a/include/menu.h b/include/menu.h
@@ -10,46 +10,16 @@
using json = nlohmann::json;
-class EMenu_callback : std::exception {
- virtual const char *what() const noexcept override {
- return "Unknown callback function or menu method";
- }
-};
-
-class EMenu_call : std::exception {
- virtual const char *what() const noexcept override {
- return "Trying a callback that doesn't exist";
- }
-};
-
class Menu {
public:
typedef int (*callback_f)(void);
- Menu(const std::string &s) {
- for (const auto &menu : json::parse(std::fstream(s))) {
- const json &items = menu["items"];
- const function_t *mf =
- new function_t(*this, menu["name"], {items.begin(), items.end()});
- lookup.insert({menu["code"], callback_t(mf)});
+ class EMenu : std::exception {
+ virtual const char *what() const noexcept override {
+ return "Trying to access an unknown menu";
}
- }
-
- void print(const std::string code = "main", const int depth = 1) const;
- void operator()() const { get_callback("main")(); }
-
- struct record_t {
- const std::string code;
- const callback_f callback;
};
- void insert(const record_t &record) {
- lookup.insert({record.code, record.callback});
- }
- void insert(const std::vector<record_t> &records) {
- for (const auto &record : records) { insert(record); }
- }
-
private:
struct item_t {
const std::string prompt;
@@ -60,40 +30,40 @@ private:
item_t(const json &j) : item_t(j["prompt"], j["callback"]) {}
};
- struct function_t {
- friend class Menu;
- function_t(const Menu &m, const std::string &n,
- const std::vector<item_t> &i)
- : menu(m), name(n), items(i) {}
+ Menu(const json &json_data)
+ : name(json_data["name"]), code(json_data["code"]),
+ items({json_data["items"].begin(), json_data["items"].end()}) {}
- int display() const;
+ Menu(const std::string code, const callback_f callback)
+ : name(code), code(code), items(), callback(callback) {}
- private:
- const Menu &menu;
- const std::string name;
- const std::vector<item_t> items;
- };
+public:
+ static void read(const std::string &s) {
+ for (const auto &json_data : json::parse(std::fstream(s))) {
+ lookup.insert({json_data["code"], Menu(json_data)});
+ }
+ }
- struct callback_t {
- const function_t *menu_func = nullptr;
- const callback_f func = nullptr;
+ static int start() { return getMenu("main")(); }
+ static void insert(const std::string code, const callback_f callback) {
+ lookup.insert({code, Menu(code, callback)});
+ }
- callback_t(const callback_f f) : func(f) {}
- callback_t(const function_t *f) : menu_func(f) {}
+ static void print(const std::string code = "main", const int depth = 1);
+ int operator()() const;
- int operator()() const {
- if (!func && !menu_func) throw EMenu_callback();
- return func ? func() : menu_func->display();
- }
- };
+private:
+ static std::unordered_map<std::string, Menu> lookup;
- const callback_t &get_callback(const std::string &s) const {
- const auto it = lookup.find(s);
- if (it == lookup.end()) throw EMenu_call();
+ static const Menu &getMenu(const std::string &code) {
+ const auto it = lookup.find(code);
+ if (it == lookup.end()) throw EMenu();
return it->second;
}
- std::unordered_map<std::string, callback_t> lookup;
+ const std::string name, code;
+ const std::vector<item_t> items;
+ const callback_f callback = nullptr;
};
#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
@@ -1,5 +1,5 @@
add_library(menu
- display.cpp
+ menu.cpp
)
target_include_directories(menu
diff --git a/src/display.cpp b/src/display.cpp
@@ -1,60 +0,0 @@
-#include "menu.h"
-#include <cmath>
-#include <format>
-#include <iostream>
-
-int Menu::function_t::display() const {
- int choice;
-
- const int n = items.size(), digits = std::log10(n) + 1;
- while (true) {
- std::cout << std::format("{}:\n", name);
- for (auto i = 0ul; i < n; i++) {
- std::cout << std::format(" {:{}}. {}\n", i, digits, items[i].prompt);
- }
-
- while (true) {
- std::cout << "Choose an option: ";
- if (std::cin >> choice && choice >= -1 && choice < n) {
- if (choice == -1) {
- std::cout << "choice: back\n";
- return 1;
- }
-
- std::cout << std::format("Choice: {}\n\n", items[choice].prompt);
- 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!\n";
- }
- std::cout << std::endl;
- }
-
- return 1;
-}
-
-void Menu::print(const std::string code, const int depth) const {
- const auto it = lookup.find(code);
- if (it == lookup.end()) return;
- const callback_t &cb = it->second;
-
- if (depth == 1) {
- std::cout << std::format("{}({})\n", cb.menu_func->name, code);
- }
-
- if (cb.menu_func) {
- for (const auto &item : cb.menu_func->items) {
- std::cout << std::format("{}{}({})\n", std::string(depth << 1, ' '),
- item.prompt, item.callback);
- if (cb.menu_func) cb.menu_func->menu.print(item.callback, depth + 1);
- }
- }
-}
diff --git a/src/main.cpp b/src/main.cpp
@@ -19,13 +19,13 @@ int finish(void) {
}
int main(void) {
- Menu menu("menu.json");
- menu.insert({
- {"algorithms", algorithms},
- { "settings", settings},
- { "finish", finish}
- });
- // menu();
- menu.print();
+ Menu::read("menu.json");
+ Menu::insert("algorithms", algorithms);
+ Menu::insert("settings", settings);
+ Menu::insert("finish", finish);
+
+ Menu::print("main");
+ Menu::start();
+
return 0;
}
diff --git a/src/menu.cpp b/src/menu.cpp
@@ -1,2 +1,61 @@
#include "menu.h"
+#include <cmath>
+#include <format>
+#include <iostream>
+std::unordered_map<std::string, Menu> Menu::lookup;
+
+int Menu::operator()() const {
+ if (callback) return callback();
+
+ int choice;
+ const int n = items.size(), digits = std::log10(n) + 1;
+ while (true) {
+ std::cout << std::format("{}:\n", name);
+ for (auto i = 0ul; i < n; i++) {
+ std::cout << std::format(" {:{}}. {}\n", i, digits, items[i].prompt);
+ }
+
+ while (true) {
+ std::cout << "Choose an option: ";
+ if (std::cin >> choice && choice >= -1 && choice < n) {
+ if (choice == -1) {
+ std::cout << "choice: back\n";
+ return 1;
+ }
+
+ std::cout << std::format("Choice: {}\n\n", items[choice].prompt);
+ int res = getMenu(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!\n";
+ }
+ std::cout << std::endl;
+ }
+
+ return 1;
+}
+
+void Menu::print(const std::string code, const int depth) {
+ const auto it = lookup.find(code);
+ if (it == lookup.end()) return;
+ const Menu &menu = it->second;
+
+ if (depth == 1) { std::cout << std::format("{}({})\n", menu.name, code); }
+
+ if (!menu.callback) {
+ for (const auto &item : menu.items) {
+ std::cout << std::format("{}{}({})\n", std::string(depth << 1, ' '),
+ item.prompt, item.callback);
+ menu.print(item.callback, depth + 1);
+ }
+ }
+}