stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | 4353db0fc0af665ffe8a735c52da61b641ab92c9 | 
| parent | 26a75511cae93934528c2c3e09c1307d20086552 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Fri, 17 Nov 2023 03:02:22 +0000 | 
stamen namespace, header only
* Put everything inside of stamen namespace
* Main functionality is provided in header only
* In order to use builtinDisplay you need to link against stamen library
* Reflect the changes in demo and README
* Improve code redability
| M | CMakeLists.txt | | | + - | 
| M | README.md | | | +++++++++ ---- | 
| M | demo/CMakeLists.txt | | | + | 
| M | demo/main.cpp | | | +++++ - | 
| M | include/stamen.h | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ---------------------- | 
| M | src/CMakeLists.txt | | | +++++++++ --- | 
| M | src/generate.cpp | | | + - | 
| M | src/stamen.cpp | | | ++++++ ----------------------------------------------------------- | 
8 files changed, 133 insertions(+), 105 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -3,7 +3,7 @@ 
          set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
        
        
          project(
              Stamen
              VERSION 0.0.8
              VERSION 0.0.9
              DESCRIPTION "Static menu generator"
              LANGUAGES CXX
          )
        
        diff --git a/ README.md b/ README.md
          @@ -68,13 +68,15 @@ 
          Please reference demo folder for relevant usage example.
        
        
          There are a few things needed before you begin.
          * Everything is contained in the `stamen namespace`
          * Panel and item codes must be one word. In addition they must be valid C++
          function names if static menu is to be build correctly.
          * Each free function must have `int name(void)` signature as prescribed by
          `Menu::callback_f`.
          * You must set a value of the static variable `const Menu::display_f
          Menu::display` to specify function used for displaying the menu. You can start
          by using a build in `Menu::builtinDisplay`.
          by using a build in `stamen::builtinDisplay`, just make sure to link stamen library
          while building your project.
          #### Dynamic menu
        
        
          @@ -84,6 +86,9 @@ 
          order to invoke the menu you need to add the following code to your C++
        
        
          program:
          ```
          // shorthand
          using stamen::Menu;
          // read the configuration
          Menu::read("path to config");
          
          @@ -118,6 +123,9 @@ 
          menu starting from that specific pane.
        
        
          #### Custom display function
          Please refer to the implementation of `stamen::builtinDisplay` to get a general
          idea of the direction.
          A display function should have `int name(const std::string&, const
          Menu::item_t[], std::size_t)` signature as prescribed by `Menu::display_f`. To
          get information about the specific item use `getPrompt()` and `getCallback()`
        
        
          @@ -129,9 +137,6 @@ 
          return type of int is intended to be used as a measure how many panels back
        
        
          should be backtracked after a free function terminates, but you can use in any
          way you see fit.
          Please refer to the implementation of `Menu::builtinDisplay` to get a general
          idea of the direction.
          ## Version History
          diff --git a/ demo/CMakeLists.txt b/ demo/CMakeLists.txt
          @@ -5,6 +5,7 @@ 
          add_executable(demo
        
        
          target_link_libraries(demo
              stamen
              stamen-display
          )
          ADD_CUSTOM_COMMAND(
        
        diff --git a/ demo/main.cpp b/ demo/main.cpp
@@ -2,7 +2,11 @@
#include "demo_menu.h"
          const Menu::display_f Menu::display = Menu::builtinDisplay;
          using stamen::Menu;
          // need to link against stamen library
          // in order to use stamen::BuiltinDisplay
          const Menu::display_f Menu::display = stamen::builtinDisplay;
          int operation1(void) {
            std::cout << "operation 1" << std::endl;
        
        diff --git a/ include/stamen.h b/ include/stamen.h
@@ -5,22 +5,23 @@
#include <format>
          #include <fstream>
          #include <iostream>
          #include <memory>
          #include <sstream>
          #include <string>
          #include <tuple>
          #include <unordered_map>
          #include <vector>
          namespace stamen {
          class Menu {
          public:
            typedef int (*callback_f)(void);
            Menu(const Menu &) = delete;
            Menu &operator=(const Menu &) = delete;
            struct private_ctor_t {};
          public:
            typedef int (*callback_f)(void);
            Menu(private_ctor_t, const std::string &code, const std::string &prompt)
                : Menu(code, prompt) {}
          
          @@ -54,44 +55,18 @@ 
          public:
        
        
              const callback_f callback = nullptr;
            };
            static void read(const std::string &s) {
              std::string line, delim, code, prompt;
              std::fstream fs(s);
              char tmp;
              lookup_t::iterator last = lookup.end();
              while (std::getline(fs, line)) {
                if (line.empty()) continue;
                std::istringstream ss(line);
                ss >> delim >> code;
                ss.ignore(1, ' '), std::getline(ss, prompt);
                if (delim == "+") {
                  const auto [iter, succ] = lookup.emplace(
                      std::piecewise_construct, std::forward_as_tuple(code),
                      std::forward_as_tuple(private_ctor_t{}, code, prompt));
                  last = iter;
                } else {
                  last->second.items.push_back({code, prompt});
                }
              }
            }
            typedef int (*display_f)(const std::string &, const item_t[], std::size_t);
            static const display_f display;
            static int start(const std::string &entry) { return getMenu(entry)(); }
            static void insert(const std::string &code, const callback_f callback) {
              lookup.emplace(std::piecewise_construct, std::forward_as_tuple(code),
                             std::forward_as_tuple(private_ctor_t{}, code, callback));
            }
            static void read(const std::string &s);
            static void insert(const std::string &code, const callback_f callback);
            static int start(const std::string &entry) { return getMenu(entry)(); }
            static void print(const std::string &entry) { print(entry, 1); }
            static void generateSource(std::ostream &os);
            static void generateInclude(std::ostream &os);
            typedef int (*display_f)(const std::string &, const item_t[], std::size_t);
            static const display_f display;
            static int builtinDisplay(const std::string &title, const item_t items[],
                                      std::size_t size);
            int operator()() const {
              return callback ? callback() : display(title, items.data(), items.size());
            }
        
        
          @@ -104,11 +79,15 @@ 
          private:
        
        
                : code(code), title(code), callback(callback) {}
            typedef std::unordered_map<std::string, Menu> lookup_t;
            static lookup_t lookup;
            static lookup_t &getLookup(void) {
              static lookup_t lookup;
              return lookup;
            }
            static void print(const std::string &entry, const int depth);
            static const Menu &getMenu(const std::string &code) {
              static lookup_t &lookup = getLookup();
              const auto it = lookup.find(code);
              if (it == lookup.end()) throw EMenu();
              return it->second;
        
        
          @@ -119,4 +98,90 @@ 
          private:
        
        
            std::vector<item_t> items;
          };
          inline void Menu::read(const std::string &s) {
            std::string line, delim, code, prompt;
            std::fstream fs(s);
            char tmp;
            lookup_t &lookup = getLookup();
            lookup_t::iterator last = lookup.end();
            while (std::getline(fs, line)) {
              if (line.empty()) continue;
              std::istringstream ss(line);
              ss >> delim >> code;
              ss.ignore(1, ' '), std::getline(ss, prompt);
              if (delim == "+") {
                const auto [iter, succ] =
                    lookup.emplace(std::piecewise_construct, std::forward_as_tuple(code),
                                   std::forward_as_tuple(private_ctor_t{}, code, prompt));
                last = iter;
              } else {
                last->second.items.push_back({code, prompt});
              }
            }
          }
          inline void Menu::insert(const std::string &code, const callback_f callback) {
            getLookup().emplace(std::piecewise_construct, std::forward_as_tuple(code),
                                std::forward_as_tuple(private_ctor_t{}, code, callback));
          }
          inline void Menu::generateInclude(std::ostream &os) {
            os << "#ifndef STAMEN_MENU_H\n";
            os << "#define STAMEN_MENU_H\n\n";
            os << "#include <stamen.h>\n\n";
            os << "namespace stamen {\n\n";
            for (const auto &[code, _] : getLookup()) {
              const Menu &menu = getMenu(code);
              if (menu.callback) continue;
              os << std::format("int {}(void);\n", menu.code);
            }
            os << "\n}\n";
            os << "#endif\n";
          }
          inline void Menu::generateSource(std::ostream &os) {
            os << "#include <stamen.h>\n";
            os << "#include \"shared.h\"\n";
            os << "\nnamespace stamen {\n";
            for (const auto &[code, _] : getLookup()) {
              const Menu &menu = getMenu(code);
              if (menu.callback) continue;
              os << std::format("int {}(void) {{\n", menu.code);
              os << std::format("\tstatic const Menu::item_t items[] = {{\n");
              for (const auto &item : menu.items) {
                os << std::format("\t\t{{{}, \"{}\"}},\n", item.code, item.prompt);
              }
              os << std::format("\t}};\n");
              os << std::format("\treturn Menu::display(\"{}\", items, "
                                "sizeof(items) / sizeof(Menu::item_t));\n",
                                menu.title);
              os << std::format("}}\n\n");
            }
            os << "\n}\n";
          }
          inline void Menu::print(const std::string &code, const int depth) {
            static lookup_t &lookup = getLookup();
            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.title, code);
            if (!menu.callback) {
              for (const auto &item : menu.items) {
                std::cout << std::format("{}{} ({})\n", std::string(depth << 1, ' '),
                                         item.prompt, item.code);
                menu.print(item.code, depth + 1);
              }
            }
          }
          int builtinDisplay(const std::string &title, const Menu::item_t items[],
                             std::size_t size);
          } // namespace stamen
          #endif
        
        diff --git a/ src/CMakeLists.txt b/ src/CMakeLists.txt
@@ -1,9 +1,15 @@
add_library(stamen
          add_library(stamen INTERFACE)
          target_include_directories(stamen
              INTERFACE ../include
          )
          add_library(stamen-display
              stamen.cpp
          )
          target_include_directories(stamen
              PUBLIC ../include
          target_link_libraries(stamen-display
              PRIVATE stamen
          )
          add_executable(stamen-generate generate.cpp)
        
        diff --git a/ src/generate.cpp b/ src/generate.cpp
@@ -1,7 +1,7 @@
#include "stamen.h"
          #include <string>
          const Menu::display_f Menu::display = Menu::builtinDisplay;
          using namespace stamen;
          int main(const int argc, const char *argv[]) {
            if (argc != 2) {
        
        diff --git a/ src/stamen.cpp b/ src/stamen.cpp
@@ -1,16 +1,13 @@
#include "../include/stamen.h"
          #include <cmath>
          #include <format>
          #include <fstream>
          #include <iostream>
          #include <ostream>
          #include <stack>
          #include <unordered_set>
          std::unordered_map<std::string, Menu> Menu::lookup;
          namespace stamen {
          int Menu::builtinDisplay(const std::string &title, const item_t items[],
                                   std::size_t size) {
          int builtinDisplay(const std::string &title, const Menu::item_t items[],
                             std::size_t size) {
            int choice;
            const int digits = std::log10(size) + 1;
            while (true) {
        
        
          @@ -27,12 +24,12 @@ 
          int Menu::builtinDisplay(const std::string &title, const item_t items[],
        
        
                    return 1;
                  }
                  const item_t &chosen = items[choice];
                  const Menu::item_t &chosen = items[choice];
                  std::cout << std::format("Choice: {}\n\n", chosen.getPrompt());
                  const int res = chosen();
                  if (res > 1) return res - 1;
                  else break;
                  break;
                } else if (std::cin.eof()) {
                  std::cerr << "encountered end of input!\n";
                  return std::numeric_limits<int>::max();
        
        
          @@ -48,54 +45,4 @@ 
          int Menu::builtinDisplay(const std::string &title, const item_t items[],
        
        
            return 1;
          }
          void Menu::generateInclude(std::ostream &os) {
            os << "#ifndef STAMEN_MENU_H\n";
            os << "#define STAMEN_MENU_H\n\n";
            os << "#include <stamen.h>\n\n";
            os << "namespace stamen {\n\n";
            for (const auto &[code, _] : lookup) {
              const Menu &menu = getMenu(code);
              if (menu.callback) continue;
              os << std::format("int {}(void);\n", menu.code);
            }
            os << "\n}\n";
            os << "#endif\n";
          }
          void Menu::generateSource(std::ostream &os) {
            os << "#include <stamen.h>\n";
            os << "#include \"shared.h\"\n";
            os << "\nnamespace stamen {\n";
            for (const auto &[code, _] : lookup) {
              const Menu &menu = getMenu(code);
              if (menu.callback) continue;
              os << std::format("int {}(void) {{\n", menu.code);
              os << std::format("\tstatic const Menu::item_t items[] = {{\n");
              for (const auto &item : menu.items) {
                os << std::format("\t\t{{{}, \"{}\"}},\n", item.code, item.prompt);
              }
              os << std::format("\t}};\n");
              os << std::format("\treturn Menu::display(\"{}\", items, "
                                "sizeof(items) / sizeof(Menu::item_t));\n",
                                menu.title);
              os << std::format("}}\n\n");
            }
            os << "\n}\n";
          }
          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.title, code);
            if (!menu.callback) {
              for (const auto &item : menu.items) {
                std::cout << std::format("{}{}({})\n", std::string(depth << 1, ' '),
                                         item.prompt, item.code);
                menu.print(item.code, depth + 1);
              }
            }
          }
          } // namespace stamen