stamenStatic Menu Generator | 
          
| git clone git://git.dimitrijedobrota.com/stamen.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | cd12b2fea38be76fe6bd2c607598c785d979c25e | 
| parent | 199eba24714cb83271e636100315011def07ded9 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Fri, 14 Jun 2024 09:33:39 +0200 | 
Split menu in its own set of headers
| M | CMakeLists.txt | | | ++++ -------- | 
| M | demo/dynamic.cpp | | | ++++++++ ------- | 
| M | include/menu.h | | | ++++++++++ ------------------------------------------------------------------------ | 
| A | include/menu.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| M | include/stamen.h | | | + ----- | 
| M | include/stamen.hpp | | | ---- | 
| M | src/CMakeLists.txt | | | + - | 
| M | src/c_bindings.cpp | | | +++++++++++ --------- | 
| M | src/generate.cpp | | | +++++ ----- | 
| M | src/menu.cpp | | | +++++++++++++++++++++++++++++++++++++ ----------------------------------- | 
| M | src/stamen.cpp | | | ---------- | 
11 files changed, 154 insertions(+), 165 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -70,14 +70,10 @@ 
          install(
        
        
          )
          install(
          	EXPORT
          		stamenTargets
          	NAMESPACE
          		"stamen::"
          	DESTINATION
          		${INSTALL_CMAKEDIR}
          	COMPONENT
          		dev
          	EXPORT stamenTargets
          	NAMESPACE "stamen::"
          	DESTINATION ${INSTALL_CMAKEDIR}
          	COMPONENT dev
          )
          include(CMakePackageConfigHelpers)
        
        diff --git a/ demo/dynamic.cpp b/ demo/dynamic.cpp
@@ -1,6 +1,7 @@
#include "stamen.hpp"
          #include <iostream>
          #include "menu.hpp"
          int finish(int) { exit(1); }
          int operation1(int) {
        
        
          @@ -20,15 +21,15 @@ 
          int operation3(int) {
        
        
          int main() {
              // read the configuration
              stamen::read("./bin/demo_menu.conf");
              stamen::menu::read("./bin/demo_menu.conf");
              // register free functions
              stamen::insert("finish", finish);
              stamen::insert("operation1", operation1);
              stamen::insert("operation2", operation2);
              stamen::insert("operation3", operation3);
              stamen::menu::insert("finish", finish);
              stamen::menu::insert("operation1", operation1);
              stamen::menu::insert("operation2", operation2);
              stamen::menu::insert("operation3", operation3);
              // start the menu on specific panel
              stamen::dynamic("menu_main", stamen::builtin_display);
              stamen::menu::dynamic("menu_main", stamen::builtin_display);
              return 0;
          }
        
        diff --git a/ include/menu.h b/ include/menu.h
@@ -1,91 +1,20 @@
#ifndef STAMEN_MENU_H
          #define STAMEN_MENU_H
          #include "stamen.hpp"
          #include <cstring>
          #include <iostream>
          #include <string>
          #include <unordered_map>
          #include <vector>
          #include "stamen.h"
          #ifdef __cplusplus
          extern "C" {
          namespace stamen {
          #endif
          class Menu {
              struct private_ctor_t {};
            public:
              // Tag type dispatch
              Menu(private_ctor_t, const std::string &code, const std::string &prompt)
                  : Menu(code, prompt) {}
              Menu(const Menu &) = delete;
              Menu &operator=(const Menu &) = delete;
              Menu(Menu &&) = delete;
              Menu &operator=(Menu &&) = delete;
              ~Menu() noexcept = default;
              static int dynamic(const std::string &code, display_f display) {
                  Menu::display_stub_default = code;
                  Menu::display = display;
                  return display_stub(-1);
              };
              static void read(const std::string &s);
              static void insert(const std::string &s, callback_f callback) {
                  free_lookup.emplace(s, callback);
              }
              const std::string &getCode() const { return code; }
              const std::string &getTitle() const { return title; }
              const item_t *getItemv() const { return entries.items.data(); }
              std::size_t getSize() const { return entries.items.size(); }
              callback_f getCallback(std::size_t idx) const;
              const std::string &getCode(std::size_t idx) const;
              const std::string &getPrompt(std::size_t idx) const;
              static std::unordered_map<std::string, Menu> menu_lookup;
            private:
              Menu(std::string code, std::string prompt)
                  : code(std::move(code)), title(std::move(prompt)) {}
              struct entries_t {
                  entries_t() = default;
                  entries_t(const entries_t &) = delete;
                  entries_t &operator=(const entries_t &) = delete;
                  entries_t(entries_t &&) = delete;
                  entries_t &operator=(entries_t &&) = delete;
                  ~entries_t() noexcept {
                      for (const auto [_, prompt] : items) {
                          delete[] prompt;
                      }
                  }
                  void insert(const std::string &code, const std::string &prompt,
                              callback_f callback = display_stub);
                  struct code_t {
                      std::string code;
                      std::string prompt;
                  };
                  std::vector<code_t> codes;
                  std::vector<item_t> items;
              };
              const std::string code, title;
              entries_t entries;
              static int display_stub(int idx);
              static std::unordered_map<std::string, callback_f> free_lookup;
              static std::string display_stub_default;
              static display_f display;
          };
          void stamen_menu_read(const char *filename);
          void stamen_menu_insert(const char *code, stamen_callback_f callback);
          int stamen_menu_dynamic(const char *code, stamen_display_f display);
          #ifdef __cplusplus
          } // namespace stamen
          } // extern "C"
          #endif
          #endif
        
        diff --git a/ include/menu.hpp b/ include/menu.hpp
@@ -0,0 +1,77 @@
#ifndef STAMEN_MENU_HPP
          #define STAMEN_MENU_HPP
          #include "stamen.hpp"
          #include <cstring>
          #include <iostream>
          #include <string>
          #include <unordered_map>
          #include <vector>
          namespace stamen {
          namespace menu {
          class menu_t;
          extern std::unordered_map<std::string, callback_f> free_lookup;
          extern std::unordered_map<std::string, menu_t> menu_lookup;
          extern std::string display_stub_default;
          extern display_f display;
          void read(const char *filename);
          void insert(const char *code, callback_f callback);
          int dynamic(const char *code, display_f display);
          int display_stub(int idx);
          class menu_t {
              struct private_ctor_t {};
              friend void read(const char *filename);
            public:
              // Tag type dispatch
              menu_t(private_ctor_t, const std::string &code, const std::string &prompt)
                  : menu_t(code, prompt) {}
              ~menu_t() noexcept {
                  for (const auto [_, prompt] : items) {
                      delete[] prompt;
                  }
              }
              const std::string &getCode() const { return code; }
              const std::string &getTitle() const { return title; }
              const item_t *getItemv() const { return items.data(); }
              std::size_t getSize() const { return items.size(); }
              auto getCallback(std::size_t idx) const { return items[idx].callback; }
              const auto &getCode(std::size_t idx) const { return codes[idx].code; }
              const auto &getPrompt(std::size_t idx) const { return codes[idx].prompt; }
            private:
              menu_t(std::string code, std::string prompt)
                  : code(std::move(code)), title(std::move(prompt)) {}
              menu_t(const menu_t &) = delete;
              menu_t &operator=(const menu_t &) = delete;
              menu_t(menu_t &&) = delete;
              menu_t &operator=(menu_t &&) = delete;
              void insert(const std::string &code, const std::string &prompt,
                          callback_f callback = display_stub);
              struct code_t {
                  std::string code;
                  std::string prompt;
              };
              const std::string code, title;
              std::vector<code_t> codes;
              std::vector<item_t> items;
          };
          } // namespace menu
          } // namespace stamen
          #endif
        
        diff --git a/ include/stamen.h b/ include/stamen.h
          @@ -18,10 +18,6 @@ 
          typedef int (*stamen_display_f)(const char *, const stamen_item_t[], int);
        
        
          #if !defined __cplusplus || defined WITH_C_BINDINGS
          void stamen_read(const char *filename);
          void stamen_insert(const char *code, stamen_callback_f callback);
          int stamen_dynamic(const char *code, stamen_display_f display);
          int stamen_builtin_display(const char *title, const stamen_item_t itemv[],
                                     int size);
          
          @@ -32,4 +28,4 @@ 
          int stamen_builtin_display(const char *title, const stamen_item_t itemv[],
        
        
          } // extern "C"
          #endif
          #endif // STAMEN_STAMEN_H
          #endif
        
        diff --git a/ include/stamen.hpp b/ include/stamen.hpp
          @@ -9,10 +9,6 @@ 
          using callback_f = stamen_callback_f;
        
        
          using display_f = stamen_display_f;
          using item_t = stamen_item_t;
          void read(const char *filename);
          void insert(const char *code, callback_f callback);
          int dynamic(const char *code, display_f display);
          int builtin_display(const char *title, const item_t itemv[], int size);
          } // namespace stamen
        
        diff --git a/ src/CMakeLists.txt b/ src/CMakeLists.txt
          @@ -7,7 +7,7 @@ 
          set_target_properties(stamen PROPERTIES
        
        
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              DEBUG_POSTFIX "d"
              PUBLIC_HEADER "include/stamen.h;include/stamen.hpp"
              PUBLIC_HEADER "include/stamen.h;include/stamen.hpp;include/menu.h;include/menu.hpp"
              MACOSX_RPATH ON
              WINDOWS_EXPORT_ALL_SYMBOLS ON
          )
        
        diff --git a/ src/c_bindings.cpp b/ src/c_bindings.cpp
@@ -1,21 +1,23 @@
#include "stamen.h"
          #include "menu.hpp"
          #include "stamen.hpp"
          namespace stamen {
          void stamen_read(const char *filename) { return read(filename); }
          int stamen_builtin_display(const char *title, const stamen_item_t itemv[], int size) {
              return builtin_display(title, itemv, size);
          }
          namespace menu {
          void stamen_menu_read(const char *filename) { return read(filename); }
          void stamen_insert(const char *code, stamen_callback_f callback) {
          void stamen_menu_insert(const char *code, stamen_callback_f callback) {
              return insert(code, callback);
          }
          int stamen_dynamic(const char *code, stamen_display_f display) {
          int stamen_menu_dynamic(const char *code, stamen_display_f display) {
              return dynamic(code, display);
          }
          int stamen_builtin_display(const char *title, const stamen_item_t itemv[],
                                     int size) {
              return builtin_display(title, itemv, size);
          }
          } // namespace menu
          } // namespace stamen
        
        diff --git a/ src/generate.cpp b/ src/generate.cpp
@@ -1,7 +1,7 @@
#include <args/args.hpp>
          #include "menu.h"
          #include "stamen.h"
          #include "menu.hpp"
          #include "stamen.hpp"
          #include <format>
          #include <fstream>
        
        
          @@ -31,7 +31,7 @@ 
          void generateInclude(std::ostream &os) {
        
        
              generateIncludeHeaders(os);
              for (const auto &[code, menu] : stamen::Menu::menu_lookup) {
              for (const auto &[code, menu] : stamen::menu::menu_lookup) {
                  os << std::format("int {}(int);\n", menu.getCode());
              }
          
          @@ -46,7 +46,7 @@ 
          void generateSource(std::ostream &os) {
        
        
              if (opt.cpp) os << "const stamen::item_t itemv[], int size);\n\n";
              else os << "const stamen_item_t itemv[], int size);\n\n";
              for (const auto &[code, menu] : stamen::Menu::menu_lookup) {
              for (const auto &[code, menu] : stamen::menu::menu_lookup) {
                  os << std::format("int {}(int) {{\n", menu.getCode());
                  if (opt.cpp) os << "\tstatic const stamen::item_t items[] = ";
        
        
          @@ -116,7 +116,7 @@ 
          int main(int argc, char *argv[]) {
        
        
              }
              const auto &config = opt.config;
              stamen::Menu::read(config);
              stamen::menu::read(config.c_str());
              std::string::size_type pos = opt.config.rfind('.');
              std::string base =
        
        diff --git a/ src/menu.cpp b/ src/menu.cpp
@@ -1,4 +1,4 @@
#include "menu.h"
          #include "menu.hpp"
          #include <deque>
          #include <format>
        
        @@ -9,36 +9,16 @@
#include <utility>
          namespace stamen {
          namespace menu {
          std::unordered_map<std::string, Menu> Menu::menu_lookup;
          std::unordered_map<std::string, callback_f> Menu::free_lookup;
          std::string Menu::display_stub_default;
          display_f Menu::display;
          std::unordered_map<std::string, menu_t> menu_lookup;
          std::unordered_map<std::string, callback_f> free_lookup;
          std::string display_stub_default;
          display_f display;
          void Menu::entries_t::insert(const std::string &code,
                                       const std::string &prompt, callback_f callback) {
              char *buffer = new char[prompt.size() + 1];
              strcpy(buffer, prompt.c_str());
              items.emplace_back(callback, buffer);
              codes.emplace_back(code, prompt);
          }
          const std::string &Menu::getCode(std::size_t idx) const {
              return entries.codes[idx].code;
          }
          const std::string &Menu::getPrompt(std::size_t idx) const {
              return entries.codes[idx].prompt;
          }
          callback_f Menu::getCallback(std::size_t idx) const {
              return entries.items[idx].callback;
          }
          void Menu::read(const std::string &s) {
          void read(const char *filename) {
              std::string line, delim, code, prompt;
              std::fstream fs(s);
              std::fstream fs(filename);
              auto last = menu_lookup.end();
              while (std::getline(fs, line)) {
        
        
          @@ -48,28 +28,40 @@ 
          void Menu::read(const std::string &s) {
        
        
                  ss >> delim >> code >> std::ws;
                  std::getline(ss, prompt);
                  if (delim != "+") last->second.entries.insert(code, prompt);
                  if (delim != "+") last->second.insert(code, prompt);
                  else {
                      const auto [iter, succ] = menu_lookup.emplace(
                          std::piecewise_construct, std::forward_as_tuple(code),
                          std::forward_as_tuple(private_ctor_t{}, code, prompt));
                          std::forward_as_tuple(menu_t::private_ctor_t{}, code, prompt));
                      last = iter;
                  }
              }
          }
          int Menu::display_stub(int idx) {
              static std::deque<const Menu *> st;
          void insert(const char *code, callback_f callback) {
              free_lookup.emplace(code, callback);
          }
          int dynamic(const char *code, display_f display) {
              menu::display_stub_default = code;
              menu::display = display;
              return display_stub(-1);
          }
          int display_stub(int idx) {
              static std::deque<const menu_t *> st;
              const std::string &code =
                  !st.empty() ? st.back()->getCode(idx) : display_stub_default;
              const auto ml_it = menu_lookup.find(code);
              if (ml_it != menu_lookup.end()) {
                  const Menu &menu = ml_it->second;
                  st.push_back(&menu);
                  int ret = display(menu.title.c_str(), menu.getItemv(), menu.getSize());
                  const auto &m = ml_it->second;
                  st.push_back(&m);
                  int ret = display(m.getTitle().c_str(), m.getItemv(), m.getSize());
                  st.pop_back();
                  return ret;
              }
          
          @@ -80,4 +72,14 @@ 
          int Menu::display_stub(int idx) {
        
        
              return 1;
          }
          void menu_t::insert(const std::string &code, const std::string &prompt,
                              callback_f callback) {
              char *buffer = new char[prompt.size() + 1];
              strcpy(buffer, prompt.c_str());
              items.emplace_back(callback, buffer);
              codes.emplace_back(code, prompt);
          }
          } // namespace menu
          } // namespace stamen
        
        diff --git a/ src/stamen.cpp b/ src/stamen.cpp
@@ -9,16 +9,6 @@
namespace stamen {
          void read(const char *filename) { Menu::read(filename); }
          void insert(const char *code, callback_f callback) {
              Menu::insert(code, callback);
          }
          int dynamic(const char *code, display_f display) {
              return Menu::dynamic(code, display);
          }
          int builtin_display(const char *title, const item_t itemv[], int size) {
              const auto items = std::span(itemv, size_t(size));
              const size_t dgts = size_t(std::log10(size)) + 1;