stamenStatic Menu Generator |
git clone git://git.dimitrijedobrota.com/stamen.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | |
commit | f4fed18ad7916907f96caaa641143d3ed00f02f6 |
parent | 69b2fecb3c6afe1358abb309dd8a3a95cdf417e7 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Thu, 30 Nov 2023 02:16:26 +0000 |
Provide C version of the library * Remove old .ccls * Add C demo code * Provide C binding for the library
Diffstat:D | .ccls | | | ---- |
M | .gitignore | | | ++ |
M | CMakeLists.txt | | | ++++++-- |
M | demo/CMakeLists.txt | | | +++++++++++++++++++++++++++++++-- |
A | demo/main.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | demo/main.cpp | | | +- |
M | include/stamen.h | | | +++++-- |
A | include/stamenc.h | | | +++++++++++++++++++++++++++ |
M | src/CMakeLists.txt | | | +++++++++++++++++------- |
M | src/generate.cpp | | | ++++++++++++++++++++++------------------ |
A | src/stamenc.cpp | | | +++++++++++++++++++++++ |
11 files changed, 199 insertions(+), 36 deletions(-)
diff --git a/.ccls b/.ccls
@@ -1,4 +0,0 @@
clang++
-style=file
-std=c++17
-Iinclude
diff --git a/.gitignore b/.gitignore
@@ -1,4 +1,6 @@
build
.cache
demo/demo_menu.c
demo/demo_menu.cpp
demo/demo_menu.h
demo/demo_menu.hpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -3,14 +3,18 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(
Stamen
VERSION 0.0.11
VERSION 0.0.12
DESCRIPTION "Static menu generator"
LANGUAGES CXX
LANGUAGES C CXX
)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
add_subdirectory(src)
add_subdirectory(demo)
diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt
@@ -8,14 +8,14 @@ target_link_libraries(demo
)
add_custom_command(
OUTPUT demo_menu.h demo_menu.cpp
OUTPUT demo_menu.hpp demo_menu.cpp
COMMAND ${CMAKE_BINARY_DIR}/bin/stamen-generate ${CMAKE_CURRENT_SOURCE_DIR}/demo_menu.conf
DEPENDS demo_menu.conf stamen-generate
COMMENT "Generating menu files"
)
add_custom_target(GenerateMenu
DEPENDS demo_menu.h demo_menu.cpp
DEPENDS demo_menu.hpp demo_menu.cpp
COMMENT "Check if re-generation of menu files is required"
)
@@ -28,3 +28,32 @@ set_target_properties(demo PROPERTIES
)
configure_file(demo_menu.conf ${CMAKE_BINARY_DIR}/bin/demo_menu.conf COPYONLY)
add_executable(cdemo
main.c
${CMAKE_CURRENT_SOURCE_DIR}/demo_menu.c
)
target_link_libraries(cdemo
stamenc
)
add_custom_command(
OUTPUT demo_menu.h demo_menu.c
COMMAND ${CMAKE_BINARY_DIR}/bin/stamen-generate ${CMAKE_CURRENT_SOURCE_DIR}/demo_menu.conf c
DEPENDS demo_menu.conf stamen-generate
COMMENT "Generating menu files"
)
add_custom_target(GenerateMenuC
DEPENDS demo_menu.h demo_menu.c
COMMENT "Check if re-generation of menu files is required"
)
add_dependencies(cdemo GenerateMenuC)
set_target_properties(cdemo PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
diff --git a/demo/main.c b/demo/main.c
@@ -0,0 +1,65 @@
#include "demo_menu.h"
#include "stamenc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const display_f display = stamen_builtin_display;
int operation1(void) {
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;
}
int operation3(void) {
printf("operation 3\n");
printf("Yet another operation is done\n");
return 1;
}
int finish(void) {
printf("finishing...\n");
exit(0);
}
int menu_static_run(void) { return menu_main(); }
int menu_dynamic_run(void) { return stamen_start("menu_main"); }
int menu_dynamic_print(void) {
stamen_print("menu_main");
return 1;
}
int main(int argc, const char *argv[]) {
const char *filename = "./demo_menu.conf";
char buffer[FILENAME_MAX] = {0};
const char *sep = strrchr(argv[0], '/');
if (sep) {
const size_t size = sep - argv[0] + 1;
memcpy(buffer, argv[0], size);
memcpy(buffer + size, filename, strlen(filename));
}
stamen_read(sep ? buffer : filename);
stamen_insert("finish", finish);
stamen_insert("operation1", operation1);
stamen_insert("operation2", operation2);
stamen_insert("operation3", operation3);
static const item_t items[] = {
{ menu_static_run, "Run statically generated menu"},
{ menu_dynamic_run, "Run dynamic menu"},
{menu_dynamic_print, "Print dynamic menu"},
{ finish, "Quit"},
};
display("Menu demo program", items, sizeof(items) / sizeof(item_t));
return 0;
}
diff --git a/demo/main.cpp b/demo/main.cpp
@@ -1,6 +1,6 @@
#include <iostream>
#include "demo_menu.h"
#include "demo_menu.hpp"
using stamen::Menu;
diff --git a/include/stamen.h b/include/stamen.h
@@ -1,6 +1,8 @@
#ifndef STAMEN_H
#define STAMEN_H
#include "stamenc.h"
#include <exception>
#include <format>
#include <fstream>
@@ -31,6 +33,7 @@ public:
: Menu(code, callback) {}
struct item_t {
item_t() {}
item_t(const callback_f func, const std::string &prompt)
: callback(func), prompt(prompt) {}
@@ -44,8 +47,8 @@ public:
int operator()(void) const { return callback ? callback() : start(code); }
private:
const std::string prompt, code;
const callback_f callback = nullptr;
std::string prompt, code;
callback_f callback = nullptr;
};
typedef int (*display_f)(const std::string &, const item_t[], std::size_t);
diff --git a/include/stamenc.h b/include/stamenc.h
@@ -0,0 +1,27 @@
#ifndef CSTAMEN_H
#define CSTAMEN_H
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC extern
#endif
typedef int (*callback_f)(void);
typedef struct item_t item_t;
struct item_t {
callback_f callback;
const char *prompt;
};
typedef int (*display_f)(const char *, const item_t[], int);
extern const display_f display;
EXTERNC void stamen_read(const char *file);
EXTERNC void stamen_print(const char *entry);
EXTERNC int stamen_start(const char *entry);
EXTERNC void stamen_insert(const char *code, callback_f callback);
EXTERNC int stamen_builtin_display(const char *title, const item_t items[], int size);
#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
@@ -1,6 +1,22 @@
add_library(stamen-include INTERFACE)
target_include_directories(stamen-include INTERFACE ../include)
add_library(stamen stamen.cpp)
target_link_libraries(stamen PUBLIC stamen-include)
set_target_properties(stamen PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
PUBLIC_HEADER ../include/stamen.h
)
add_library(stamenc stamenc.cpp stamen.cpp)
target_link_libraries(stamenc PUBLIC stamen-include)
set_target_properties(stamenc PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
PUBLIC_HEADER ../include/stamenc.h
)
add_executable(stamen-generate generate.cpp)
target_link_libraries(stamen-generate PRIVATE stamen-include)
target_include_directories(stamen-generate PUBLIC ../include)
@@ -11,16 +27,10 @@ set_target_properties(stamen-generate PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
add_library(stamen stamen.cpp)
target_link_libraries(stamen PUBLIC stamen-include)
set_target_properties(stamen PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
PUBLIC_HEADER ../include/stamen.h
)
install(TARGETS stamen-generate DESTINATION bin)
install(TARGETS stamen
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
)
diff --git a/src/generate.cpp b/src/generate.cpp
@@ -13,63 +13,67 @@ public:
}
};
static void generateInclude(std::ostream &os) {
static void generateInclude(std::ostream &os, bool cpp) {
os << "#ifndef STAMEN_MENU_H\n";
os << "#define STAMEN_MENU_H\n\n";
os << "#include <stamen.h>\n\n";
os << "namespace stamen {\n\n";
os << std::format("#include <stamen{}.h>\n\n", !cpp ? "c" : "");
if (cpp) 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";
if (cpp) 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";
static void generateSource(std::ostream &os, bool cpp) {
os << std::format("#include <stamen{}.h>\n", !cpp ? "c" : "");
os << "#include \"shared.h\"\n\n";
if (cpp) os << "namespace 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");
os << std::format("\tstatic const {}item_t items[] = {{\n",
cpp ? "Menu::" : "");
for (const auto &item : menu->items) {
os << std::format("\t\t{{{}, \"{}\"}},\n", item.getCode(),
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("\treturn {0}display(\"{1}\", items, "
"sizeof(items) / sizeof({0}item_t));\n",
cpp ? "Menu::" : "", menu->title);
os << std::format("}}\n\n");
}
os << "\n}\n";
if (cpp) os << "\n}\n";
}
};
} // namespace stamen
int main(const int argc, const char *argv[]) {
if (argc != 2) {
if (argc != 2 && argc != 3) {
std::cout << "please enter exaclty one config file" << std::endl;
return 1;
}
const bool cpp = argc == 2 || std::string(argv[2]) == "cpp";
std::string path = argv[1];
Menu::read(path);
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");
std::string ext = cpp ? "pp" : "";
Generator::generateSource(cpp);
Generator::generateInclude(h);
std::ofstream source(base + ".c" + ext), include(base + ".h" + ext);
Generator::generateSource(source, cpp);
Generator::generateInclude(include, cpp);
return 0;
}
diff --git a/src/stamenc.cpp b/src/stamenc.cpp
@@ -0,0 +1,23 @@
#include "../include/stamen.h"
using namespace stamen;
const Menu::display_f Menu::display = nullptr;
void stamen_read(const char *file) { stamen::Menu::read(file); }
void stamen_print(const char *entry) { stamen::Menu::print(entry); }
int stamen_start(const char *entry) { return stamen::Menu::start(entry); }
void stamen_insert(const char *code, callback_f callback) {
Menu::insert(code, callback);
}
int stamen_builtin_display(const char *title, const item_t items[], int size) {
Menu::item_t *litems = new Menu::item_t[size];
if (!litems) return -1;
for (int i = 0; i < size; i++) {
litems[i] = Menu::item_t(items[i].callback, items[i].prompt);
}
const int ret = builtinDisplay(title, litems, size);
delete[] litems;
return ret;
}