stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | cff8002912e2fbd52d9904edabc930a9aa077fe5 | 
| parent | 7a6fd17c3d31fd728820bd3aa259894d7efbedb3 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Thu, 9 Feb 2023 21:33:28 +0100 | 
Ability to create multiple independent menus
- Menu is no longer static
- Extract display logic to separate cpp file
| M | .ccls | | | + - | 
| M | Makefile | | | + - | 
| M | include/menu.h | | | +++++++++++++++++++++++++++ ----------------- | 
| A | src/display.cpp | | | ++++++++++++++++++++++++++++++++ | 
| M | src/main.cpp | | | +++++ ------- | 
| M | src/menu.cpp | | | + ---------------------------------- | 
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;
          }