From e0933dcf6b539096c6556170bc9930abe39a2c59 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Sat, 30 Apr 2016 17:41:10 -0600 Subject: more fleshing out --- args.hxx | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 194 insertions(+), 5 deletions(-) diff --git a/args.hxx b/args.hxx index 442b2a9..a82bbfa 100644 --- a/args.hxx +++ b/args.hxx @@ -2,13 +2,56 @@ * This code is released under the license described in the LICENSE file */ +#include +#include +#include +#include #include #include -#include -#include +#include namespace args { + class ParseError : public std::runtime_error + { + public: + ParseError(const char *problem) : std::runtime_error(problem) {} + virtual ~ParseError() {}; + }; + + // A class of "matchers", specifying short and long options that can possibly be matched + class Matcher + { + private: + std::vector shortOpts; + std::vector longOpts; + + public: + // Specify short and long opts separately as iterators + template + Matcher(ShortIt shortOptsStart, ShortIt shortOptsEnd, LongIt longOptsStart, LongIt longOptsEnd) : + shortOpts(shortOptsStart, shortOptsEnd), + longOpts(longOptsStart, longOptsEnd) + {} + + // Specify short and long opts separately as iterables + template + Matcher(Short &&shortIn, Long &&longIn) : + Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn)) + {} + + bool Match(const char opt) const + { + return std::find(shortOpts.begin(), shortOpts.end(), opt) != shortOpts.end(); + } + + bool Match(const std::string &opt) const + { + return std::find(longOpts.begin(), longOpts.end(), opt) != longOpts.end(); + } + }; + + // Base class for groups and individual argument types class Base { protected: @@ -18,10 +61,25 @@ namespace args Base() : matched(false) {} virtual ~Base() {} + virtual Base *Match(const std::string &arg) = 0; + virtual Base *Match(const char arg) = 0; + virtual bool Matched() const { return matched; } + + operator bool() const + { + return matched; + } + }; + + // Base class that takes arguments + class ArgBase : public Base + { + public: + virtual ~ArgBase() {} }; class Group : public Base @@ -35,6 +93,32 @@ namespace args Group(const std::function &validator = Validators::DontCare) : validator(validator) {} virtual ~Group() {} + virtual Base *Match(const std::string &arg) override + { + for (Base *child: children) + { + Base *match = child->Match(arg); + if (match) + { + return match; + } + } + return nullptr; + } + + virtual Base *Match(const char arg) override + { + for (Base *child: children) + { + Base *match = child->Match(arg); + if (match) + { + return match; + } + } + return nullptr; + } + void Add(Base &child) { children.emplace_back(&child); @@ -104,20 +188,79 @@ namespace args std::string description; std::string epilog; - Group args; + std::string longprefix; + std::string shortprefix; + + Group group; public: ArgumentParser( - const std::string &prog, const std::string &description, - const std::string &epilog = std::string()) : prog(prog), description(description), epilog(epilog) {} + const std::string &longprefix = "--", + const std::string &shortprefix = "-", + const std::string &prog = std::string(), + const std::string &epilog = std::string() + ) : + prog(prog), + description(description), + epilog(epilog), + longprefix(longprefix), + shortprefix(shortprefix), + group(Group::Validators::DontCare) {} + + void Add(Base &item) + { + group.Add(item); + } void ParseArgs(const std::vector &args) { + for (auto it = std::begin(args); it != std::end(args); ++it) + { + const std::string &chunk = *it; + if (chunk.find(longprefix) == 0 && chunk.size() > longprefix.size()) + { + const std::string argchunk(chunk.substr(longprefix.size())); + Base *base = group.Match(argchunk); + if (base) + { + // Do match logic here, specifically passing an + // argument, if necessary, to the base. Query the + // base and see if it takes an argument + } else + { + std::ostringstream problem; + problem << "Argument could not be matched: " << chunk; + throw ParseError(problem.str().c_str()); + } + } else if (chunk.find(shortprefix) == 0 && chunk.size() > shortprefix.size()) + { + const std::string argchunk(chunk.substr(shortprefix.size())); + for (const char &arg: argchunk) + { + Base *base = group.Match(arg); + if (base) + { + // Do match logic here, specifically passing an + // argument, if necessary, to the base. Query the + // base and see if it takes an argument + } else + { + std::ostringstream problem; + problem << "Argument could not be matched: " << arg; + throw ParseError(problem.str().c_str()); + } + } + } + } } void ParseCLI(const int argc, const char * const * const argv) { + if (prog.empty()) + { + prog.assign(argv[0]); + } std::vector args; for (int i = 1; i < argc; ++i) { @@ -126,4 +269,50 @@ namespace args ParseArgs(args); } }; + + // Boolean argument matcher + class Flag : public Base + { + private: + Matcher matcher; + + public: + //template + //Flag(std::string help, Types ...args): matcher(args...) {} + + template + Flag(ArgumentParser &parser, std::string help, Types&& ...args): matcher(args...) + { + parser.Add(*this); + } + + template + Flag(ArgumentParser &parser, std::string help, const std::initializer_list &shortIn, const std::initializer_list &longIn): matcher(shortIn, longIn) + { + parser.Add(*this); + } + + virtual ~Flag() {} + + virtual Base *Match(const std::string &arg) override + { + if (matcher.Match(arg)) + { + matched = true; + return this; + } + return nullptr; + } + + virtual Base *Match(const char arg) override + { + if (matcher.Match(arg)) + { + matched = true; + return this; + } + return nullptr; + } + }; + } -- cgit v1.2.1