stamen

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

commit 075d692948c0933690afdb61764047bf3e00466f
parent ca36d15bfaaa0846e70bb0ad32da1fc11c1d8fef
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Wed,  6 Dec 2023 20:08:29 +0000

Error fixing and new C++ namespace bindings

* C++ stamen namespace for improved consistency
* Ability to start dynamic menu from custom panel
* Fix memory leak in Menu::Enries
* Removed redundancy
* Update README

Diffstat:
MCMakeLists.txt | 2+-
MREADME.md | 23++++++++++++++++++++++-
Mdemo/dynamic.cpp | 21+++++++++++++--------
Mdemo/main.cpp | 2+-
Minclude/menu.h | 23+++++++++++++----------
Minclude/stamen.h | 16+++++++++++++++-
Msrc/generate.cpp | 6++++++
Msrc/menu.cpp | 15+++++++++++----
Msrc/stamen.cpp | 4+++-
9 files changed, 85 insertions(+), 27 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Stamen - VERSION 0.0.18 + VERSION 0.0.19 DESCRIPTION "Static menu generator" LANGUAGES C CXX ) diff --git a/README.md b/README.md @@ -75,6 +75,8 @@ Please reference demo folder for relevant usage example. There are a few things needed before you begin. +* All types and functions with prefix stamen_ are also available in namespace +stamen:: in C++ for easier use. * Panel and item codes must be one word. In addition they must be valid C/C++ function names if static menu is to be build correctly. * Each free function must have `int name(int)` signature as prescribed by @@ -125,7 +127,7 @@ terminates, but you can use in any way you see fit. #### Dynamic menu In dynamic mode, configuration file is read every time the program is run. In -order to invoke the menu you need to add the following code to your C/C++ +order to invoke the menu you need to add the following snippet to your C program: ``` @@ -144,6 +146,25 @@ stamen_insert("free function code", some_free_function); stamen_dynamic("panel code"); ``` +For C++ there is a namespaced version of the functions: +``` +#include <stamen.h> + +const stamen::display_f stamen_display = stamen::builtin_display; + +// read the configuration +stamen::read("path to config"); + +// register free functions +stamen::insert("free function code", some_free_function); +... + +// start the menu on specific panel +stamen::dynamic("panel code"); +``` + +` + ## Version History * 1.0 diff --git a/demo/dynamic.cpp b/demo/dynamic.cpp @@ -1,7 +1,7 @@ -#include <stamen.h> #include <iostream> +#include <stamen.h> -const stamen_display_f stamen_display = stamen_builtin_display; +const stamen::display_f stamen_display = stamen::builtin_display; int finish(int) { exit(1); } @@ -21,11 +21,16 @@ int operation3(int) { } int main() { - stamen_read("./bin/demo_menu.conf"); - stamen_insert("finish", finish); - stamen_insert("operation1", operation1); - stamen_insert("operation2", operation2); - stamen_insert("operation3", operation3); - stamen_dynamic(); + // read the configuration + stamen::read("./bin/demo_menu.conf"); + + // register free functions + stamen::insert("finish", finish); + stamen::insert("operation1", operation1); + stamen::insert("operation2", operation2); + stamen::insert("operation3", operation3); + + // start the menu on specific panel + stamen::dynamic("menu_main"); return 0; } diff --git a/demo/main.cpp b/demo/main.cpp @@ -5,7 +5,7 @@ // need to link against stamen library // in order to use stamen_builtin_display -const stamen_display_f stamen_display = stamen_builtin_display; +const stamen::display_f stamen_display = stamen::builtin_display; int operation1(int) { std::cout << "operation 1" << std::endl; diff --git a/include/menu.h b/include/menu.h @@ -9,14 +9,14 @@ #include <unordered_map> #include <vector> +namespace stamen { + class Menu { friend class Generator; - using callback_f = stamen_callback_f; - using item_t = stamen_item_t; - static std::unordered_map<std::string, Menu> menu_lookup; static std::unordered_map<std::string, callback_f> free_lookup; + static std::string display_stub_default; struct private_ctor_t {}; @@ -28,7 +28,10 @@ public: Menu(const Menu &) = delete; Menu &operator=(const Menu &) = delete; - static int dynamic() { return display_stub(-1); }; + static int dynamic(const std::string &code) { + display_stub_default = code; + return display_stub(-1); + }; static void read(const std::string &s); static void print(const std::string &entry) { print(entry, 1); } static void insert(const std::string &s, callback_f callback) { @@ -60,18 +63,16 @@ private: static void print(const std::string &entry, const int depth); static int display_stub(int idx); - static const Menu *getMenu(const std::string &code) { - const auto it = menu_lookup.find(code); - if (it == menu_lookup.end()) return nullptr; - return &it->second; - } - struct Entries { struct code_t { const std::string code; const std::string prompt; }; + ~Entries() { + for (const auto [_, prompt] : items) delete[] prompt; + } + std::vector<code_t> codes; std::vector<item_t> items; @@ -89,4 +90,6 @@ private: Entries entries; }; +} // namespace stamen + #endif diff --git a/include/stamen.h b/include/stamen.h @@ -18,11 +18,25 @@ struct stamen_item_t { typedef int (*stamen_display_f)(const char *, const stamen_item_t[], int); extern const stamen_display_f stamen_display; -EXTERNC int stamen_dynamic(void); +EXTERNC int stamen_dynamic(const char *code); EXTERNC void stamen_read(const char *filename); EXTERNC void stamen_insert(const char *code, stamen_callback_f callback); EXTERNC int stamen_builtin_display(const char *title, const stamen_item_t itemv[], int size); +#ifdef __cplusplus +namespace stamen { + +using callback_f = stamen_callback_f; +using display_f = stamen_display_f; +using item_t = stamen_item_t; + +const auto dynamic = stamen_dynamic; +const auto read = stamen_read; +const auto insert = stamen_insert; +const auto builtin_display = stamen_builtin_display; + +} // namespace stamen +#endif #endif diff --git a/src/generate.cpp b/src/generate.cpp @@ -6,6 +6,8 @@ #include <iostream> #include <string> +namespace stamen { + class Generator { public: static void generateInclude(std::ostream &os) { @@ -39,6 +41,10 @@ public: } }; +} // namespace stamen + +using namespace stamen; + int main(const int argc, const char *argv[]) { const auto args = std::span(argv, size_t(argc)); diff --git a/src/menu.cpp b/src/menu.cpp @@ -9,8 +9,11 @@ #include <tuple> #include <utility> +namespace stamen { + std::unordered_map<std::string, Menu> Menu::menu_lookup; -std::unordered_map<std::string, Menu::callback_f> Menu::free_lookup; +std::unordered_map<std::string, callback_f> Menu::free_lookup; +std::string Menu::display_stub_default; void Menu::read(const std::string &s) { std::string line, delim, code, prompt; @@ -35,8 +38,9 @@ void Menu::read(const std::string &s) { } void Menu::print(const std::string &code, const int depth) { - const Menu *menu = getMenu(code); - if (!menu) return; + const auto it = menu_lookup.find(code); + if (it == menu_lookup.end()) return; + const Menu *menu = &it->second; if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code); @@ -50,7 +54,8 @@ void Menu::print(const std::string &code, const int depth) { int Menu::display_stub(int idx) { static std::deque<const Menu *> st; - const std::string &code = st.size() ? st.back()->getCode(idx) : "menu_main"; + const std::string &code = + st.size() ? st.back()->getCode(idx) : display_stub_default; const auto ml_it = menu_lookup.find(code); if (ml_it != menu_lookup.end()) { @@ -68,3 +73,5 @@ int Menu::display_stub(int idx) { std::cout << "Stamen: nothing to do..." << std::endl; return 1; } + +} // namespace stamen diff --git a/src/stamen.cpp b/src/stamen.cpp @@ -7,7 +7,9 @@ #include <ostream> #include <variant> -int stamen_dynamic(void) { return Menu::dynamic(); } +using namespace stamen; + +int stamen_dynamic(const char *code) { return Menu::dynamic(code); } void stamen_read(const char *filename) { Menu::read(filename); } void stamen_insert(const char *code, stamen_callback_f callback) { Menu::insert(code, callback);