stamen

Static Menu Generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

commitcd12b2fea38be76fe6bd2c607598c785d979c25e
parent199eba24714cb83271e636100315011def07ded9
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateFri, 14 Jun 2024 09:33:39 +0200

Split menu in its own set of headers

Diffstat:
MCMakeLists.txt|++++--------
Mdemo/dynamic.cpp|++++++++-------
Minclude/menu.h|++++++++++------------------------------------------------------------------------
Ainclude/menu.hpp|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/stamen.h|+-----
Minclude/stamen.hpp|----
Msrc/CMakeLists.txt|+-
Msrc/c_bindings.cpp|+++++++++++---------
Msrc/generate.cpp|+++++-----
Msrc/menu.cpp|+++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/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;