macro and basic parsers

This commit is contained in:
Benedek László 2023-12-01 03:32:03 +01:00
parent e1588bcd70
commit 37a397d3e6
2 changed files with 140 additions and 37 deletions

120
flags.cpp
View File

@ -1,5 +1,6 @@
#include "flags.h" #include "flags.h"
#include <sstream> #include <sstream>
#include <iostream>
namespace Flags { namespace Flags {
@ -14,7 +15,110 @@ std::vector<std::string> split(const std::string &input, const char separator) {
} }
bool starts_with(const std::string &input, const std::string &start) { bool starts_with(const std::string &input, const std::string &start) {
return (input.length() >= start.length() && input.substr(0, 2) == start); return (input.size() >= start.size() && input.substr(0, 2) == start);
}
PARSER(StringFlag, std::string, {
set_found(true);
set_parsed(true);
value = arg;
})
PARSER(IntFlag, int, {
set_found(true);
try {
value = std::stoi(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(LongIntFlag, long int, {
set_found(true);
try {
value = std::stol(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(LongLongIntFlag, long long int, {
set_found(true);
try {
value = std::stoll(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(UnsignedLongIntFlag, unsigned long int, {
set_found(true);
try {
value = std::stoul(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(UnsignedLongLongIntFlag, unsigned long long int, {
set_found(true);
try {
value = std::stoull(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(FloatFlag, float, {
set_found(true);
try {
value = std::stof(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(DoubleFlag, double, {
set_found(true);
try {
value = stod(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(LongDoubleFlag, long double, {
set_found(true);
try {
value = stold(arg);
set_parsed(true);
} catch (std::exception &) {
}
})
PARSER(BoolFlag, bool, {
std::string copy(arg);
std::transform(copy.begin(), copy.end(), copy.begin(),
[](const char c) { return std::tolower(c); });
if (copy == "false")
value = false;
else
value = true;
})
Parser::Parser(const std::string &prefix, const std::string &help_text)
: prefix(prefix), help_text(help_text) {
set_parser<std::string>(flag_constructor_t(StringFlag::make));
set_parser<int>(flag_constructor_t(IntFlag::make));
set_parser<long int>(flag_constructor_t(LongIntFlag::make));
set_parser<long long int>(flag_constructor_t(LongLongIntFlag::make));
set_parser<unsigned long int>(flag_constructor_t(UnsignedLongIntFlag::make));
set_parser<unsigned long long int>(
flag_constructor_t(UnsignedLongLongIntFlag::make));
set_parser<float>(flag_constructor_t(FloatFlag::make));
set_parser<double>(flag_constructor_t(DoubleFlag::make));
set_parser<long double>(flag_constructor_t(LongDoubleFlag::make));
set_parser<bool>(flag_constructor_t(BoolFlag::make));
} }
bool Parser::parse(int argc, char **argv) { bool Parser::parse(int argc, char **argv) {
@ -22,7 +126,7 @@ bool Parser::parse(int argc, char **argv) {
std::vector<std::string> args; std::vector<std::string> args;
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
std::string arg(argv[i]); std::string arg(argv[i]);
if (starts_with(arg, prefix) && arg.length() > prefix.length()) { if (starts_with(arg, prefix) && arg.size() > prefix.size()) {
std::vector<std::string> parts = split(arg, '='); std::vector<std::string> parts = split(arg, '=');
args.insert(args.end(), parts.begin(), parts.end()); args.insert(args.end(), parts.begin(), parts.end());
} else { } else {
@ -33,27 +137,27 @@ bool Parser::parse(int argc, char **argv) {
// search for flags // search for flags
for (size_t i = 1; i < args.size() - 1; i++) { for (size_t i = 1; i < args.size() - 1; i++) {
const std::string &arg = args[i]; const std::string &arg = args[i];
if (starts_with(arg, prefix) && arg.length() > prefix.length()) { if (starts_with(arg, prefix) && arg.size() > prefix.size()) {
flags[arg.substr(prefix.length())]->parse(args[i + 1]); flags[arg.substr(prefix.size())]->parse(args[i + 1]);
} }
} }
// check for the last argument // check for the last argument
if (starts_with(args[args.size() - 1], prefix) && if (starts_with(args[args.size() - 1], prefix) &&
args[args.size() - 1].length() > prefix.length()) { args[args.size() - 1].size() > prefix.size()) {
flags[args[args.size() - 1].substr(prefix.length())]->parse(""); flags[args[args.size() - 1].substr(prefix.size())]->parse("");
} }
return get_found() == flags.size(); return get_found() == flags.size();
} }
const unsigned Parser::get_parsed() const { unsigned Parser::get_parsed() const {
return std::count_if( return std::count_if(
flags.begin(), flags.end(), flags.begin(), flags.end(),
[](std::pair<std::string, Flag *> f) { return f.second->get_parsed(); }); [](std::pair<std::string, Flag *> f) { return f.second->get_parsed(); });
} }
const unsigned Parser::get_found() const { unsigned Parser::get_found() const {
return std::count_if( return std::count_if(
flags.begin(), flags.end(), flags.begin(), flags.end(),
[](std::pair<std::string, Flag *> f) { return f.second->get_found(); }); [](std::pair<std::string, Flag *> f) { return f.second->get_found(); });

57
flags.h
View File

@ -1,9 +1,7 @@
#ifndef FLAGS_H #ifndef FLAGS_H
#define FLAGS_H #define FLAGS_H
#include <algorithm>
#include <functional> #include <functional>
#include <iostream>
#include <map> #include <map>
#include <string> #include <string>
#include <typeindex> #include <typeindex>
@ -31,41 +29,44 @@ public:
Flag(const std::string &description, const bool required) Flag(const std::string &description, const bool required)
: description(description), found(!required), parsed(false) {} : description(description), found(!required), parsed(false) {}
virtual ~Flag() {}; virtual ~Flag(){};
const std::string &get_description() const { return description; } const std::string &get_description() const { return description; }
const bool get_found() const { return found; } bool get_found() const { return found; }
const bool get_parsed() const { return parsed; } bool get_parsed() const { return parsed; }
virtual void parse(const std::string &arg) {} virtual void parse(const std::string &arg) = 0;
}; };
// type of flag based parser classes' factory functions // type of flag based parser classes' factory functions
typedef std::function<Flag *(const void *, const std::string &, const bool)> typedef std::function<Flag *(const void *, const std::string &, const bool)>
flag_constructor_t; flag_constructor_t;
class StringFlag : public Flag { // use this class to create your own parser
std::string value; template <typename T> class FlagTemplate : public Flag {
protected:
T value;
public: public:
StringFlag(const void *default_value, const std::string &description, explicit FlagTemplate<T>(const void *default_value,
const bool required) const std::string &description, const bool required)
: Flag(description, required), value(*(std::string *)default_value) {} : Flag(description, required), value(*(T *)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; }; const void *get_value_ptr() const override { return &value; };
void parse(const std::string &arg) override {
set_found(true);
set_parsed(true);
value = arg;
}
}; };
#define PARSER(name, type, func) \
class name : public FlagTemplate<type> { \
using FlagTemplate<type>::FlagTemplate; \
\
public: \
static Flag *make(const void *default_value, \
const std::string &description, const bool required) { \
return new name(default_value, description, required); \
} \
void parse(const std::string &arg) override func \
}; // namespace Flags
// flag holder, parsing orchestrator // flag holder, parsing orchestrator
class Parser { class Parser {
private: private:
@ -76,10 +77,7 @@ private:
std::map<std::type_index, flag_constructor_t> constructors; std::map<std::type_index, flag_constructor_t> constructors;
public: public:
Parser(const std::string &prefix, const std::string &help_text) Parser(const std::string &prefix, const std::string &help_text);
: prefix(prefix), help_text(help_text) {
set_parser<std::string>(flag_constructor_t(StringFlag::make));
}
Parser() : Parser("--", "Help:") {} Parser() : Parser("--", "Help:") {}
@ -89,7 +87,8 @@ public:
} }
} }
template <typename T> void set_parser(flag_constructor_t constructor_function) { template <typename T>
void set_parser(flag_constructor_t constructor_function) {
constructors[typeid(T)] = constructor_function; constructors[typeid(T)] = constructor_function;
} }
@ -101,9 +100,9 @@ public:
return (T *)flags[name]->get_value_ptr(); return (T *)flags[name]->get_value_ptr();
} }
const unsigned get_found() const; unsigned get_found() const;
const unsigned get_parsed() const; unsigned get_parsed() const;
bool parse(int argc, char **argv); bool parse(int argc, char **argv);