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:
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;
+}