stamen

Stamen - static menu generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE

commit 69b2fecb3c6afe1358abb309dd8a3a95cdf417e7
parent 8af1469e5448a18f63b83bf7b144d18d87940f5b
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Thu, 23 Nov 2023 19:34:45 +0000

Consistency Improvement

* Move generation code to generate.cpp
* Improve exception safety
* Improve decomposition

Diffstat:
MCMakeLists.txt | 2+-
Minclude/stamen.h | 88++++++++++++++++++++-----------------------------------------------------------
Msrc/generate.cpp | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 78 insertions(+), 69 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Stamen - VERSION 0.0.10 + VERSION 0.0.11 DESCRIPTION "Static menu generator" LANGUAGES CXX ) diff --git a/include/stamen.h b/include/stamen.h @@ -15,6 +15,7 @@ namespace stamen { class Menu { public: + friend class Generator; typedef int (*callback_f)(void); Menu(const Menu &) = delete; @@ -22,35 +23,27 @@ public: struct private_ctor_t {}; + // Tag type dispatch Menu(private_ctor_t, const std::string &code, const std::string &prompt) : Menu(code, prompt) {} Menu(private_ctor_t, const std::string &code, const callback_f callback) : Menu(code, callback) {} - class EMenu : std::exception { - virtual const char *what() const noexcept override { - return "Trying to access an unknown menu"; - } - }; - struct item_t { - friend class Menu; - item_t(const callback_f func, const std::string &prompt) : callback(func), prompt(prompt) {} + item_t(const std::string &code, const std::string &prompt) + : code(code), prompt(prompt) {} + + const std::string getCode(void) const { return code; } const std::string getPrompt(void) const { return prompt; } const callback_f getCallback(void) const { return callback; } - int operator()(void) const { - return callback ? callback() : getMenu(code)(); - } + int operator()(void) const { return callback ? callback() : start(code); } private: - item_t(const std::string &code, const std::string &prompt) - : code(code), prompt(prompt) {} - const std::string prompt, code; const callback_f callback = nullptr; }; @@ -61,11 +54,12 @@ public: 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); + static int start(const std::string &entry) { + const Menu *menu = getMenu(entry); + if (!menu) return 1; + return menu->operator()(); + } int operator()() const { return callback ? callback() : display(title, items.data(), items.size()); @@ -86,11 +80,11 @@ private: static void print(const std::string &entry, const int depth); - static const Menu &getMenu(const std::string &code) { + 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; + if (it == lookup.end()) return nullptr; + return &it->second; } const std::string code, title; @@ -126,55 +120,17 @@ inline void Menu::insert(const std::string &code, const callback_f callback) { 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; + const Menu *menu = getMenu(code); + if (!menu) return; - if (depth == 1) std::cout << std::format("{}({})\n", menu.title, code); + if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code); - if (!menu.callback) { - for (const auto &item : menu.items) { + 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); + item.getPrompt(), item.getCode()); + menu->print(item.getCode(), depth + 1); } } } diff --git a/src/generate.cpp b/src/generate.cpp @@ -3,6 +3,58 @@ using namespace stamen; +namespace stamen { + +class Generator { +public: + class EGenerate : std::exception { + virtual const char *what() const noexcept override { + return "Trying to access unknown code"; + } + }; + + static void 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, _] : Menu::getLookup()) { + const Menu *menu = Menu::getMenu(code); + if (!menu) throw EGenerate(); + if (menu->callback) continue; + os << std::format("int {}(void);\n", menu->code); + } + os << "\n}\n"; + os << "#endif\n"; + } + + static void generateSource(std::ostream &os) { + os << "#include <stamen.h>\n"; + os << "#include \"shared.h\"\n"; + os << "\nnamespace stamen {\n"; + for (const auto &[code, _] : Menu::getLookup()) { + const Menu *menu = Menu::getMenu(code); + if (!menu) throw EGenerate(); + 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.getCode(), + item.getPrompt()); + } + 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"; + } +}; + +} // namespace stamen + int main(const int argc, const char *argv[]) { if (argc != 2) { std::cout << "please enter exaclty one config file" << std::endl; @@ -15,8 +67,9 @@ int main(const int argc, const char *argv[]) { std::string::size_type pos = path.rfind('.'); std::string base = pos != std::string::npos ? path.substr(0, pos) : path; std::ofstream cpp(base + ".cpp"), h(base + ".h"); - Menu::generateSource(cpp); - Menu::generateInclude(h); + + Generator::generateSource(cpp); + Generator::generateInclude(h); return 0; }