aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylor C. Richberger <taywee@gmx.com>2017-11-19 09:49:53 -0700
committerGitHub <noreply@github.com>2017-11-19 09:49:53 -0700
commit431997da39a678b1aad0b85a5aacabd70120d513 (patch)
tree452b897a311e5d6f190ad33979ecdb00092fba55
parentfix ValueReader for types assignable to std::string (diff)
parentMerge pull request #45 from pavel-belikov/readme (diff)
downloadargs.hxx-431997da39a678b1aad0b85a5aacabd70120d513.tar.xz
Merge branch 'master' into better-value-parsing
-rw-r--r--README.md185
-rw-r--r--args.hxx36
-rw-r--r--test.cxx21
3 files changed, 232 insertions, 10 deletions
diff --git a/README.md b/README.md
index c89804a..f3a35d8 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,11 @@
#### Note that this library is essentially in maintenance mode. I haven't had the time to work on it or give it the love that it deserves. I'm not adding new features, but I will fix bugs. I will also very gladly accept pull requests.
-[![build status](https://travis-ci.org/Taywee/args.svg?branch=master)](https://travis-ci.org/Taywee/args)
-[![Build status](https://ci.appveyor.com/api/projects/status/nlnlmpttdjlndyc2?svg=true)](https://ci.appveyor.com/project/Taywee/args)
+[![Cpp Standard](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B11)
+[![Travis status](https://travis-ci.org/Taywee/args.svg?branch=master)](https://travis-ci.org/Taywee/args)
+[![AppVeyor status](https://ci.appveyor.com/api/projects/status/nlnlmpttdjlndyc2?svg=true)](https://ci.appveyor.com/project/Taywee/args)
[![Coverage Status](https://coveralls.io/repos/github/Taywee/args/badge.svg?branch=master)](https://coveralls.io/github/Taywee/args?branch=master)
+[![Read the Docs](https://img.shields.io/readthedocs/pip.svg)](https://taywee.github.io/args)
A simple, small, flexible, single-header C++11 argument parsing library, in
fewer than 2K lines of code.
@@ -40,10 +42,10 @@ describe the usage, as it's built to push the boundaries.
It:
-* lets you handle flags, flag+value, and positional arguments simply and
+* Lets you handle flags, flag+value, and positional arguments simply and
elegently, with the full help of static typechecking.
-* allows you to use your own types in a pretty simple way.
-* lets you use count flags, and lists of all argument-accepting types.
+* Allows you to use your own types in a pretty simple way.
+* Lets you use count flags, and lists of all argument-accepting types.
* Allows full validation of groups of required arguments, though output isn't
pretty when something fails group validation. User validation functions are
accepted. Groups are fully nestable.
@@ -56,12 +58,11 @@ It:
* Lets you decide not to allow separate-argument value flags or joined ones
(like disallowing `--foo bar`, requiring `--foo=bar`, or the inverse, or the
same for short options).
-* Allows you to create subparsers somewhat like argparse, through the use of
- kick-out arguments (check the gitlike.cxx example program for a simple sample
- of this)
-* Allow one value flag to take a specific number of values (like `--foo first
+* Allows you to create subparsers, to reuse arguments for multiple commands and
+ to refactor your command's logic to a function or lambda
+* Allows one value flag to take a specific number of values (like `--foo first
second`, where --foo slurps both arguments).
-* Allow you to have value flags only optionally accept values
+* Allows you to have value flags only optionally accept values
# What does it not do?
@@ -377,6 +378,170 @@ Argument 'numbers' received invalid value type 'a'
...
```
+## Commands
+
+```cpp
+#include <iostream>
+#include <args.hxx>
+int main(int argc, char **argv)
+{
+ args::ArgumentParser p("git-like parser");
+ args::Group commands(p, "commands");
+ args::Command add(commands, "add", "add file contents to the index");
+ args::Command commit(commands, "commit", "record changes to the repository");
+ args::Group arguments(p, "arguments", args::Group::Validators::DontCare, args::Options::Global);
+ args::ValueFlag<std::string> gitdir(arguments, "path", "", {"git-dir"});
+ args::HelpFlag h(arguments, "help", "help", {'h', "help"});
+ args::PositionalList<std::string> pathsList(arguments, "paths", "files to commit");
+
+ try
+ {
+ p.ParseCLI(argc, argv);
+ if (add)
+ {
+ std::cout << "Add";
+ }
+ else
+ {
+ std::cout << "Commit";
+ }
+
+ for (auto &&path : pathsList)
+ {
+ std::cout << ' ' << path;
+ }
+
+ std::cout << std::endl;
+ }
+ catch (args::Help)
+ {
+ std::cout << p;
+ }
+ catch (args::Error& e)
+ {
+ std::cerr << e.what() << std::endl << p;
+ return 1;
+ }
+ return 0;
+}
+```
+
+```shell
+% ./test -h
+ ./test COMMAND [paths...] {OPTIONS}
+
+ git-like parser
+
+ OPTIONS:
+
+ commands
+ add add file contents to the index
+ commit record changes to the repository
+ arguments
+ --git-dir=[path]
+ -h, --help help
+ paths... files
+ "--" can be used to terminate flag options and force all following
+ arguments to be treated as positional options
+
+% ./test add 1 2
+Add 1 2
+```
+
+## Refactoring commands
+
+```cpp
+#include <iostream>
+#include "args.hxx"
+
+args::Group arguments("arguments");
+args::ValueFlag<std::string> gitdir(arguments, "path", "", {"git-dir"});
+args::HelpFlag h(arguments, "help", "help", {'h', "help"});
+args::PositionalList<std::string> pathsList(arguments, "paths", "files to commit");
+
+void CommitCommand(args::Subparser &parser)
+{
+ args::ValueFlag<std::string> message(parser, "MESSAGE", "commit message", {'m'});
+ parser.Parse();
+
+ std::cout << "Commit";
+
+ for (auto &&path : pathsList)
+ {
+ std::cout << ' ' << path;
+ }
+
+ std::cout << std::endl;
+
+ if (message)
+ {
+ std::cout << "message: " << args::get(message) << std::endl;
+ }
+}
+
+int main(int argc, const char **argv)
+{
+ args::ArgumentParser p("git-like parser");
+ args::Group commands(p, "commands");
+ args::Command add(commands, "add", "add file contents to the index", [&](args::Subparser &parser)
+ {
+ parser.Parse();
+ std::cout << "Add";
+
+ for (auto &&path : pathsList)
+ {
+ std::cout << ' ' << path;
+ }
+
+ std::cout << std::endl;
+ });
+
+ args::Command commit(commands, "commit", "record changes to the repository", &CommitCommand);
+ args::GlobalOptions globals(p, arguments);
+
+ try
+ {
+ p.ParseCLI(argc, argv);
+ }
+ catch (args::Help)
+ {
+ std::cout << p;
+ }
+ catch (args::Error& e)
+ {
+ std::cerr << e.what() << std::endl << p;
+ return 1;
+ }
+ return 0;
+}
+```
+
+```shell
+% ./test -h
+ ./test COMMAND [paths...] {OPTIONS}
+
+ git-like parser
+
+ OPTIONS:
+
+ commands
+ add add file contents to the index
+ commit record changes to the repository
+ arguments
+ --git-dir=[path]
+ -h, --help help
+ paths... files
+ "--" can be used to terminate flag options and force all following
+ arguments to be treated as positional options
+
+% ./test add 1 2
+Add 1 2
+
+% ./test commit -m "my commit message" 1 2
+Commit 1 2
+message: my commit message
+```
+
# Custom type parsers (here we use std::tuple)
```cpp
diff --git a/args.hxx b/args.hxx
index 5169d13..b45b912 100644
--- a/args.hxx
+++ b/args.hxx
@@ -2577,6 +2577,42 @@ namespace args
}
};
+ /** A flag class that calls a function when it's matched
+ */
+ class ActionFlag : public FlagBase
+ {
+ private:
+ std::function<void(const std::vector<std::string> &)> action;
+ Nargs nargs;
+
+ public:
+ ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function<void(const std::vector<std::string> &)> action_, Options options_ = {}):
+ FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
+ {
+ group_.Add(*this);
+ }
+
+ ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
+ FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
+ {
+ group_.Add(*this);
+ action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
+ }
+
+ ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
+ FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
+ {
+ group_.Add(*this);
+ action = [action_](const std::vector<std::string> &) { return action_(); };
+ }
+
+ virtual Nargs NumberOfArguments() const noexcept override
+ { return nargs; }
+
+ virtual void ParseValue(const std::vector<std::string> &value) override
+ { action(value); }
+ };
+
/** A default Reader class for argument classes
*
* If destination type is assignable to std::string it uses an assignment to std::string.
diff --git a/test.cxx b/test.cxx
index c73c692..46a7b4c 100644
--- a/test.cxx
+++ b/test.cxx
@@ -1097,6 +1097,27 @@ TEST_CASE("ValueParser works as expected", "[args]")
REQUIRE_NOTHROW(p.ParseArgs(std::vector<std::string>{"-i", " 12"}));
REQUIRE(args::get(i) == 12);
+
+TEST_CASE("ActionFlag works as expected", "[args]")
+{
+ args::ArgumentParser p("parser");
+ std::string s;
+
+ args::ActionFlag action0(p, "name", "description", {'x'}, [&]() { s = "flag"; });
+ args::ActionFlag action1(p, "name", "description", {'y'}, [&](const std::string &arg) { s = arg; });
+ args::ActionFlag actionN(p, "name", "description", {'z'}, 2, [&](const std::vector<std::string> &arg) { s = arg[0] + arg[1]; });
+ args::ActionFlag actionThrow(p, "name", "description", {'v'}, [&]() { throw std::runtime_error(""); });
+
+ p.ParseArgs(std::vector<std::string>{"-x"});
+ REQUIRE(s == "flag");
+
+ p.ParseArgs(std::vector<std::string>{"-y", "a"});
+ REQUIRE(s == "a");
+
+ p.ParseArgs(std::vector<std::string>{"-z", "a", "b"});
+ REQUIRE(s == "ab");
+
+ REQUIRE_THROWS_AS(p.ParseArgs(std::vector<std::string>{"-v"}), std::runtime_error);
}
#undef ARGS_HXX