From d8645e87109d76b868f0d9b3cba72e2d9392df3a Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Mon, 11 Jul 2016 14:30:37 -0600 Subject: bump version, change template use --- CHANGELOG | 10 +++ Doxyfile | 2 +- README.md | 14 +++-- args.hxx | 204 +++++++++++++++++++++++++++++++++++++++----------------------- test.cxx | 30 +++++---- 5 files changed, 164 insertions(+), 96 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0dd68bc..ad62d44 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +* 6.0.0 +Change Reader to functor type, breaking change. +Change Reader functor to allow any return type, but specifically need bool-testable return for NOEXCEPT use. +Change List and Map templates into template templates to enforce proper type use and to clean up user template invocations (i.e. `args::ValueFlagList>` becomes `args::ValueFlagList`, also breaking change. + +* 5.0.0 +Implemented proper subparsers. +Added better C++11 style. +Improved documentation. + * 4.0.0 Changed all wording: diff --git a/Doxyfile b/Doxyfile index 1ba8d5e..8c76055 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "args" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 5.1.0 +PROJECT_NUMBER = 6.0.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/README.md b/README.md index 966eabd..b08f9f2 100644 --- a/README.md +++ b/README.md @@ -391,13 +391,15 @@ std::istream& operator>>(std::istream& is, std::tuple& ints) #include -bool DoublesReader(const std::string &name, const std::string &value, std::tuple &destination) +struct DoublesReader { - size_t commapos = 0; - std::get<0>(destination) = std::stod(value, &commapos); - std::get<1>(destination) = std::stod(std::string(value, commapos + 1)); - return true; -} + void operator()(const std::string &name, const std::string &value, std::tuple &destination) + { + size_t commapos = 0; + std::get<0>(destination) = std::stod(value, &commapos); + std::get<1>(destination) = std::stod(std::string(value, commapos + 1)); + } +}; int main(int argc, char **argv) { diff --git a/args.hxx b/args.hxx index f2a45b9..fa38aab 100644 --- a/args.hxx +++ b/args.hxx @@ -1510,29 +1510,32 @@ namespace args } }; - /** A default Reader function for argument classes + /** A default Reader class for argument classes * * Simply uses a std::istringstream to read into the destination type, and * raises a ParseError if there are any characters left. */ template - bool ValueReader(const std::string &name, const std::string &value, T &destination) + struct ValueReader { - std::istringstream ss(value); - ss >> destination; - - if (ss.rdbuf()->in_avail() > 0) + bool operator ()(const std::string &name, const std::string &value, T &destination) { + std::istringstream ss(value); + ss >> destination; + + if (ss.rdbuf()->in_avail() > 0) + { #ifdef ARGS_NOEXCEPT - return false; + return false; #else - std::ostringstream problem; - problem << "Argument '" << name << "' received invalid value type '" << value << "'"; - throw ParseError(problem.str()); + std::ostringstream problem; + problem << "Argument '" << name << "' received invalid value type '" << value << "'"; + throw ParseError(problem.str()); #endif + } + return true; } - return true; - } + }; /** std::string specialization for ValueReader * @@ -1540,22 +1543,28 @@ namespace args * it is more efficient to ust copy a string into the destination. */ template <> - bool ValueReader(const std::string &name, const std::string &value, std::string &destination) + struct ValueReader { - destination.assign(value); - return true; - } + bool operator()(const std::string &name, const std::string &value, std::string &destination) + { + destination.assign(value); + return true; + } + }; /** An argument-accepting flag class * * \tparam T the type to extract the argument as - * \tparam Reader The function used to read the argument, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) */ - template > + template < + typename T, + typename Reader = ValueReader> class ValueFlag : public ValueFlagBase { private: T value; + Reader reader; public: @@ -1568,12 +1577,14 @@ namespace args virtual void ParseValue(const std::string &value) override { - if (!Reader(name, value, this->value)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, this->value)) + { error = Error::Parse; -#endif } +#else + reader(name, value, this->value); +#endif } /** Get the value @@ -1588,20 +1599,21 @@ namespace args * * \tparam T the type to extract the argument as * \tparam List the list type that houses the values - * \tparam Reader The function used to read the argument, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) */ template < typename T, - typename List = std::vector, - bool (*Reader)(const std::string &, const std::string &, T&) = ValueReader> + template class List = std::vector, + typename Reader = ValueReader> class ValueFlagList : public ValueFlagBase { private: - List values; + List values; + Reader reader; public: - ValueFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const List &defaultValues = List()): ValueFlagBase(name, help, std::move(matcher)), values(defaultValues) + ValueFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const List &defaultValues = List()): ValueFlagBase(name, help, std::move(matcher)), values(defaultValues) { group.Add(*this); } @@ -1611,18 +1623,20 @@ namespace args virtual void ParseValue(const std::string &value) override { T v; - if (!Reader(name, value, v)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, v)) + { error = Error::Parse; -#endif } +#else + reader(name, value, v); +#endif values.insert(std::end(values), v); } /** Get the values */ - List &Get() noexcept + List &Get() noexcept { return values; } @@ -1637,19 +1651,24 @@ namespace args * * \tparam K the type to extract the argument as * \tparam T the type to store the result as - * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) * \tparam Map The Map type. Should operate like std::map or std::unordered_map */ - template , typename Map = std::unordered_map> + template < + typename K, + typename T, + typename Reader = ValueReader, + template class Map = std::unordered_map> class MapFlag : public ValueFlagBase { private: - const Map map; + const Map map; T value; + Reader reader; public: - MapFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const T &defaultValue = T(), const bool extraError = false): ValueFlagBase(name, help, std::move(matcher), extraError), map(map), value(defaultValue) + MapFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const T &defaultValue = T(), const bool extraError = false): ValueFlagBase(name, help, std::move(matcher), extraError), map(map), value(defaultValue) { group.Add(*this); } @@ -1659,12 +1678,14 @@ namespace args virtual void ParseValue(const std::string &value) override { K key; - if (!Reader(name, value, key)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, key)) + { error = Error::Parse; -#endif } +#else + reader(name, value, key); +#endif auto it = map.find(key); if (it == std::end(map)) { @@ -1694,19 +1715,25 @@ namespace args * \tparam K the type to extract the argument as * \tparam T the type to store the result as * \tparam List the list type that houses the values - * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) * \tparam Map The Map type. Should operate like std::map or std::unordered_map */ - template , bool (*Reader)(const std::string &, const std::string &, K&) = ValueReader, typename Map = std::unordered_map> + template < + typename K, + typename T, + template class List = std::vector, + typename Reader = ValueReader, + template class Map = std::unordered_map> class MapFlagList : public ValueFlagBase { private: - const Map map; - List values; + const Map map; + List values; + Reader reader; public: - MapFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const List &defaultValues = List()): ValueFlagBase(name, help, std::move(matcher)), map(map), values(defaultValues) + MapFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const List &defaultValues = List()): ValueFlagBase(name, help, std::move(matcher)), map(map), values(defaultValues) { group.Add(*this); } @@ -1716,12 +1743,14 @@ namespace args virtual void ParseValue(const std::string &value) override { K key; - if (!Reader(name, value, key)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, key)) + { error = Error::Parse; -#endif } +#else + reader(name, value, key); +#endif auto it = map.find(key); if (it == std::end(map)) { @@ -1740,7 +1769,7 @@ namespace args /** Get the value */ - List &Get() noexcept + List &Get() noexcept { return values; } @@ -1754,13 +1783,16 @@ namespace args /** A positional argument class * * \tparam T the type to extract the argument as - * \tparam Reader The function used to read the argument, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) */ - template > + template < + typename T, + typename Reader = ValueReader> class Positional : public PositionalBase { private: T value; + Reader reader; public: Positional(Group &group, const std::string &name, const std::string &help, const T &defaultValue = T()): PositionalBase(name, help), value(defaultValue) { @@ -1771,12 +1803,14 @@ namespace args virtual void ParseValue(const std::string &value) override { - if (!Reader(name, value, this->value)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, this->value)) + { error = Error::Parse; -#endif } +#else + reader(name, value, this->value); +#endif ready = false; matched = true; } @@ -1793,19 +1827,20 @@ namespace args * * \tparam T the type to extract the argument as * \tparam List the list type that houses the values - * \tparam Reader The function used to read the argument, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) */ template < typename T, - typename List = std::vector, - bool (*Reader)(const std::string &, const std::string &, T&) = ValueReader> + template class List = std::vector, + typename Reader = ValueReader> class PositionalList : public PositionalBase { private: - List values; + List values; + Reader reader; public: - PositionalList(Group &group, const std::string &name, const std::string &help, const List &defaultValues = List()): PositionalBase(name, help), values(defaultValues) + PositionalList(Group &group, const std::string &name, const std::string &help, const List &defaultValues = List()): PositionalBase(name, help), values(defaultValues) { group.Add(*this); } @@ -1815,12 +1850,14 @@ namespace args virtual void ParseValue(const std::string &value) override { T v; - if (!Reader(name, value, v)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, v)) + { error = Error::Parse; -#endif } +#else + reader(name, value, v); +#endif values.insert(std::end(values), v); matched = true; } @@ -1832,7 +1869,7 @@ namespace args /** Get the values */ - List &Get() noexcept + List &Get() noexcept { return values; } @@ -1842,19 +1879,24 @@ namespace args * * \tparam K the type to extract the argument as * \tparam T the type to store the result as - * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) * \tparam Map The Map type. Should operate like std::map or std::unordered_map */ - template , typename Map = std::unordered_map> + template < + typename K, + typename T, + typename Reader = ValueReader, + template class Map = std::unordered_map> class MapPositional : public PositionalBase { private: - const Map map; + const Map map; T value; + Reader reader; public: - MapPositional(Group &group, const std::string &name, const std::string &help, const Map &map, const T &defaultValue = T()): PositionalBase(name, help), map(map), value(defaultValue) + MapPositional(Group &group, const std::string &name, const std::string &help, const Map &map, const T &defaultValue = T()): PositionalBase(name, help), map(map), value(defaultValue) { group.Add(*this); } @@ -1864,12 +1906,14 @@ namespace args virtual void ParseValue(const std::string &value) override { K key; - if (!Reader(name, value, key)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, key)) + { error = Error::Parse; -#endif } +#else + reader(name, value, key); +#endif auto it = map.find(key); if (it == std::end(map)) { @@ -1901,19 +1945,25 @@ namespace args * \tparam K the type to extract the argument as * \tparam T the type to store the result as * \tparam List the list type that houses the values - * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference + * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) * \tparam Map The Map type. Should operate like std::map or std::unordered_map */ - template , bool (*Reader)(const std::string &, const std::string &, K&) = ValueReader, typename Map = std::unordered_map> + template < + typename K, + typename T, + template class List = std::vector, + typename Reader = ValueReader, + template class Map = std::unordered_map> class MapPositionalList : public PositionalBase { private: - const Map map; - List values; + const Map map; + List values; + Reader reader; public: - MapPositionalList(Group &group, const std::string &name, const std::string &help, const Map &map, const List &defaultValues = List()): PositionalBase(name, help), map(map), values(defaultValues) + MapPositionalList(Group &group, const std::string &name, const std::string &help, const Map &map, const List &defaultValues = List()): PositionalBase(name, help), map(map), values(defaultValues) { group.Add(*this); } @@ -1923,12 +1973,14 @@ namespace args virtual void ParseValue(const std::string &value) override { K key; - if (!Reader(name, value, key)) - { #ifdef ARGS_NOEXCEPT + if (!reader(name, value, key)) + { error = Error::Parse; -#endif } +#else + reader(name, value, key); +#endif auto it = map.find(key); if (it == std::end(map)) { @@ -1948,7 +2000,7 @@ namespace args /** Get the value */ - List &Get() noexcept + List &Get() noexcept { return values; } diff --git a/test.cxx b/test.cxx index b4cc9fc..8d77bf4 100644 --- a/test.cxx +++ b/test.cxx @@ -142,7 +142,7 @@ TEST_CASE("Argument flag lists work as expected", "[args]") TEST_CASE("Argument flag lists work with sets", "[args]") { args::ArgumentParser parser("This is a test program.", "This goes after the options."); - args::ValueFlagList> foo(parser, "FOO", "test flag", {'f', "foo"}); + args::ValueFlagList foo(parser, "FOO", "test flag", {'f', "foo"}); parser.ParseArgs(std::vector{"--foo=7", "-fblah", "-f", "9", "--foo", "blah"}); REQUIRE((args::get(foo) == std::unordered_set{"7", "9", "blah"})); } @@ -165,7 +165,7 @@ TEST_CASE("Positional arguments and positional argument lists work as expected", TEST_CASE("Positional lists work with sets", "[args]") { args::ArgumentParser parser("This is a test program.", "This goes after the options."); - args::PositionalList> foo(parser, "FOO", "test positional"); + args::PositionalList foo(parser, "FOO", "test positional"); parser.ParseArgs(std::vector{"foo", "FoO", "bar", "baz", "foo", "9", "baz"}); REQUIRE((args::get(foo) == std::unordered_set{"foo", "FoO", "bar", "baz", "9"})); } @@ -251,13 +251,15 @@ TEST_CASE("Argument groups should nest", "[args]") REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"-a", "-dg"}), args::ValidationError); } -bool DoublesReader(const std::string &name, const std::string &value, std::tuple &destination) +struct DoublesReader { - size_t commapos = 0; - std::get<0>(destination) = std::stod(value, &commapos); - std::get<1>(destination) = std::stod(std::string(value, commapos + 1)); - return true; -} + void operator()(const std::string &name, const std::string &value, std::tuple &destination) + { + size_t commapos = 0; + std::get<0>(destination) = std::stod(value, &commapos); + std::get<1>(destination) = std::stod(std::string(value, commapos + 1)); + } +}; TEST_CASE("Custom types work", "[args]") { @@ -376,12 +378,14 @@ enum class MappingEnum #include #include -bool ToLowerReader(const std::string &name, const std::string &value, std::string &destination) +struct ToLowerReader { - destination = value; - std::transform(destination.begin(), destination.end(), destination.begin(), ::tolower); - return true; -} + void operator()(const std::string &name, const std::string &value, std::string &destination) + { + destination = value; + std::transform(destination.begin(), destination.end(), destination.begin(), ::tolower); + } +}; TEST_CASE("Mapping types work as needed", "[args]") { -- cgit v1.2.1