commit e1588bcd70106cd590b8b07342d7c15b1625ce07 Author: BENEDEK László Date: Thu Nov 30 21:36:11 2023 +0100 init diff --git a/flags.cpp b/flags.cpp new file mode 100644 index 0000000..508e458 --- /dev/null +++ b/flags.cpp @@ -0,0 +1,70 @@ +#include "flags.h" +#include + +namespace Flags { + +std::vector split(const std::string &input, const char separator) { + std::vector result; + std::istringstream stream(input); + std::string part; + while (std::getline(stream, part, separator)) + result.push_back(part); + + return result; +} + +bool starts_with(const std::string &input, const std::string &start) { + return (input.length() >= start.length() && input.substr(0, 2) == start); +} + +bool Parser::parse(int argc, char **argv) { + // put each argument into a std::string + std::vector args; + for (int i = 0; i < argc; i++) { + std::string arg(argv[i]); + if (starts_with(arg, prefix) && arg.length() > prefix.length()) { + std::vector parts = split(arg, '='); + args.insert(args.end(), parts.begin(), parts.end()); + } else { + args.push_back(arg); + } + } + + // search for flags + for (size_t i = 1; i < args.size() - 1; i++) { + const std::string &arg = args[i]; + if (starts_with(arg, prefix) && arg.length() > prefix.length()) { + flags[arg.substr(prefix.length())]->parse(args[i + 1]); + } + } + + // check for the last argument + if (starts_with(args[args.size() - 1], prefix) && + args[args.size() - 1].length() > prefix.length()) { + flags[args[args.size() - 1].substr(prefix.length())]->parse(""); + } + + return get_found() == flags.size(); +} + +const unsigned Parser::get_parsed() const { + return std::count_if( + flags.begin(), flags.end(), + [](std::pair f) { return f.second->get_parsed(); }); +} + +const unsigned Parser::get_found() const { + return std::count_if( + flags.begin(), flags.end(), + [](std::pair f) { return f.second->get_found(); }); +} + +void Parser::help() const { + std::cout << help_text << std::endl; + + for (auto f : flags) { + std::cout << '\t' << prefix << f.first << ": " + << f.second->get_description() << std::endl; + } +} +} // namespace Flags \ No newline at end of file diff --git a/flags.h b/flags.h new file mode 100644 index 0000000..535b59c --- /dev/null +++ b/flags.h @@ -0,0 +1,113 @@ +#ifndef FLAGS_H +#define FLAGS_H + +#include +#include +#include +#include +#include +#include + +namespace Flags { + +// utility functions +std::vector split(const std::string &input, const char separator); +bool starts_with(const std::string &input, const std::string &start); + +// flag base class +// should be abstract, don't use +class Flag { + std::string description; + bool found; + bool parsed; + +protected: + void set_found(const bool found) { this->found = found; } + void set_parsed(const bool parsed) { this->parsed = parsed; } + +public: + virtual const void *get_value_ptr() const = 0; + + Flag(const std::string &description, const bool required) + : description(description), found(!required), parsed(false) {} + + virtual ~Flag() {}; + + const std::string &get_description() const { return description; } + const bool get_found() const { return found; } + const bool get_parsed() const { return parsed; } + + virtual void parse(const std::string &arg) {} +}; + +// type of flag based parser classes' factory functions +typedef std::function + flag_constructor_t; + +class StringFlag : public Flag { + std::string value; + +public: + StringFlag(const void *default_value, const std::string &description, + const bool required) + : Flag(description, required), value(*(std::string *)default_value) {} + + static Flag *make(const void *default_value, const std::string &description, + const bool required) { + return new StringFlag(default_value, description, required); + } + + const void *get_value_ptr() const override { return &value; }; + + void parse(const std::string &arg) override { + set_found(true); + set_parsed(true); + value = arg; + } +}; + +// flag holder, parsing orchestrator +class Parser { +private: + const std::string prefix; + const std::string help_text; + + std::map flags; + std::map constructors; + +public: + Parser(const std::string &prefix, const std::string &help_text) + : prefix(prefix), help_text(help_text) { + set_parser(flag_constructor_t(StringFlag::make)); + } + + Parser() : Parser("--", "Help:") {} + + ~Parser() { + for (auto f : flags) { + delete f.second; + } + } + + template void set_parser(flag_constructor_t constructor_function) { + constructors[typeid(T)] = constructor_function; + } + + template + T *add(const std::string &name, const std::string &description, + const bool required, T default_value) { + flags[name] = + constructors[typeid(T)]((void *)&default_value, description, required); + return (T *)flags[name]->get_value_ptr(); + } + + const unsigned get_found() const; + + const unsigned get_parsed() const; + + bool parse(int argc, char **argv); + + void help() const; +}; +} // namespace Flags +#endif \ No newline at end of file