diff options
Diffstat (limited to 'args.hxx')
| -rw-r--r-- | args.hxx | 417 | 
1 files changed, 283 insertions, 134 deletions
| @@ -82,9 +82,6 @@ namespace args              Base(const std::string &help) : matched(false), help(help) {}              virtual ~Base() {} -            virtual Base *Match(const std::string &arg) = 0; -            virtual Base *Match(const char arg) = 0; -              virtual bool Matched() const noexcept              {                  return matched; @@ -92,51 +89,157 @@ namespace args              operator bool() const noexcept              { -                return matched; +                return Matched(); +            } +    }; + +    // Named arguments, not including groups +    class NamedBase : public Base +    { +        protected: +            const std::string name; + +        public: +            NamedBase(const std::string &name, const std::string &help) : Base(help), name(name) {} +            virtual ~NamedBase() {} + +            const std::string &Name(); +    }; + +    // Base class for flag arguments +    class FlagBase : public NamedBase +    { +        protected: +            const Matcher matcher; + +        public: +            FlagBase(const std::string &name, const std::string &help, const Matcher &matcher) : NamedBase(name, help), matcher(matcher) {} +            FlagBase(const std::string &name, const std::string &help, Matcher &&matcher) : NamedBase(name, help), matcher(std::move(matcher)) {} + +            virtual ~FlagBase() {} + +            virtual FlagBase *Match(const std::string &arg) +            { +                if (matcher.Match(arg)) +                { +                    matched = true; +                    return this; +                } +                return nullptr; +            } + +            virtual FlagBase *Match(const char arg) +            { +                if (matcher.Match(arg)) +                { +                    matched = true; +                    return this; +                } +                return nullptr;              }      };      // Base class that takes arguments -    class ArgBase : public Base +    class ArgFlagBase : public FlagBase +    { +        public: +            ArgFlagBase(const std::string &name, const std::string &help, const Matcher &matcher) : FlagBase(name, help, matcher) {} +            ArgFlagBase(const std::string &name, const std::string &help, Matcher &&matcher) : FlagBase(name, help, std::move(matcher)) {} +            virtual ~ArgFlagBase() {} +            virtual void ParseArg(const std::string &value) = 0; +    }; + +    // Base class for positional arguments +    class PosBase : public NamedBase      { +        protected: +            bool ready; +          public: -            ArgBase(const std::string &help) : Base(help) {} -            virtual ~ArgBase() {} -            virtual void ParseArg(const std::string &arg, const std::string &value) = 0; +            PosBase(const std::string &name, const std::string &help) : NamedBase(name, help), ready(true) {} +            virtual ~PosBase() {} + +            bool Ready() +            { +                return ready; +            } + +            virtual void ParseArg(const std::string &value) = 0;      };      class Group : public Base      {          private:              std::vector<Base*> children; -            std::function<bool(int, int)> validator; +            std::function<bool(const Group &)> validator;          public: - -            Group(const std::string &help, const std::function<bool(int, int)> &validator = Validators::DontCare) : Base(help), validator(validator) {} +            Group(const std::string &help, const std::function<bool(const Group &)> &validator = Validators::DontCare) : Base(help), validator(validator) {}              virtual ~Group() {} -            virtual Base *Match(const std::string &arg) override +            FlagBase *Match(const std::string &arg)              {                  for (Base *child: children)                  { -                    Base *match = child->Match(arg); -                    if (match) +                    FlagBase *flag = dynamic_cast<FlagBase *>(child); +                    Group *group = dynamic_cast<Group *>(child); +                    if (flag)                      { -                        return match; +                        FlagBase *match = flag->Match(arg); +                        if (match) +                        { +                            return match; +                        } +                    } else if (group) +                    { +                        FlagBase *match = group->Match(arg); +                        if (match) +                        { +                            return match; +                        } +                    } +                } +                return nullptr; +            } + +            FlagBase *Match(const char arg) +            { +                for (Base *child: children) +                { +                    FlagBase *flag = dynamic_cast<FlagBase *>(child); +                    Group *group = dynamic_cast<Group *>(child); +                    if (flag) +                    { +                        FlagBase *match = flag->Match(arg); +                        if (match) +                        { +                            return match; +                        } +                    } else if (group) +                    { +                        FlagBase *match = group->Match(arg); +                        if (match) +                        { +                            return match; +                        }                      }                  }                  return nullptr;              } -            virtual Base *Match(const char arg) override +            PosBase *GetNextPos()              {                  for (Base *child: children)                  { -                    Base *match = child->Match(arg); -                    if (match) +                    PosBase *next = dynamic_cast<PosBase *>(child); +                    Group *group = dynamic_cast<Group *>(child); +                    if (group) +                    { +                        next = group->GetNextPos(); +                    } +                    if (next and next->Ready())                      { -                        return match; +                        return next;                      }                  }                  return nullptr; @@ -147,9 +250,14 @@ namespace args                  children.emplace_back(&child);              } -            int MatchedChildren() const +            const std::vector<Base *> Children() const +            { +                return children; +            } + +            std::vector<Base *>::size_type MatchedChildren() const              { -                int sum = 0; +                std::vector<Base *>::size_type sum = 0;                  for (const Base * child: children)                  {                      if (child->Matched()) @@ -162,48 +270,67 @@ namespace args              virtual bool Matched() const noexcept override              { -                return validator(children.size(), MatchedChildren()); +                return validator(*this);              }              struct Validators              { -                static bool Xor(int children, int matched) +                static bool Xor(const Group &group)                  { -                    return matched == 1; +                    return group.MatchedChildren() == 1;                  } -                static bool AtLeastOne(int children, int matched) +                static bool AtLeastOne(const Group &group)                  { -                    return matched >= 1; +                    return group.MatchedChildren() >= 1;                  } -                static bool AtMostOne(int children, int matched) +                static bool AtMostOne(const Group &group)                  { -                    return matched <= 1; +                    return group.MatchedChildren() <= 1;                  } -                static bool All(int children, int matched) +                static bool All(const Group &group)                  { -                    return children == matched; +                    return group.Children().size() == group.MatchedChildren();                  } -                static bool DontCare(int children, int matched) +                static bool AllOrNone(const Group &group) +                { +                    return (All(group) || None(group)); +                } + +                static bool AllChildGroups(const Group &group) +                { +                    for (const auto child: group.Children()) +                    { +                        const Group *group = dynamic_cast<Group *>(child); +                        if (group && (!group->Matched())) +                        { +                            return false; +                        } +                    } +                    return true; +                } + +                static bool DontCare(const Group &group)                  {                      return true;                  } -                static bool CareTooMuch(int children, int matched) +                static bool CareTooMuch(const Group &group)                  {                      return false;                  } -                static bool None(int children, int matched) +                static bool None(const Group &group)                  { -                    return matched == 0; +                    return group.MatchedChildren() == 0;                  }              };      }; +    // Command line argument parser      class ArgumentParser      {          private: @@ -238,7 +365,12 @@ namespace args                      longprefix(longprefix),                      shortprefix(shortprefix),                      longseparator(longseparator), -                    group("arguments", Group::Validators::DontCare) {} +                    group("arguments", Group::Validators::AllChildGroups) {} + +            void Add(Base &child) +            { +                group.Add(child); +            }              void ParseArgs(const std::vector<std::string> &args)              { @@ -257,15 +389,15 @@ namespace args                              std::string(argchunk, 0, separator)                              : argchunk); -                        Base *base = group.Match(arg); +                        FlagBase *base = group.Match(arg);                          if (base)                          { -                            ArgBase *argbase = dynamic_cast<ArgBase *>(base); +                            ArgFlagBase *argbase = dynamic_cast<ArgFlagBase *>(base);                              if (argbase)                              {                                  if (separator != argchunk.npos)                                  { -                                    argbase->ParseArg(arg, argchunk.substr(separator + longseparator.size())); +                                    argbase->ParseArg(argchunk.substr(separator + longseparator.size()));                                  } else                                  {                                      ++it; @@ -276,7 +408,7 @@ namespace args                                          throw ParseError(problem.str().c_str());                                      } else                                      { -                                        argbase->ParseArg(arg, *it); +                                        argbase->ParseArg(*it);                                      }                                  }                              } else if (separator != argchunk.npos) @@ -288,9 +420,10 @@ namespace args                          } else                          {                              std::ostringstream problem; -                            problem << "Argument could not be matched: " << chunk; +                            problem << "Argument could not be matched: " << arg;                              throw ParseError(problem.str().c_str());                          } +                        // Check short args                      } else if (chunk.find(shortprefix) == 0 && chunk.size() > shortprefix.size())                      {                          std::string argchunk(chunk.substr(shortprefix.size())); @@ -301,24 +434,24 @@ namespace args                              Base *base = group.Match(arg);                              if (base)                              { -                                ArgBase *argbase = dynamic_cast<ArgBase *>(base); +                                ArgFlagBase *argbase = dynamic_cast<ArgFlagBase *>(base);                                  if (argbase)                                  {                                      argchunk.erase(std::begin(argchunk), ++argit);                                      if (!argchunk.empty())                                      { -                                        argbase->ParseArg(std::string(1, arg), argchunk); +                                        argbase->ParseArg(argchunk);                                      } else                                      {                                          ++it;                                          if (it == std::end(args))                                          {                                              std::ostringstream problem; -                                            problem << "Argument " << arg << " requires an argument but received none"; +                                            problem << "Flag '" << arg << "' requires an argument but received none";                                              throw ParseError(problem.str().c_str());                                          } else                                          { -                                            argbase->ParseArg(std::string(1, arg), *it); +                                            argbase->ParseArg(*it);                                          }                                      }                                      // Because this argchunk is done regardless @@ -327,12 +460,35 @@ namespace args                              } else                              {                                  std::ostringstream problem; -                                problem << "Argument could not be matched: " << arg; +                                problem << "Argument could not be matched: '" << arg << "'";                                  throw ParseError(problem.str().c_str());                              }                          } +                    } else +                    { +                        SetNextPositional(chunk);                      }                  } +                if (!group.Matched()) +                { +                    std::ostringstream problem; +                    problem << "Group validation failed somewhere!"; +                    throw ParseError(problem.str().c_str()); +                } +            } + +            void SetNextPositional(const std::string &arg) +            { +                PosBase *pos = group.GetNextPos(); +                if (pos) +                { +                    pos->ParseArg(arg); +                } else +                { +                    std::ostringstream problem; +                    problem << "Passed in argument, but no positional arguments were ready to receive it" << arg; +                    throw ParseError(problem.str().c_str()); +                }              }              void ParseCLI(const int argc, const char * const * const argv) @@ -351,85 +507,60 @@ namespace args      };      // Boolean argument matcher -    class Flag : public Base +    class Flag : public FlagBase      { -        private: -            const Matcher matcher; -          public: -            Flag(Group &group, const std::string &help, const Matcher &matcher): Base(help), matcher(matcher) +            Flag(Group &group, const std::string &name, const std::string &help, const Matcher &matcher): FlagBase(name, help, matcher)              {                  group.Add(*this);              } -            Flag(Group &group, const std::string &help, Matcher &&matcher): Base(help), matcher(std::move(matcher)) +            Flag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher): FlagBase(name, help, std::move(matcher))              {                  group.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; -            }      };      // Count matches -    class Counter : public Base +    class Counter : public FlagBase      {          private: -            const Matcher matcher;              unsigned int count;          public: -            Counter(Group &group, const std::string &help, const Matcher &matcher, const unsigned int startcount = 0): Base(help), matcher(matcher), count(startcount) +            Counter(Group &group, const std::string &name, const std::string &help, const Matcher &matcher, const unsigned int startcount = 0): FlagBase(name, help, matcher), count(startcount)              {                  group.Add(*this);              } -            Counter(Group &group, const std::string &help, Matcher &&matcher, const unsigned int startcount = 0): Base(help), matcher(std::move(matcher)), count(startcount) +            Counter(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const unsigned int startcount = 0): FlagBase(name, help, std::move(matcher)), count(startcount)              {                  group.Add(*this);              }              virtual ~Counter() {} -            virtual Base *Match(const std::string &arg) override +            virtual FlagBase *Match(const std::string &arg) override              { -                if (matcher.Match(arg)) +                FlagBase *me = FlagBase::Match(arg); +                if (me)                  { -                    matched = true;                      ++count; -                    return this;                  } -                return nullptr; +                return me;              } -            virtual Base *Match(const char arg) override +            virtual FlagBase *Match(const char arg) override              { -                if (matcher.Match(arg)) +                FlagBase *me = FlagBase::Match(arg); +                if (me)                  { -                    matched = true;                      ++count; -                    return this;                  } -                return nullptr; +                return me;              }              unsigned int Count() @@ -439,7 +570,7 @@ namespace args      };      template <typename T> -    void ArgReader(const std::string &arg, const std::string &value, T &destination) +    void ArgReader(const std::string &name, const std::string &value, T &destination)      {          std::istringstream ss(value);          ss >> destination; @@ -447,60 +578,39 @@ namespace args          if (ss.rdbuf()->in_avail() > 0)          {              std::ostringstream problem; -            problem << "Argument " << arg << " received invalid value type " << value; +            problem << "Argument '" << name << "' received invalid value type '" << value << "'";              throw ParseError(problem.str().c_str());          }      }      template <> -    void ArgReader<std::string>(const std::string &arg, const std::string &value, std::string &destination) +    void ArgReader<std::string>(const std::string &name, const std::string &value, std::string &destination)      {          destination.assign(value);      }      template <typename T, void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> -    class ArgFlag : public ArgBase +    class ArgFlag : public ArgFlagBase      {          private: -            const Matcher matcher;              T value;          public: -            ArgFlag(Group &group, const std::string &help, const Matcher &matcher): ArgBase(help), matcher(matcher) +            ArgFlag(Group &group, const std::string &name, const std::string &help, const Matcher &matcher): ArgFlagBase(name, help, matcher)              {                  group.Add(*this);              } -            ArgFlag(Group &group, const std::string &help, Matcher &&matcher): ArgBase(help), matcher(std::move(matcher)) +            ArgFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher): ArgFlagBase(name, help, std::move(matcher))              {                  group.Add(*this);              }              virtual ~ArgFlag() {} -            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; -            } - -            virtual void ParseArg(const std::string &arg, const std::string &value) override +            virtual void ParseArg(const std::string &value) override              { -                Reader(arg, value, this->value); +                Reader(name, value, this->value);              }              const T &Value() @@ -509,50 +619,89 @@ namespace args              }      }; -    template <typename T, typename List = std::vector<T>, void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> -    class ArgFlagList : public ArgBase +    template < +        typename T, +        typename List = std::vector<T>, +        void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> +    class ArgFlagList : public ArgFlagBase      {          private: -            const Matcher matcher;              List values;          public: -            ArgFlagList(Group &group, const std::string &help, const Matcher &matcher): ArgBase(help), matcher(matcher) +            ArgFlagList(Group &group, const std::string &name, const std::string &help, const Matcher &matcher): ArgFlagBase(name, help, matcher)              {                  group.Add(*this);              } -            ArgFlagList(Group &group, const std::string &help, Matcher &&matcher): ArgBase(help), matcher(std::move(matcher)) +            ArgFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher): ArgFlagBase(name, help, std::move(matcher))              {                  group.Add(*this);              }              virtual ~ArgFlagList() {} -            virtual Base *Match(const std::string &arg) override +            virtual void ParseArg(const std::string &value) override              { -                if (matcher.Match(arg)) -                { -                    matched = true; -                    return this; -                } -                return nullptr; +                values.emplace_back(); +                Reader(name, value, values.back());              } -            virtual Base *Match(const char arg) override +            const List &Values()              { -                if (matcher.Match(arg)) -                { -                    matched = true; -                    return this; -                } -                return nullptr; +                return values;              } +    }; + +    template <typename T, void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> +    class PosArg : public PosBase +    { +        private: +            T value; + +        public: +            PosArg(Group &group, const std::string &name, const std::string &help): PosBase(name, help) +            { +                group.Add(*this); +            } + +            virtual ~PosArg() {} + +            virtual void ParseArg(const std::string &value) override +            { +                Reader(name, value, this->value); +                ready = false; +                matched = true; +            } + +            const T &Value() +            { +                return value; +            } +    }; + +    template < +        typename T, +        typename List = std::vector<T>, +        void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> +    class PosArgList : public PosBase +    { +        private: +            List values; + +        public: +            PosArgList(Group &group, const std::string &name, const std::string &help): PosBase(name, help) +            { +                group.Add(*this); +            } + +            virtual ~PosArgList() {} -            virtual void ParseArg(const std::string &arg, const std::string &value) override +            virtual void ParseArg(const std::string &value) override              {                  values.emplace_back(); -                Reader(arg, value, values.back()); +                Reader(name, value, values.back()); +                matched = true;              }              const List &Values() | 
