stamenStatic Menu Generator |
git clone git://git.dimitrijedobrota.com/stamen.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | |
commit | 472fad60cd717eef6ead0ed88668462e630f78c3 |
parent | 45ebb3b798a9cf306ccfce512db8755e22cc472e |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Thu, 13 Jun 2024 20:00:12 +0200 |
New .clang-format, full reformat
Diffstat:M | .clang-format | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
M | demo/dynamic.cpp | | | ++++++++++++++++++------------------ |
M | demo/main.c | | | +++++++++++++------------- |
M | demo/main.cpp | | | +++++++++++++------------- |
M | include/menu.h | | | ++++++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | include/stamen.h | | | ++-- |
M | src/c_bindings.cpp | | | ++-- |
M | src/generate.cpp | | | +++++++++++++++++++++++++++++++++++++++++----------------------------------------- |
M | src/menu.cpp | | | +++++++++++++++++++++++++++++++++++++++++----------------------------------------- |
M | src/stamen.cpp | | | ++++++++++++++++++++++++++++++++++--------------------------------- |
10 files changed, 293 insertions(+), 249 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -1,26 +1,45 @@
---
Language: Cpp
# BasedOnStyle: LLVM
# BasedOnStyle: Microsoft
AccessModifierOffset: -2
AlignAfterOpenBracket: true
AlignArrayOfStructures: Right
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: true
AllowShortEnumsOnASingleLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortFunctionsOnASingleLine: true
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
@@ -32,7 +51,7 @@ BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
@@ -49,8 +68,8 @@ BraceWrapping:
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
@@ -58,10 +77,10 @@ BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
ColumnLimit: 79
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
@@ -71,6 +90,10 @@ DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: true
ForEachMacros:
- foreach
@@ -100,9 +123,10 @@ IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 2
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
@@ -121,15 +145,19 @@ PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyReturnTypeOnItsOwnLine: 1000
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RequiresClausePosition: OwnLine
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
@@ -143,6 +171,16 @@ SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
@@ -165,7 +203,7 @@ StatementAttributeLikeMacros:
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
TabWidth: 4
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
diff --git a/demo/dynamic.cpp b/demo/dynamic.cpp
@@ -8,31 +8,31 @@ const stamen::display_f &stamen::display = stamen::builtin_display;
int finish(int) { exit(1); }
int operation1(int) {
std::cout << "1" << std::endl;
return 1;
std::cout << "1" << std::endl;
return 1;
}
int operation2(int) {
std::cout << "2" << std::endl;
return 1;
std::cout << "2" << std::endl;
return 1;
}
int operation3(int) {
std::cout << "3" << std::endl;
return 1;
std::cout << "3" << std::endl;
return 1;
}
int main() {
// 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;
// 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.c b/demo/main.c
@@ -9,29 +9,29 @@
const stamen_display_f stamen_display = stamen_builtin_display;
int operation1(void) {
printf("operation 1\n");
printf("Some operation is done\n");
return 1;
printf("operation 1\n");
printf("Some operation is done\n");
return 1;
}
int operation2(void) {
printf("operation 2\n");
printf("Some other operation is done\n");
return 1;
printf("operation 2\n");
printf("Some other operation is done\n");
return 1;
}
int operation3(void) {
printf("operation 3\n");
printf("Yet another operation is done\n");
return 1;
printf("operation 3\n");
printf("Yet another operation is done\n");
return 1;
}
int finish(void) {
printf("finishing...\n");
exit(0);
printf("finishing...\n");
exit(0);
}
int main(void) {
menu_main(0);
return 0;
menu_main(0);
return 0;
}
diff --git a/demo/main.cpp b/demo/main.cpp
@@ -8,29 +8,29 @@
const stamen::display_f &stamen::display = stamen::builtin_display;
int operation1(int) {
std::cout << "operation 1" << std::endl;
std::cout << "Some operation is done" << std::endl;
return 1;
std::cout << "operation 1" << std::endl;
std::cout << "Some operation is done" << std::endl;
return 1;
}
int operation2(int) {
std::cout << "operation 2" << std::endl;
std::cout << "Some other operation is done" << std::endl;
return 1;
std::cout << "operation 2" << std::endl;
std::cout << "Some other operation is done" << std::endl;
return 1;
}
int operation3(int) {
std::cout << "operation 3" << std::endl;
std::cout << "Yet another operation is done" << std::endl;
return 1;
std::cout << "operation 3" << std::endl;
std::cout << "Yet another operation is done" << std::endl;
return 1;
}
int finish(int) {
std::cout << "finishing..." << std::endl;
exit(0);
std::cout << "finishing..." << std::endl;
exit(0);
}
int main() {
menu_main(0);
return 0;
menu_main(0);
return 0;
}
diff --git a/include/menu.h b/include/menu.h
@@ -12,82 +12,86 @@
namespace stamen {
class Menu {
friend class Generator;
friend class Generator;
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;
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 {};
struct private_ctor_t {};
public:
// Tag type dispatch
Menu(private_ctor_t, const std::string &code, const std::string &prompt)
: Menu(code, prompt) {}
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(const Menu &) = delete;
Menu &operator=(const Menu &) = delete;
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) {
free_lookup.emplace(s, callback);
}
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) {
free_lookup.emplace(s, callback);
}
[[nodiscard]] const std::string &getCode() const { return code; }
[[nodiscard]] const std::string &getTitle() const { return title; }
[[nodiscard]] const std::string &getCode() const { return code; }
[[nodiscard]] const std::string &getTitle() const { return title; }
[[nodiscard]] const item_t *getItemv() const { return entries.items.data(); }
[[nodiscard]] std::size_t getSize() const { return entries.items.size(); }
[[nodiscard]] const item_t *getItemv() const {
return entries.items.data();
}
[[nodiscard]] std::size_t getSize() const { return entries.items.size(); }
[[nodiscard]] const std::string &getCode(std::size_t idx) const {
return entries.codes[idx].code;
}
[[nodiscard]] const std::string &getCode(std::size_t idx) const {
return entries.codes[idx].code;
}
[[nodiscard]] const std::string &getPrompt(std::size_t idx) const {
return entries.codes[idx].prompt;
}
[[nodiscard]] const std::string &getPrompt(std::size_t idx) const {
return entries.codes[idx].prompt;
}
[[nodiscard]] callback_f getCallback(std::size_t idx) const {
return entries.items[idx].callback;
}
[[nodiscard]] callback_f getCallback(std::size_t idx) const {
return entries.items[idx].callback;
}
private:
Menu(std::string code, std::string prompt)
: code(std::move(code)), title(std::move(prompt)) {}
private:
Menu(std::string code, std::string prompt)
: code(std::move(code)), title(std::move(prompt)) {}
static void print(const std::string &entry, const int depth);
static int display_stub(int idx);
static void print(const std::string &entry, const int depth);
static int display_stub(int idx);
struct Entries {
struct code_t {
const std::string code;
const std::string prompt;
};
struct Entries {
struct code_t {
std::string code;
std::string prompt;
};
~Entries() {
for (const auto [_, prompt] : items) delete[] prompt;
}
~Entries() {
for (const auto [_, prompt] : items) {
delete[] prompt;
}
}
std::vector<code_t> codes;
std::vector<item_t> items;
std::vector<code_t> codes;
std::vector<item_t> items;
void insert(const std::string &code, const std::string &prompt,
callback_f callback = display_stub) {
char *buffer = new char[prompt.size() + 1];
strcpy(buffer, prompt.c_str());
items.emplace_back(callback, buffer);
void insert(const std::string &code, const std::string &prompt,
callback_f callback = display_stub) {
char *buffer = new char[prompt.size() + 1];
strcpy(buffer, prompt.c_str());
items.emplace_back(callback, buffer);
codes.emplace_back(code, prompt);
}
};
codes.emplace_back(code, prompt);
}
};
const std::string code, title;
Entries entries;
const std::string code, title;
Entries entries;
};
} // namespace stamen
diff --git a/include/stamen.h b/include/stamen.h
@@ -10,8 +10,8 @@ typedef int (*stamen_callback_f)(int);
typedef struct stamen_item_t stamen_item_t;
struct stamen_item_t {
stamen_callback_f callback;
const char *prompt;
stamen_callback_f callback;
const char *prompt;
};
typedef int (*stamen_display_f)(const char *, const stamen_item_t[], int);
diff --git a/src/c_bindings.cpp b/src/c_bindings.cpp
@@ -8,12 +8,12 @@ int stamen_dynamic(const char *code) { return dynamic(code); }
void stamen_read(const char *filename) { return read(filename); }
void stamen_insert(const char *code, stamen_callback_f callback) {
return insert(code, callback);
return insert(code, callback);
}
int stamen_builtin_display(const char *title, const stamen_item_t itemv[],
int size) {
return builtin_display(title, itemv, size);
return builtin_display(title, itemv, size);
}
} // namespace stamen
diff --git a/src/generate.cpp b/src/generate.cpp
@@ -9,47 +9,47 @@
namespace stamen {
class Generator {
public:
static void generateInclude(std::ostream &os, bool cpp) {
os << "#ifndef STAMEN_MENU_H\n";
os << "#define STAMEN_MENU_H\n\n";
public:
static void generateInclude(std::ostream &os, bool cpp) {
os << "#ifndef STAMEN_MENU_H\n";
os << "#define STAMEN_MENU_H\n\n";
if (cpp) os << "#include \"stamen.hpp\"\n\n";
else os << "#include \"stamen.h\"\n\n";
if (cpp) os << "#include \"stamen.hpp\"\n\n";
else os << "#include \"stamen.h\"\n\n";
for (const auto &[code, menu] : Menu::menu_lookup) {
os << std::format("int {}(int);\n", menu.getCode());
}
for (const auto &[code, menu] : Menu::menu_lookup) {
os << std::format("int {}(int);\n", menu.getCode());
}
os << "\n#endif\n";
}
os << "\n#endif\n";
}
static void generateSource(std::ostream &os, bool cpp) {
if (cpp) os << "#include \"stamen.hpp\"\n\n";
else os << "#include \"stamen.h\"\n\n";
static void generateSource(std::ostream &os, bool cpp) {
if (cpp) os << "#include \"stamen.hpp\"\n\n";
else os << "#include \"stamen.h\"\n\n";
os << "#include \"shared.h\"\n\n";
for (const auto &[code, menu] : Menu::menu_lookup) {
os << std::format("int {}(int) {{\n", menu.getCode());
os << "#include \"shared.h\"\n\n";
for (const auto &[code, menu] : Menu::menu_lookup) {
os << std::format("int {}(int) {{\n", menu.getCode());
if (cpp) os << "\tstatic const stamen::item_t items[] = ";
else os << "\tstatic const stamen_item_t items[] = ";
if (cpp) os << "\tstatic const stamen::item_t items[] = ";
else os << "\tstatic const stamen_item_t items[] = ";
os << "{\n";
for (int i = 0; i < menu.getSize(); i++) {
os << "\t\t{ " << menu.getCode(i);
os << ", \"" << menu.getPrompt(i) << "\" },\n";
}
os << "\t};\n";
os << "{\n";
for (int i = 0; i < menu.getSize(); i++) {
os << "\t\t{ " << menu.getCode(i);
os << ", \"" << menu.getPrompt(i) << "\" },\n";
}
os << "\t};\n";
if (cpp) os << "\treturn stamen::display";
else os << "\treturn stamen_display";
if (cpp) os << "\treturn stamen::display";
else os << "\treturn stamen_display";
os << std::format("(\"{}\"", menu.getTitle());
os << ", items, sizeof(items) / sizeof(items[0]));\n";
os << "}\n\n";
os << std::format("(\"{}\"", menu.getTitle());
os << ", items, sizeof(items) / sizeof(items[0]));\n";
os << "}\n\n";
}
}
}
};
} // namespace stamen
@@ -57,25 +57,25 @@ public:
using namespace stamen;
int main(const int argc, const char *argv[]) {
const auto args = std::span(argv, size_t(argc));
const auto args = std::span(argv, size_t(argc));
if (argc != 2 && argc != 3) {
std::cout << argv[0] << " config_file [c/cpp]" << std::endl;
return 1;
}
if (argc != 2 && argc != 3) {
std::cout << argv[0] << " config_file [c/cpp]" << std::endl;
return 1;
}
const bool cpp = argc == 2 || std::string(args[2]) == "cpp";
const bool cpp = argc == 2 || std::string(args[2]) == "cpp";
std::string path = args[1];
Menu::read(path);
std::string path = args[1];
Menu::read(path);
std::string::size_type pos = path.rfind('.');
std::string base = pos != std::string::npos ? path.substr(0, pos) : path;
std::string ext = cpp ? "pp" : "";
std::string::size_type pos = path.rfind('.');
std::string base = pos != std::string::npos ? path.substr(0, pos) : path;
std::string ext = cpp ? "pp" : "";
std::ofstream source(base + ".c" + ext), include(base + ".h" + ext);
Generator::generateSource(source, cpp);
Generator::generateInclude(include, cpp);
std::ofstream source(base + ".c" + ext), include(base + ".h" + ext);
Generator::generateSource(source, cpp);
Generator::generateInclude(include, cpp);
return 0;
return 0;
}
diff --git a/src/menu.cpp b/src/menu.cpp
@@ -15,62 +15,63 @@ 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;
std::fstream fs(s);
char tmp = 0;
std::string line, delim, code, prompt;
std::fstream fs(s);
char tmp = 0;
auto last = menu_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] = menu_lookup.emplace(
std::piecewise_construct, std::forward_as_tuple(code),
std::forward_as_tuple(private_ctor_t{}, code, prompt));
last = iter;
} else {
last->second.entries.insert(code, prompt);
auto last = menu_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] = menu_lookup.emplace(
std::piecewise_construct, std::forward_as_tuple(code),
std::forward_as_tuple(private_ctor_t{}, code, prompt));
last = iter;
} else {
last->second.entries.insert(code, prompt);
}
}
}
}
void Menu::print(const std::string &code, const int depth) {
const auto it = menu_lookup.find(code);
if (it == menu_lookup.end()) return;
const Menu *menu = &it->second;
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);
if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code);
for (int i = 0; i < menu->getSize(); i++) {
std::cout << std::format("{}{} ({})\n", std::string(depth << 1, ' '),
menu->getPrompt(i), menu->getCode(i));
menu->print(code, depth + 1);
}
for (int i = 0; i < menu->getSize(); i++) {
std::cout << std::string(depth << 1, ' ');
std::cout << menu->getPrompt(i);
std::cout << std::format(" ({})\n", menu->getCode(i));
menu->print(code, depth + 1);
}
}
int Menu::display_stub(int idx) {
static std::deque<const Menu *> st;
static std::deque<const Menu *> st;
const std::string &code =
st.size() ? st.back()->getCode(idx) : display_stub_default;
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()) {
const Menu &menu = ml_it->second;
st.push_back(&menu);
int ret = stamen_builtin_display(menu.title.c_str(), menu.getItemv(),
menu.getSize());
st.pop_back();
return ret;
}
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 = builtin_display(menu.title.c_str(), menu.getItemv(),
menu.getSize());
st.pop_back();
return ret;
}
const auto fl_it = free_lookup.find(code);
if (fl_it != free_lookup.end()) { return fl_it->second(0); }
const auto fl_it = free_lookup.find(code);
if (fl_it != free_lookup.end()) { return fl_it->second(0); }
std::cout << "Stamen: nothing to do..." << std::endl;
return 1;
std::cout << "Stamen: nothing to do..." << std::endl;
return 1;
}
} // namespace stamen
diff --git a/src/stamen.cpp b/src/stamen.cpp
@@ -12,48 +12,49 @@ namespace stamen {
int dynamic(const char *code) { return Menu::dynamic(code); }
void read(const char *filename) { Menu::read(filename); }
void insert(const char *code, callback_f callback) {
Menu::insert(code, callback);
Menu::insert(code, callback);
}
int builtin_display(const char *title, const item_t itemv[], int size) {
const size_t digits = size_t(std::log10(size)) + 1;
const auto items = std::span(itemv, size_t(size));
int choice = 0;
while (true) {
std::cout << std::format("{}:\n", title);
for (auto i = 0ul; i < size; i++) {
std::cout << std::format(" {:{}}. {}\n", i, digits, items[i].prompt);
}
const size_t digits = size_t(std::log10(size)) + 1;
const auto items = std::span(itemv, size_t(size));
int choice = 0;
while (true) {
std::cout << "Choose an option: ";
if (std::cin >> choice && choice >= -1 && choice < (int)size) {
if (choice == -1) {
std::cout << "Choice: back\n";
return 1;
std::cout << std::format("{}:\n", title);
for (auto i = 0ul; i < size; i++) {
std::cout << std::format(" {:{}}. {}\n", i, digits,
items[i].prompt);
}
std::cout << std::format("Choice: {}\n\n", items[choice].prompt);
const int res = items[choice].callback(choice);
if (res > 1)
return res - 1;
else
break;
} else if (std::cin.eof()) {
std::cerr << "encountered end of input!\n";
return std::numeric_limits<int>::max();
} else {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
std::cout << "Invalid option, please choose again!\n";
while (true) {
std::cout << "Choose an option: ";
if (std::cin >> choice && choice >= -1 && choice < (int)size) {
if (choice == -1) {
std::cout << "Choice: back\n";
return 1;
}
std::cout << std::format("Choice: {}\n\n",
items[choice].prompt);
const int res = items[choice].callback(choice);
if (res > 1) return res - 1;
else break;
} else if (std::cin.eof()) {
std::cerr << "encountered end of input!\n";
return std::numeric_limits<int>::max();
} else {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
}
std::cout << "Invalid option, please choose again!\n";
}
std::cout << std::endl;
}
std::cout << std::endl;
}
return 1;
return 1;
}
} // namespace stamen