stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | 260569531d18b4c35adeaa7b53c866ebea1e2d28 | 
| parent | 0ab8d3b1b0cae32d598a861f4fa04f95b348d54c | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Tue, 7 Nov 2023 01:37:21 +0000 | 
Refactoring
* Better initializastion of function lookup
* Consturctor now reads configuration directly
* More consistent menu display
* Remove menu.cpp
* Renaming
* Start menu with operator()
| M | CMakeLists.txt | | | + - | 
| M | include/menu.h | | | +++++++++++++++++++++++++++++++++++++++++ ------------------------------- | 
| M | src/CMakeLists.txt | | | + ----- | 
| M | src/display.cpp | | | +++++++++++++++ --------- | 
| M | src/main.cpp | | | ++++++++ ---- | 
| M | src/menu.cpp | | | --------- | 
6 files changed, 66 insertions(+), 59 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -3,7 +3,7 @@ 
          set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
        
        
          project(
              Menu
              VERSION 0.0.1
              VERSION 0.0.2
              DESCRIPTION "Experimentation with dinamic menus"
              LANGUAGES CXX
          )
        
        diff --git a/ include/menu.h b/ include/menu.h
@@ -9,7 +9,6 @@
#include <vector>
          using json = nlohmann::json;
          using std::string, std::vector;
          class EMenu_callback : std::exception {
            virtual const char *what() const noexcept override {
        
        
          @@ -25,49 +24,60 @@ 
          class EMenu_call : std::exception {
        
        
          class Menu {
          public:
            typedef int (*Menu_f)(void);
            Menu() : function_lookup() {}
            Menu(const string &s) : Menu() {
              std::fstream f(s);
              Read(f);
            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)});
              }
            }
            void Start() const { get_callback("main")(); }
            void operator()() const { get_callback("main")(); }
            void Read(std::istream &is);
            Menu &Register(const string &s, Menu_f f) {
              function_lookup.insert({s, f});
              return *this;
            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 Menu_item {
              const string prompt;
              const string callback;
            struct item_t {
              const std::string prompt;
              const std::string callback;
              Menu_item(const string &p, const string &c) : prompt(p), callback(c) {}
              Menu_item(const json &j) : Menu_item(j["prompt"], j["callback"]) {}
              item_t(const std::string &p, const std::string &c)
                  : prompt(p), callback(c) {}
              item_t(const json &j) : item_t(j["prompt"], j["callback"]) {}
            };
            struct Menu_function {
              Menu_function(const Menu &m, const string &n, const vector<Menu_item> &i)
            struct function_t {
              function_t(const Menu &m, const std::string &n,
                         const std::vector<item_t> &i)
                  : menu(m), name(n), items(i) {}
              int display() const;
            private:
              const Menu &menu;
              const string name;
              const vector<Menu_item> items;
              const std::string name;
              const std::vector<item_t> items;
            };
            struct Menu_callback {
              const Menu_f func = nullptr;
              const Menu_function *menu_func = nullptr;
            struct callback_t {
              const function_t *menu_func = nullptr;
              const callback_f func = nullptr;
              Menu_callback(const Menu_f f) : func(f) {}
              Menu_callback(const Menu_function *f) : menu_func(f) {}
              callback_t(const callback_f f) : func(f) {}
              callback_t(const function_t *f) : menu_func(f) {}
              int operator()() const {
                if (!func && !menu_func) throw EMenu_callback();
        
        
          @@ -75,13 +85,13 @@ 
          private:
        
        
              }
            };
            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;
            const callback_t &get_callback(const std::string &s) const {
              const auto it = lookup.find(s);
              if (it == lookup.end()) throw EMenu_call();
              return it->second;
            }
            std::unordered_map<string, Menu_callback> function_lookup;
            std::unordered_map<std::string, callback_t> lookup;
          };
          #endif
        
        diff --git a/ src/CMakeLists.txt b/ src/CMakeLists.txt
@@ -1,5 +1,4 @@
add_library(menu
              menu.cpp
              display.cpp
          )
          
          @@ -7,10 +6,7 @@ 
          target_include_directories(menu
        
        
              PUBLIC ../include
          )
          add_executable(demo
              main.cpp
          )
          add_executable(demo main.cpp)
          target_link_libraries(demo
              PRIVATE menu
        
        diff --git a/ src/display.cpp b/ src/display.cpp
@@ -1,24 +1,30 @@
#include "menu.h"
          #include <cmath>
          #include <format>
          #include <iostream>
          int Menu::Menu_function::display() const {
          int Menu::function_t::display() const {
            int choice;
            while (true) {
              std::cout << name << std::endl;
              for (auto i = 0ul; i < items.size(); i++)
                std::cout << i << ". " << items[i].prompt << std::endl;
            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 < (int)items.size()) {
                if (std::cin >> choice && choice >= -1 && choice < n) {
                  if (choice == -1) {
                    std::cout << "Back" << items[choice].prompt << "\n";
                    std::cout << "choice: back\n";
                    return 1;
                  }
                  std::cout << "Chosen: " << items[choice].prompt << "\n\n";
                  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";
        
        
          @@ -27,7 +33,7 @@ 
          int Menu::Menu_function::display() const {
        
        
                  std::cin.clear();
                  std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                }
                std::cout << "Invalid option, please choose again!" << std::endl;
                std::cout << "Invalid option, please choose again!\n";
              }
              std::cout << std::endl;
            }
        
        diff --git a/ src/main.cpp b/ src/main.cpp
@@ -3,11 +3,13 @@
int algorithms(void) {
            std::cout << "algorithms" << std::endl;
            std::cout << "nothing to do here" << std::endl;
            return 1;
          }
          int settings(void) {
            std::cout << "settings" << std::endl;
            std::cout << "nothing to do here" << std::endl;
            return 1;
          }
          
          @@ -18,9 +20,11 @@ 
          int finish(void) {
        
        
          int main(void) {
            Menu menu("menu.json");
            menu.Register("algorithms", algorithms)
                .Register("settings", settings)
                .Register("finish", finish);
            menu.Start();
            menu.insert({
                {"algorithms", algorithms},
                {  "settings",   settings},
                {    "finish",     finish}
            });
            menu();
            return 0;
          }
        
        diff --git a/ src/menu.cpp b/ src/menu.cpp
@@ -1,11 +1,2 @@
#include "menu.h"
          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(*this, menu["name"], {items.begin(), items.end()});
              function_lookup.insert({menu["code"], Menu_callback(mf)});
            }
          }