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
Diffstat:
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