diff options
-rw-r--r-- | README.md | 26 | ||||
-rw-r--r-- | args.hxx | 108 | ||||
-rw-r--r-- | test.cxx | 68 |
3 files changed, 144 insertions, 58 deletions
@@ -176,9 +176,9 @@ int main() args::ArgFlagList<char> characters(parser, "characters", "The character flag", args::Matcher{'c', "char"}); args::PosArgList<double> numbers(parser, "numbers", "The numbers position list"); parser.ParseArgs(arguments); - const int i = integer.value; - const std::vector<char> c(characters.values); - const std::vector<double> n(numbers.values); + const int i = args::get(integer); + const std::vector<char> c(args::get(characters)); + const std::vector<double> n(args::get(numbers)); assert(i == 7); assert(c[0] == 'a'); assert(c[1] == 'b'); @@ -432,10 +432,10 @@ int main(int argc, char **argv) std::cerr << parser; return 1; } - if (integer) { std::cout << "i: " << integer.value << std::endl; } - if (characters) { for (const auto ch: characters.values) { std::cout << "c: " << ch << std::endl; } } - if (foo) { std::cout << "f: " << foo.value << std::endl; } - if (numbers) { for (const auto nm: numbers.values) { std::cout << "n: " << nm << std::endl; } } + if (integer) { std::cout << "i: " << args::get(integer) << std::endl; } + if (characters) { for (const auto ch: args::get(characters)) { std::cout << "c: " << ch << std::endl; } } + if (foo) { std::cout << "f: " << args::get(foo) << std::endl; } + if (numbers) { for (const auto nm: args::get(numbers)) { std::cout << "n: " << nm << std::endl; } } return 0; } ``` @@ -528,11 +528,11 @@ int main(int argc, char **argv) } if (ints) { - std::cout << "ints found: " << std::get<0>(ints.value) << " and " << std::get<1>(ints.value) << std::endl; + std::cout << "ints found: " << std::get<0>(args::get(ints)) << " and " << std::get<1>(args::get(ints)) << std::endl; } if (doubles) { - std::cout << "doubles found: " << std::get<0>(doubles.value) << " and " << std::get<1>(doubles.value) << std::endl; + std::cout << "doubles found: " << std::get<0>(args::get(doubles)) << " and " << std::get<1>(args::get(doubles)) << std::endl; } return 0; } @@ -720,10 +720,10 @@ int main(int argc, char **argv) std::cerr << parser; return 1; } - std::cout << "bs = " << bs.value << std::endl; - std::cout << "skip = " << skip.value << std::endl; - if (input) { std::cout << "if = " << input.value << std::endl; } - if (output) { std::cout << "of = " << output.value << std::endl; } + std::cout << "bs = " << args::get(bs) << std::endl; + std::cout << "skip = " << args::get(skip) << std::endl; + if (input) { std::cout << "if = " << args::get(input) << std::endl; } + if (output) { std::cout << "of = " << args::get(output) << std::endl; } return 0; } ``` @@ -27,9 +27,18 @@ #include <tuple> #include <vector> #include <unordered_set> +#include <type_traits> namespace args { + /** Getter to grab the value reference + */ + template <typename Arg> + auto get(Arg &arg) -> decltype(arg.Get()) + { + return arg.Get(); + } + /** (INTERNAL) Count UTF-8 glyphs * * This is not reliable, and will fail for combinatory glyphs, but it's @@ -613,6 +622,13 @@ namespace args return validator(*this); } + /** Get validation + */ + bool Get() const + { + return Matched(); + } + /** Get all the child descriptions for help generation */ std::vector<std::tuple<std::string, std::string, unsigned int>> GetChildDescriptions(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator, unsigned int indent = 0) const @@ -1199,6 +1215,13 @@ namespace args } virtual ~Flag() {} + + /** Get whether this was matched + */ + bool Get() const + { + return Matched(); + } }; /** Help flag class @@ -1229,17 +1252,25 @@ namespace args } return nullptr; } + + /** Get whether this was matched + */ + bool Get() const noexcept + { + return Matched(); + } }; /** A flag class that simply counts the number of times it's matched */ class Counter : public Flag { - public: - /** The public count variable. Can be changed at will, but probably shouldn't be. - */ + friend int get(Counter &); + + private: int count; + public: Counter(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const int startcount = 0): Flag(group, name, help, std::move(matcher)), count(startcount) {} virtual ~Counter() {} @@ -1263,6 +1294,13 @@ namespace args } return me; } + + /** Get the count + */ + int &Get() noexcept + { + return count; + } }; /** A default Reader function for argument classes @@ -1303,13 +1341,13 @@ namespace args template <typename T, void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> class ArgFlag : public ArgFlagBase { - public: - /** The publicly accessible value member - * - * You can change this, but you probably shouldn't. - */ + friend T &get(ArgFlag &); + + private: T value; + public: + ArgFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const T &defaultValue = T()): ArgFlagBase(name, help, std::move(matcher)), value(defaultValue) { group.Add(*this); @@ -1321,6 +1359,13 @@ namespace args { Reader(name, value, this->value); } + + /** Get the value + */ + T &Get() noexcept + { + return value; + } }; /** An argument-accepting flag class that pushes the found values into a list @@ -1335,13 +1380,12 @@ namespace args void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> class ArgFlagList : public ArgFlagBase { - public: - /** The publicly accessible value member list - * - * You can change this, but you probably shouldn't. - */ + friend List &get(ArgFlagList &); + private: List values; + public: + ArgFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const List &defaultValues = List()): ArgFlagBase(name, help, std::move(matcher)), values(defaultValues) { group.Add(*this); @@ -1354,6 +1398,13 @@ namespace args values.emplace_back(); Reader(name, value, values.back()); } + + /** Get the values + */ + List &Get() noexcept + { + return values; + } }; /** A positional argument class @@ -1364,13 +1415,10 @@ namespace args template <typename T, void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> class PosArg : public PosBase { - public: - /** The publicly accessible value member - * - * You can change this, but you probably shouldn't. - */ + friend T &get(PosArg &); + private: T value; - + public: PosArg(Group &group, const std::string &name, const std::string &help, const T &defaultValue = T()): PosBase(name, help), value(defaultValue) { group.Add(*this); @@ -1384,6 +1432,13 @@ namespace args ready = false; matched = true; } + + /** Get the value + */ + T &Get() noexcept + { + return value; + } }; /** A positional argument class that pushes the found values into a list @@ -1398,13 +1453,11 @@ namespace args void (*Reader)(const std::string &, const std::string &, T&) = ArgReader<T>> class PosArgList : public PosBase { - public: - /** The publicly accessible value member list - * - * You can change this, but you probably shouldn't. - */ + friend List &get(PosArgList &); + private: List values; + public: PosArgList(Group &group, const std::string &name, const std::string &help, const List &defaultValues = List()): PosBase(name, help), values(defaultValues) { group.Add(*this); @@ -1423,5 +1476,12 @@ namespace args { return name + std::string("..."); } + + /** Get the values + */ + List &Get() noexcept + { + return values; + } }; } @@ -49,6 +49,21 @@ TEST_CASE("Boolean flags work as expected, with clustering", "[args]") REQUIRE_FALSE(bix); } +TEST_CASE("Count flag works as expected", "[args]") +{ + args::ArgumentParser parser("This is a test program.", "This goes after the options."); + args::Counter foo(parser, "FOO", "test flag", args::Matcher{'f', "foo"}); + args::Counter bar(parser, "BAR", "test flag", args::Matcher{'b', "bar"}, 7); + args::Counter baz(parser, "BAZ", "test flag", args::Matcher{'z', "baz"}, 7); + parser.ParseArgs(std::vector<std::string>{"--foo", "-fb", "--bar", "-b", "-f", "--foo"}); + REQUIRE(foo); + REQUIRE(bar); + REQUIRE_FALSE(baz); + REQUIRE(args::get(foo) == 4); + REQUIRE(args::get(bar) == 10); + REQUIRE(args::get(baz) == 7); +} + TEST_CASE("Argument flags work as expected, with clustering", "[args]") { args::ArgumentParser parser("This is a test program.", "This goes after the options."); @@ -59,12 +74,12 @@ TEST_CASE("Argument flags work as expected, with clustering", "[args]") args::Flag bix(parser, "BAZ", "test flag", args::Matcher{'x', "bix"}); parser.ParseArgs(std::vector<std::string>{"-bftest", "--baz=7.555e2", "--bim", "c"}); REQUIRE(foo); - REQUIRE(foo.value == "test"); + REQUIRE(args::get(foo) == "test"); REQUIRE(bar); REQUIRE(baz); - REQUIRE((baz.value > 755.49 && baz.value < 755.51)); + REQUIRE((args::get(baz) > 755.49 && args::get(baz) < 755.51)); REQUIRE(bim); - REQUIRE(bim.value == 'c'); + REQUIRE(args::get(bim) == 'c'); REQUIRE_FALSE(bix); } @@ -85,15 +100,26 @@ TEST_CASE("Unified argument lists for match work", "[args]") args::Flag bix(parser, "BAZ", "test flag", args::Matcher{"bix"}); parser.ParseArgs(std::vector<std::string>{"-bftest", "--baz=7.555e2", "--bim", "c"}); REQUIRE(foo); - REQUIRE(foo.value == "test"); + REQUIRE(args::get(foo) == "test"); REQUIRE(bar); REQUIRE(baz); - REQUIRE((baz.value > 755.49 && baz.value < 755.51)); + REQUIRE((args::get(baz) > 755.49 && args::get(baz) < 755.51)); REQUIRE(bim); - REQUIRE(bim.value == 'c'); + REQUIRE(args::get(bim) == 'c'); REQUIRE_FALSE(bix); } +TEST_CASE("Get can be assigned to for non-reference types", "[args]") +{ + args::ArgumentParser parser("This is a test program.", "This goes after the options."); + args::ArgFlag<std::string> foo(parser, "FOO", "test flag", args::Matcher{'f', "foo"}); + parser.ParseArgs(std::vector<std::string>{"--foo=test"}); + REQUIRE(foo); + REQUIRE(args::get(foo) == "test"); + args::get(foo) = "bar"; + REQUIRE(args::get(foo) == "bar"); +} + TEST_CASE("Invalid argument parsing throws parsing exceptions", "[args]") { args::ArgumentParser parser("This is a test program.", "This goes after the options."); @@ -108,7 +134,7 @@ TEST_CASE("Argument flag lists work as expected", "[args]") args::ArgumentParser parser("This is a test program.", "This goes after the options."); args::ArgFlagList<int> foo(parser, "FOO", "test flag", args::Matcher{'f', "foo"}); parser.ParseArgs(std::vector<std::string>{"--foo=7", "-f2", "-f", "9", "--foo", "42"}); - REQUIRE((foo.values == std::vector<int>{7, 2, 9, 42})); + REQUIRE((args::get(foo) == std::vector<int>{7, 2, 9, 42})); } TEST_CASE("Positional arguments and positional argument lists work as expected", "[args]") @@ -119,11 +145,11 @@ TEST_CASE("Positional arguments and positional argument lists work as expected", args::PosArgList<char> baz(parser, "BAZ", "test flag"); parser.ParseArgs(std::vector<std::string>{"this is a test flag", "0", "a", "b", "c", "x", "y", "z"}); REQUIRE(foo); - REQUIRE((foo.value == "this is a test flag")); + REQUIRE((args::get(foo) == "this is a test flag")); REQUIRE(bar); - REQUIRE(!bar.value); + REQUIRE(!args::get(bar)); REQUIRE(baz); - REQUIRE((baz.values == std::vector<char>{'a', 'b', 'c', 'x', 'y', 'z'})); + REQUIRE((args::get(baz) == std::vector<char>{'a', 'b', 'c', 'x', 'y', 'z'})); } TEST_CASE("Positionals that are unspecified evaluate false", "[args]") @@ -134,7 +160,7 @@ TEST_CASE("Positionals that are unspecified evaluate false", "[args]") args::PosArgList<char> baz(parser, "BAZ", "test flag"); parser.ParseArgs(std::vector<std::string>{"this is a test flag again"}); REQUIRE(foo); - REQUIRE((foo.value == "this is a test flag again")); + REQUIRE((args::get(foo) == "this is a test flag again")); REQUIRE_FALSE(bar); REQUIRE_FALSE(baz); } @@ -225,10 +251,10 @@ TEST_CASE("Custom types work", "[args]") args::PosArg<std::tuple<int, int>> ints(parser, "INTS", "This takes a pair of integers."); args::PosArg<std::tuple<double, double>, DoublesReader> doubles(parser, "DOUBLES", "This takes a pair of doubles."); parser.ParseArgs(std::vector<std::string>{"1,2", "3.8,4"}); - REQUIRE(std::get<0>(ints.value) == 1); - REQUIRE(std::get<1>(ints.value) == 2); - REQUIRE((std::get<0>(doubles.value) > 3.79 && std::get<0>(doubles.value) < 3.81)); - REQUIRE((std::get<1>(doubles.value) > 3.99 && std::get<1>(doubles.value) < 4.01)); + REQUIRE(std::get<0>(args::get(ints)) == 1); + REQUIRE(std::get<1>(args::get(ints)) == 2); + REQUIRE((std::get<0>(args::get(doubles)) > 3.79 && std::get<0>(args::get(doubles)) < 3.81)); + REQUIRE((std::get<1>(args::get(doubles)) > 3.99 && std::get<1>(args::get(doubles)) < 4.01)); } TEST_CASE("Custom parser prefixes (dd-style)", "[args]") @@ -243,11 +269,11 @@ TEST_CASE("Custom parser prefixes (dd-style)", "[args]") args::ArgFlag<std::string> output(parser, "BLOCK SIZE", "Block size", args::Matcher({"of"})); parser.ParseArgs(std::vector<std::string>{"skip=8", "if=/dev/null"}); REQUIRE_FALSE(bs); - REQUIRE(bs.value == 512); + REQUIRE(args::get(bs) == 512); REQUIRE(skip); - REQUIRE(skip.value == 8); + REQUIRE(args::get(skip) == 8); REQUIRE(input); - REQUIRE(input.value == "/dev/null"); + REQUIRE(args::get(input) == "/dev/null"); REQUIRE_FALSE(output); } @@ -263,11 +289,11 @@ TEST_CASE("Custom parser prefixes (Some Windows styles)", "[args]") args::ArgFlag<std::string> output(parser, "BLOCK SIZE", "Block size", args::Matcher({"of"})); parser.ParseArgs(std::vector<std::string>{"/skip:8", "/if:/dev/null"}); REQUIRE_FALSE(bs); - REQUIRE(bs.value == 512); + REQUIRE(args::get(bs) == 512); REQUIRE(skip); - REQUIRE(skip.value == 8); + REQUIRE(args::get(skip) == 8); REQUIRE(input); - REQUIRE(input.value == "/dev/null"); + REQUIRE(args::get(input) == "/dev/null"); REQUIRE_FALSE(output); } |