commit 8950af9d55d13335e71fe92c0cfc6ee132919584
parent 130362e87a569b184f3dfef590569354de92397a
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Mon, 10 Jun 2024 23:23:26 +0200
Proof of concept C bindings
Diffstat:
7 files changed, 191 insertions(+), 34 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -3,14 +3,18 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(
args
- VERSION 0.0.1
+ VERSION 0.0.2
DESCRIPTION "Command Line Argument Parser"
- LANGUAGES CXX
+ LANGUAGES CXX C
)
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
@@ -1,13 +1,19 @@
set(GENERATE_OUT "${CMAKE_BINARY_DIR}/bin")
-add_executable(demo
- main.cpp
-)
-
+add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE args)
-
+set_target_properties(demo PROPERTIES LINKER_LANGUAGE CXX)
set_target_properties(demo PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT}"
)
+
+add_executable(cdemo main.c)
+target_link_libraries(cdemo PRIVATE cargs)
+set_target_properties(cdemo PROPERTIES LINKER_LANGUAGE C)
+set_target_properties(cdemo PROPERTIES
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ RUNTIME_OUTPUT_DIRECTORY "${GENERATE_OUT}"
+)
diff --git a/demo/main.c b/demo/main.c
@@ -0,0 +1,82 @@
+#include "args.h"
+
+#include <stdio.h>
+
+void error(const char *message) { fprintf(stderr, "%s\n", message); }
+
+typedef struct {
+ const char *output_file;
+ const char *input_file;
+
+ int debug;
+ int hex;
+ int relocatable;
+} arguments_t;
+
+int parse_opt(int key, const char *arg, args_parser *parser) {
+ arguments_t *arguments = (arguments_t *)args_parser_input(parser);
+
+ switch (key) {
+ case 777: arguments->debug = 1; break;
+ case 'h':
+ if (arguments->relocatable) error("cannot mix -hex and -relocatable");
+ arguments->hex = 1;
+ break;
+ case 'r':
+ if (arguments->hex) error("cannot mix -hex and -relocatable");
+ arguments->relocatable = 1;
+ break;
+ case 'o': arguments->output_file = arg ? arg : "stdout"; break;
+ case 'i': arguments->input_file = arg; break;
+ // case Parser::Key::ARG: arguments->args.push_back(arg); break;
+ case ARGS_KEY_ERROR: fprintf(stderr, "handled error\n");
+ case ARGS_KEY_INIT:
+ arguments->input_file = "stdin";
+ arguments->output_file = "stdout";
+ }
+
+ return 0;
+}
+
+// clang-format off
+static const args_option_t options[] = {
+ { 0, 'R', 0, 0, "random 0-group option"},
+ { 0, 0, 0, 0, "Program mode", 1},
+ {"relocatable", 'r', 0, 0, "Output in relocatable format"},
+ { "hex", 'h', 0, 0, "Output in hex format"},
+ {"hexadecimal", 0, 0, ARGS_OPTION_ALIAS | ARGS_OPTION_HIDDEN},
+ { 0, 0, 0, 0, "For developers", 4},
+ { "debug", 777, 0, 0, "Enable debugging mode"},
+ { 0, 0, 0, 0, "Input/output", 3},
+ { "output", 'o', "file", ARGS_OPTION_ARG_OPTIONAL, "Output file, default stdout"},
+ { 0, 'i', "file", 0, "Input file"},
+ { 0, 0, 0, 0, "Informational Options", -1},
+ {0},
+};
+// clang-format on
+
+int main(int argc, char *argv[]) {
+ arguments_t arguments = {0};
+ args_argp_t argp = {
+ options, parse_opt, "doc string\nother usage",
+ "First half of the message\vsecond half of the message"};
+
+ if (args_parse(&argp, argc, argv, &arguments)) {
+ error("There was an error while parsing arguments");
+ return 1;
+ }
+
+ printf("Command line options:\n");
+ printf("\t input: %s\n", arguments.input_file);
+ printf("\t output: %s\n", arguments.output_file);
+ printf("\t hex: %d\n", arguments.hex);
+ printf("\t debug: %d\n", arguments.debug);
+ printf("\t relocatable: %d\n", arguments.relocatable);
+
+ // std::cout << "\t args: ";
+ // for (const auto &arg : arguments.args)
+ // std::cout << arg << " ";
+ // std::cout << std::endl;
+
+ return 0;
+}
diff --git a/include/args.h b/include/args.h
@@ -0,0 +1,54 @@
+#ifndef ARGS_H
+#define ARGS_H
+
+#ifdef __cplusplus
+extern "C" {
+struct Parser;
+typedef Parser args_parser;
+#else
+struct __Parser;
+typedef struct __Parser args_parser;
+#endif
+
+typedef struct {
+ const char *name;
+ int key;
+ const char *arg;
+ int flags;
+ const char *message;
+ int group;
+} args_option_t;
+
+enum args_option_e {
+ ARGS_OPTION_ARG_OPTIONAL = 0x1,
+ ARGS_OPTION_HIDDEN = 0x2,
+ ARGS_OPTION_ALIAS = 0x4,
+};
+
+enum args_key_e {
+ ARGS_KEY_ARG = 0,
+ ARGS_KEY_END = 0x1000001,
+ ARGS_KEY_NO_ARGS = 0x1000002,
+ ARGS_KEY_INIT = 0x1000003,
+ ARGS_KEY_SUCCESS = 0x1000004,
+ ARGS_KEY_ERROR = 0x1000005,
+};
+
+struct args_Parser;
+
+typedef int (*args_parse_f)(int key, const char *arg, args_parser *parser);
+typedef struct {
+ const args_option_t *options;
+ const args_parse_f parse;
+ const char *doc;
+ const char *message;
+} args_argp_t;
+
+int args_parse(args_argp_t *argp, int argc, char *argv[], void *input);
+void *args_parser_input(args_parser *parser);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
diff --git a/include/args.hpp b/include/args.hpp
@@ -1,43 +1,30 @@
#ifndef ARGS_HPP
#define ARGS_HPP
+#include "args.h"
+
#include <string>
#include <unordered_map>
#include <vector>
class Parser {
public:
- struct option_t {
- const char *name;
- const int key;
- const char *arg;
- const int flags;
- const char *message;
- const int group;
- };
+ using option_t = args_option_t;
+ using argp_t = args_argp_t;
enum Option {
- ARG_OPTIONAL = 0x1,
- HIDDEN = 0x2,
- ALIAS = 0x4,
+ ARG_OPTIONAL = ARGS_OPTION_ARG_OPTIONAL,
+ HIDDEN = ARGS_OPTION_HIDDEN,
+ ALIAS = ARGS_OPTION_ALIAS,
};
enum Key {
- ARG = 0,
- END = 0x1000001,
- NO_ARGS = 0x1000002,
- INIT = 0x1000003,
- SUCCESS = 0x1000004,
- ERROR = 0x1000005,
- };
-
- struct argp_t {
- using parse_f = int (*)(int key, const char *arg, Parser *parser);
-
- const option_t *options;
- const parse_f parse;
- const char *doc;
- const char *message;
+ ARG = ARGS_KEY_ARG,
+ END = ARGS_KEY_END,
+ NO_ARGS = ARGS_KEY_NO_ARGS,
+ INIT = ARGS_KEY_INIT,
+ SUCCESS = ARGS_KEY_SUCCESS,
+ ERROR = ARGS_KEY_ERROR,
};
static int parse(argp_t *argp, int argc, char *argv[], void *input) {
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
@@ -1,5 +1,6 @@
-add_library(args args.cpp help.cpp trie.cpp)
+add_library(args STATIC args.cpp help.cpp trie.cpp)
target_include_directories(args PUBLIC ../include)
+set_target_properties(args PROPERTIES LINKER_LANGUAGE CXX)
set_target_properties(args PROPERTIES
VERSION ${PROJECT_VERSION}
@@ -11,3 +12,18 @@ install(TARGETS args
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
)
+
+ add_library(cargs STATIC c_bindings.cpp args.cpp help.cpp trie.cpp)
+target_include_directories(cargs PUBLIC ../include)
+set_target_properties(cargs PROPERTIES LINKER_LANGUAGE CXX)
+
+set_target_properties(cargs PROPERTIES
+ VERSION ${PROJECT_VERSION}
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ PUBLIC_HEADER ../include/args.h
+)
+
+install(TARGETS cargs
+ LIBRARY DESTINATION lib
+ PUBLIC_HEADER DESTINATION include
+)
diff --git a/src/c_bindings.cpp b/src/c_bindings.cpp
@@ -0,0 +1,8 @@
+#include "args.h"
+#include "args.hpp"
+
+int args_parse(args_argp_t *argp, int argc, char *argv[], void *input) {
+ return Parser::parse(argp, argc, argv, input);
+}
+
+void *args_parser_input(args_parser *parser) { return parser->input; }