aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylor C. Richberger <Taywee@gmx.com>2016-06-30 00:12:02 -0600
committerTaylor C. Richberger <Taywee@gmx.com>2016-06-30 00:12:02 -0600
commit1acf1d62cea4d71ed68e621893ec101264fc42e9 (patch)
treef484d454352d545dbf8fface269f7d85effdb717
parentbump minor version (diff)
downloadargs.hxx-1acf1d62cea4d71ed68e621893ec101264fc42e9.tar.xz
make the last of the v5 changes
-rw-r--r--.gitignore1
-rw-r--r--README.md293
-rw-r--r--args.hxx77
-rw-r--r--gitlike.cxx128
4 files changed, 321 insertions, 178 deletions
diff --git a/.gitignore b/.gitignore
index 58a27ec..5056399 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,3 +45,4 @@ install_manifest.txt
Makefile
!/Makefile
*-prefix
+gitlike
diff --git a/README.md b/README.md
index 662d8ee..0ac02e4 100644
--- a/README.md
+++ b/README.md
@@ -52,16 +52,16 @@ It:
* Lets you decide not to allow separate-argument argument 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 flags (check the gitlike.cxx example program for a simple sample of
+ this)
# What does it not do?
There are tons of things this library does not do!
-
## It will not ever:
-* Allow you to create subparsers like argparse in a master parser (you can do
- this yourself with iterators and multiple parsers)
* Allow one argument flag to take a specific number of arguments (like `--foo
first second`). You can instead split that with a flag list (`--foo first
--foo second`) or a custom type extraction (`--foo first,second`)
@@ -121,144 +121,18 @@ interpreted as a boolean to see if they've been matched.
All variables can be pulled (including the boolean match status) with
args::get.
-# How fast is it?
-
-This should not really be a question you ask when you are looking for an
-argument-parsing library, but every test I've done shows args as being about
-65% faster than TCLAP and 220% faster than boost::program_options.
-
-The simplest benchmark I threw together is the following one, which parses the
-command line `-i 7 -c a 2.7 --char b 8.4 -c c 8.8 --char d` with a parser that
-parses -i as an int, -c as a list of chars, and the positional parameters as a
-list of doubles (the command line was originally much more complex, but TCLAP's
-limitations made me trim it down so I could use a common command line across
-all libraries. I also have to copy in the arguments list with every run,
-because TCLAP permutes its argument list as it runs (and comparison would have
-been unfair without comparing all about equally), but that surprisingly didn't
-affect much. Also tested is pulling the arguments out, but that was fast
-compared to parsing, as would be expected.
+# Group validation is weird. How do I get more helpful output for failed validation?
-### The run:
-
-```shell
-% g++ -obench bench.cxx -O2 -std=c++11 -lboost_program_options
-% ./bench
-args seconds to run: 0.895472
-tclap seconds to run: 1.45001
-boost::program_options seconds to run: 1.98972
-%
-```
-
-### The benchmark:
-
-```cpp
-#undef NDEBUG
-#include <iostream>
-#include <chrono>
-#include <cassert>
-#include "args.hxx"
-#include <tclap/CmdLine.h>
-#include <boost/program_options.hpp>
-namespace po = boost::program_options;
-using namespace std::chrono;
-inline bool doubleequals(const double a, const double b)
-{
- static const double delta = 0.0001;
- const double diff = a - b;
- return diff < delta && diff > -delta;
-}
-int main()
-{
- const std::vector<std::string> carguments({"-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"});
- const std::vector<std::string> pcarguments({"progname", "-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"});
- // args
- {
- high_resolution_clock::time_point start = high_resolution_clock::now();
- for (unsigned int x = 0; x < 100000; ++x)
- {
- std::vector<std::string> arguments(carguments);
- args::ArgumentParser parser("This is a test program.", "This goes after the options.");
- args::ValueFlag<int> integer(parser, "integer", "The integer flag", {'i', "int"});
- args::ValueFlagList<char> characters(parser, "characters", "The character flag", {'c', "char"});
- args::PositionalList<double> numbers(parser, "numbers", "The numbers position list");
- parser.ParseArgs(arguments);
- 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');
- assert(c[2] == 'c');
- assert(c[3] == 'd');
- assert(doubleequals(n[0], 2.7));
- assert(doubleequals(n[1], 8.4));
- assert(doubleequals(n[2], 8.8));
- }
- high_resolution_clock::duration runtime = high_resolution_clock::now() - start;
- std::cout << "args seconds to run: " << duration_cast<duration<double>>(runtime).count() << std::endl;
- }
- // tclap
- {
- high_resolution_clock::time_point start = high_resolution_clock::now();
- for (unsigned int x = 0; x < 100000; ++x)
- {
- std::vector<std::string> arguments(pcarguments);
- TCLAP::CmdLine cmd("Command description message", ' ', "0.9");
- TCLAP::ValueArg<int> integer("i", "int", "The integer flag", false, 0, "integer", cmd);
- TCLAP::MultiArg<char> characters("c", "char", "The character flag", false, "characters", cmd);
- TCLAP::UnlabeledMultiArg<double> numbers("numbers", "The numbers position list", false, "foo", cmd, false);
- cmd.parse(arguments);
- const int i = integer.getValue();
- const std::vector<char> c(characters.getValue());
- const std::vector<double> n(numbers.getValue());
- assert(i == 7);
- assert(c[0] == 'a');
- assert(c[1] == 'b');
- assert(c[2] == 'c');
- assert(c[3] == 'd');
- assert(doubleequals(n[0], 2.7));
- assert(doubleequals(n[1], 8.4));
- assert(doubleequals(n[2], 8.8));
- }
- high_resolution_clock::duration runtime = high_resolution_clock::now() - start;
- std::cout << "tclap seconds to run: " << duration_cast<duration<double>>(runtime).count() << std::endl;
- }
- // boost::program_options
- {
- high_resolution_clock::time_point start = high_resolution_clock::now();
- for (unsigned int x = 0; x < 100000; ++x)
- {
- std::vector<std::string> arguments(carguments);
- po::options_description desc("This is a test program.");
- desc.add_options()
- ("int,i", po::value<int>(), "The integer flag")
- ("char,c", po::value<std::vector<char>>(), "The character flag")
- ("numbers", po::value<std::vector<double>>(), "The numbers flag");
- po::positional_options_description p;
- p.add("numbers", -1);
- po::variables_map vm;
- po::store(po::command_line_parser(carguments).options(desc).positional(p).run(), vm);
- const int i = vm["int"].as<int>();
- const std::vector<char> c(vm["char"].as<std::vector<char>>());
- const std::vector<double> n(vm["numbers"].as<std::vector<double>>());
- assert(i == 7);
- assert(c[0] == 'a');
- assert(c[1] == 'b');
- assert(c[2] == 'c');
- assert(c[3] == 'd');
- assert(doubleequals(n[0], 2.7));
- assert(doubleequals(n[1], 8.4));
- assert(doubleequals(n[2], 8.8));
- }
- high_resolution_clock::duration runtime = high_resolution_clock::now() - start;
- std::cout << "boost::program_options seconds to run: " << duration_cast<duration<double>>(runtime).count() << std::endl;
- }
- return 0;
-}
-```
-
-So, on top of being more flexible, smaller, and easier to read, it is faster
-than the most common alternatives.
+This is unfortunately not possible, given the power of the groups available.
+For instance, if you have a group validation that works like
+`(A && B) || (C && (D XOR E))`, how is this library going to be able to
+determine what exactly when wrong when it fails? It only knows that the
+entire expression evaluated false, not specifically what the user did wrong
+(and this is doubled over by the fact that validation operations are ordinary
+functions without any special meaning to the library). As you are the only one
+who understands the logic of your program, if you want useful group messages,
+you have to catch the ValidationError as a special case and check your own
+groups and spit out messages accordingly.
# Is it developed with regression tests?
@@ -915,3 +789,142 @@ TEST_CASE("Mapping types work as needed", "[args]")
REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"--mf=YeLLoW"}), args::MapError);
}
```
+
+# How fast is it?
+
+This should not really be a question you ask when you are looking for an
+argument-parsing library, but every test I've done shows args as being about
+65% faster than TCLAP and 220% faster than boost::program_options.
+
+The simplest benchmark I threw together is the following one, which parses the
+command line `-i 7 -c a 2.7 --char b 8.4 -c c 8.8 --char d` with a parser that
+parses -i as an int, -c as a list of chars, and the positional parameters as a
+list of doubles (the command line was originally much more complex, but TCLAP's
+limitations made me trim it down so I could use a common command line across
+all libraries. I also have to copy in the arguments list with every run,
+because TCLAP permutes its argument list as it runs (and comparison would have
+been unfair without comparing all about equally), but that surprisingly didn't
+affect much. Also tested is pulling the arguments out, but that was fast
+compared to parsing, as would be expected.
+
+### The run:
+
+```shell
+% g++ -obench bench.cxx -O2 -std=c++11 -lboost_program_options
+% ./bench
+args seconds to run: 0.895472
+tclap seconds to run: 1.45001
+boost::program_options seconds to run: 1.98972
+%
+```
+
+### The benchmark:
+
+```cpp
+#undef NDEBUG
+#include <iostream>
+#include <chrono>
+#include <cassert>
+#include "args.hxx"
+#include <tclap/CmdLine.h>
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+using namespace std::chrono;
+inline bool doubleequals(const double a, const double b)
+{
+ static const double delta = 0.0001;
+ const double diff = a - b;
+ return diff < delta && diff > -delta;
+}
+int main()
+{
+ const std::vector<std::string> carguments({"-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"});
+ const std::vector<std::string> pcarguments({"progname", "-i", "7", "-c", "a", "2.7", "--char", "b", "8.4", "-c", "c", "8.8", "--char", "d"});
+ // args
+ {
+ high_resolution_clock::time_point start = high_resolution_clock::now();
+ for (unsigned int x = 0; x < 100000; ++x)
+ {
+ std::vector<std::string> arguments(carguments);
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::ValueFlag<int> integer(parser, "integer", "The integer flag", {'i', "int"});
+ args::ValueFlagList<char> characters(parser, "characters", "The character flag", {'c', "char"});
+ args::PositionalList<double> numbers(parser, "numbers", "The numbers position list");
+ parser.ParseArgs(arguments);
+ 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');
+ assert(c[2] == 'c');
+ assert(c[3] == 'd');
+ assert(doubleequals(n[0], 2.7));
+ assert(doubleequals(n[1], 8.4));
+ assert(doubleequals(n[2], 8.8));
+ }
+ high_resolution_clock::duration runtime = high_resolution_clock::now() - start;
+ std::cout << "args seconds to run: " << duration_cast<duration<double>>(runtime).count() << std::endl;
+ }
+ // tclap
+ {
+ high_resolution_clock::time_point start = high_resolution_clock::now();
+ for (unsigned int x = 0; x < 100000; ++x)
+ {
+ std::vector<std::string> arguments(pcarguments);
+ TCLAP::CmdLine cmd("Command description message", ' ', "0.9");
+ TCLAP::ValueArg<int> integer("i", "int", "The integer flag", false, 0, "integer", cmd);
+ TCLAP::MultiArg<char> characters("c", "char", "The character flag", false, "characters", cmd);
+ TCLAP::UnlabeledMultiArg<double> numbers("numbers", "The numbers position list", false, "foo", cmd, false);
+ cmd.parse(arguments);
+ const int i = integer.getValue();
+ const std::vector<char> c(characters.getValue());
+ const std::vector<double> n(numbers.getValue());
+ assert(i == 7);
+ assert(c[0] == 'a');
+ assert(c[1] == 'b');
+ assert(c[2] == 'c');
+ assert(c[3] == 'd');
+ assert(doubleequals(n[0], 2.7));
+ assert(doubleequals(n[1], 8.4));
+ assert(doubleequals(n[2], 8.8));
+ }
+ high_resolution_clock::duration runtime = high_resolution_clock::now() - start;
+ std::cout << "tclap seconds to run: " << duration_cast<duration<double>>(runtime).count() << std::endl;
+ }
+ // boost::program_options
+ {
+ high_resolution_clock::time_point start = high_resolution_clock::now();
+ for (unsigned int x = 0; x < 100000; ++x)
+ {
+ std::vector<std::string> arguments(carguments);
+ po::options_description desc("This is a test program.");
+ desc.add_options()
+ ("int,i", po::value<int>(), "The integer flag")
+ ("char,c", po::value<std::vector<char>>(), "The character flag")
+ ("numbers", po::value<std::vector<double>>(), "The numbers flag");
+ po::positional_options_description p;
+ p.add("numbers", -1);
+ po::variables_map vm;
+ po::store(po::command_line_parser(carguments).options(desc).positional(p).run(), vm);
+ const int i = vm["int"].as<int>();
+ const std::vector<char> c(vm["char"].as<std::vector<char>>());
+ const std::vector<double> n(vm["numbers"].as<std::vector<double>>());
+ assert(i == 7);
+ assert(c[0] == 'a');
+ assert(c[1] == 'b');
+ assert(c[2] == 'c');
+ assert(c[3] == 'd');
+ assert(doubleequals(n[0], 2.7));
+ assert(doubleequals(n[1], 8.4));
+ assert(doubleequals(n[2], 8.8));
+ }
+ high_resolution_clock::duration runtime = high_resolution_clock::now() - start;
+ std::cout << "boost::program_options seconds to run: " << duration_cast<duration<double>>(runtime).count() << std::endl;
+ }
+ return 0;
+}
+```
+
+So, on top of being more flexible, smaller, and easier to read, it is faster
+than the most common alternatives.
diff --git a/args.hxx b/args.hxx
index aab92dd..6d5be60 100644
--- a/args.hxx
+++ b/args.hxx
@@ -85,11 +85,11 @@ namespace args
std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
{
// Preserve existing line breaks
- const std::string::size_type newlineloc = in.find('\n');
+ const auto newlineloc = in.find('\n');
if (newlineloc != in.npos)
{
- std::vector<std::string> first(Wrap(std::string(in, 0, newlineloc), width));
- std::vector<std::string> second(Wrap(std::string(in, newlineloc + 1), width));
+ auto first = Wrap(std::string(in, 0, newlineloc), width);
+ auto second = Wrap(std::string(in, newlineloc + 1), width);
first.insert(
std::end(first),
std::make_move_iterator(std::begin(second)),
@@ -100,7 +100,7 @@ namespace args
{
firstlinewidth = width;
}
- std::string::size_type currentwidth = firstlinewidth;
+ auto currentwidth = firstlinewidth;
std::istringstream stream(in);
std::vector<std::string> output;
@@ -110,7 +110,7 @@ namespace args
{
std::string item;
stream >> item;
- std::string::size_type itemsize = Glyphs(item);
+ auto itemsize = Glyphs(item);
if ((linesize + 1 + itemsize) > currentwidth)
{
if (linesize > 0)
@@ -461,7 +461,7 @@ namespace args
virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator) const override
{
std::tuple<std::string, std::string> description;
- const std::vector<std::string> flagStrings(matcher.GetFlagStrings(shortPrefix, longPrefix));
+ const auto flagStrings = matcher.GetFlagStrings(shortPrefix, longPrefix);
std::ostringstream flagstream;
for (auto it = std::begin(flagStrings); it != std::end(flagStrings); ++it)
{
@@ -489,7 +489,7 @@ namespace args
virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator) const override
{
std::tuple<std::string, std::string> description;
- const std::vector<std::string> flagStrings(matcher.GetFlagStrings(shortPrefix, longPrefix, Name(), shortSeparator, longSeparator));
+ const auto flagStrings = matcher.GetFlagStrings(shortPrefix, longPrefix, Name(), shortSeparator, longSeparator);
std::ostringstream flagstream;
for (auto it = std::begin(flagStrings); it != std::end(flagStrings); ++it)
{
@@ -600,8 +600,8 @@ namespace args
{
for (Base *child: children)
{
- PositionalBase *next = dynamic_cast<PositionalBase *>(child);
- Group *group = dynamic_cast<Group *>(child);
+ auto next = dynamic_cast<PositionalBase *>(child);
+ auto group = dynamic_cast<Group *>(child);
if (group)
{
next = group->GetNextPositional();
@@ -626,7 +626,7 @@ namespace args
{
return true;
}
- if (Group *group = dynamic_cast<Group *>(child))
+ if (auto group = dynamic_cast<Group *>(child))
{
if (group->HasFlag())
{
@@ -687,7 +687,7 @@ namespace args
std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
for (const auto &child: children)
{
- if (const Group *group = dynamic_cast<Group *>(child))
+ if (const auto group = dynamic_cast<Group *>(child))
{
// Push that group description on the back if not empty
unsigned char addindent = 0;
@@ -696,14 +696,14 @@ namespace args
descriptions.emplace_back(group->help, "", indent);
addindent = 1;
}
- std::vector<std::tuple<std::string, std::string, unsigned int>> groupDescriptions(group->GetChildDescriptions(shortPrefix, longPrefix, shortSeparator, longSeparator, indent + addindent));
+ auto groupDescriptions = group->GetChildDescriptions(shortPrefix, longPrefix, shortSeparator, longSeparator, indent + addindent);
descriptions.insert(
std::end(descriptions),
std::make_move_iterator(std::begin(groupDescriptions)),
std::make_move_iterator(std::end(groupDescriptions)));
- } else if (const NamedBase *named = dynamic_cast<NamedBase *>(child))
+ } else if (const auto named = dynamic_cast<NamedBase *>(child))
{
- const std::tuple<std::string, std::string> description(named->GetDescription(shortPrefix, longPrefix, shortSeparator, longSeparator));
+ const auto description = named->GetDescription(shortPrefix, longPrefix, shortSeparator, longSeparator);
descriptions.emplace_back(std::get<0>(description), std::get<1>(description), indent);
}
}
@@ -719,7 +719,7 @@ namespace args
{
if (const Group *group = dynamic_cast<Group *>(child))
{
- std::vector<std::string> groupNames(group->GetPosNames());
+ auto groupNames = group->GetPosNames();
names.insert(
std::end(names),
std::make_move_iterator(std::begin(groupNames)),
@@ -773,7 +773,7 @@ namespace args
{
for (const auto child: group.Children())
{
- if (const Group *group = dynamic_cast<Group *>(child))
+ if (const auto group = dynamic_cast<Group *>(child))
{
if (!group->Matched())
{
@@ -999,8 +999,8 @@ namespace args
bool hasoptions = false;
bool hasarguments = false;
- const std::vector<std::string> description(Wrap(this->description, helpParams.width - helpParams.descriptionindent));
- const std::vector<std::string> epilog(Wrap(this->epilog, helpParams.width - helpParams.descriptionindent));
+ const auto description = Wrap(this->description, helpParams.width - helpParams.descriptionindent);
+ const auto epilog = Wrap(this->epilog, helpParams.width - helpParams.descriptionindent);
std::ostringstream prognameline;
prognameline << prog;
if (HasFlag())
@@ -1023,7 +1023,7 @@ namespace args
{
prognameline << ' ' << proglinePostfix;
}
- const std::vector<std::string> proglines(Wrap(prognameline.str(), helpParams.width - (helpParams.progindent + 4), helpParams.width - helpParams.progindent));
+ const auto proglines = Wrap(prognameline.str(), helpParams.width - (helpParams.progindent + 4), helpParams.width - helpParams.progindent);
auto progit = std::begin(proglines);
if (progit != std::end(proglines))
{
@@ -1037,7 +1037,7 @@ namespace args
help << '\n';
- for (const std::string &line: description)
+ for (const auto &line: description)
{
help << std::string(helpParams.descriptionindent, ' ') << line << "\n";
}
@@ -1045,9 +1045,9 @@ namespace args
help << std::string(helpParams.progindent, ' ') << "OPTIONS:\n\n";
for (const auto &description: GetChildDescriptions(shortprefix, longprefix, allowJoinedShortValue ? "" : " ", allowJoinedLongValue ? longseparator : " "))
{
- const unsigned int groupindent = std::get<2>(description) * helpParams.eachgroupindent;
- const std::vector<std::string> flags(Wrap(std::get<0>(description), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter)));
- const std::vector<std::string> info(Wrap(std::get<1>(description), helpParams.width - (helpParams.helpindent + groupindent)));
+ const auto groupindent = std::get<2>(description) * helpParams.eachgroupindent;
+ const auto flags = Wrap(std::get<0>(description), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter));
+ const auto info = Wrap(std::get<1>(description), helpParams.width - (helpParams.helpindent + groupindent));
std::string::size_type flagssize = 0;
for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
@@ -1078,14 +1078,14 @@ namespace args
}
if (hasoptions && hasarguments && helpParams.showTerminator)
{
- for (const std::string &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent))
+ for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent))
{
help << std::string(helpParams.flagindent, ' ') << item << '\n';
}
}
help << "\n";
- for (const std::string &line: epilog)
+ for (const auto &line: epilog)
{
help << std::string(helpParams.descriptionindent, ' ') << line << "\n";
}
@@ -1118,7 +1118,7 @@ namespace args
// Check all arg chunks
for (auto it = begin; it != end; ++it)
{
- const std::string &chunk = *it;
+ const auto &chunk = *it;
if (!terminated and chunk == terminator)
{
@@ -1126,16 +1126,17 @@ namespace args
// If a long arg was found
} else if (!terminated && chunk.find(longprefix) == 0 && chunk.size() > longprefix.size())
{
- const std::string argchunk(chunk.substr(longprefix.size()));
+ const auto argchunk = chunk.substr(longprefix.size());
// Try to separate it, in case of a separator:
const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
- const std::string arg = (separator != argchunk.npos ?
+ // If the separator is in the argument, separate it.
+ const auto arg = (separator != argchunk.npos ?
std::string(argchunk, 0, separator)
: argchunk);
- if (FlagBase *base = Match(arg))
+ if (auto base = Match(arg))
{
- if (ValueFlagBase *argbase = dynamic_cast<ValueFlagBase *>(base))
+ if (auto argbase = dynamic_cast<ValueFlagBase *>(base))
{
if (separator != argchunk.npos)
{
@@ -1188,14 +1189,14 @@ namespace args
// Check short args
} else if (!terminated && chunk.find(shortprefix) == 0 && chunk.size() > shortprefix.size())
{
- const std::string argchunk(chunk.substr(shortprefix.size()));
+ const auto argchunk = chunk.substr(shortprefix.size());
for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
{
- const char arg = *argit;
+ const auto arg = *argit;
- if (FlagBase *base = Match(arg))
+ if (auto base = Match(arg))
{
- if (ValueFlagBase *argbase = dynamic_cast<ValueFlagBase *>(base))
+ if (auto argbase = dynamic_cast<ValueFlagBase *>(base))
{
const std::string value(++argit, std::end(argchunk));
if (!value.empty())
@@ -1246,7 +1247,7 @@ namespace args
}
} else
{
- PositionalBase *pos = GetNextPositional();
+ auto pos = GetNextPositional();
if (pos)
{
pos->ParseValue(chunk);
@@ -1289,7 +1290,7 @@ namespace args
*
* \return whether or not all arguments were parsed. This works for detecting kick-out, but is generally useless as it can't do anything with it.
*/
- bool ParseCLI(const int argc, const char * const * const argv)
+ bool ParseCLI(const int argc, const char * const * argv)
{
if (prog.empty())
{
@@ -1377,7 +1378,7 @@ namespace args
virtual FlagBase *Match(const std::string &arg) override
{
- FlagBase *me = FlagBase::Match(arg);
+ auto me = FlagBase::Match(arg);
if (me)
{
++count;
@@ -1387,7 +1388,7 @@ namespace args
virtual FlagBase *Match(const char arg) override
{
- FlagBase *me = FlagBase::Match(arg);
+ auto me = FlagBase::Match(arg);
if (me)
{
++count;
diff --git a/gitlike.cxx b/gitlike.cxx
new file mode 100644
index 0000000..bea4395
--- /dev/null
+++ b/gitlike.cxx
@@ -0,0 +1,128 @@
+/* Copyright © 2016 Taylor C. Richberger <taywee@gmx.com>
+ * This code is released under the license described in the LICENSE file
+ */
+
+#include <iostream>
+#include <unordered_map>
+#include <functional>
+#include <args.hxx>
+
+void Init(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs);
+void Add(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs);
+
+using commandtype = std::function<void(const std::string &, std::vector<std::string>::const_iterator, std::vector<std::string>::const_iterator)>;
+
+int main(int argc, char **argv)
+{
+ std::unordered_map<std::string, commandtype> map{
+ {"init", Init},
+ {"add", Add}};
+
+ const std::vector<std::string> args(argv + 1, argv + argc);
+ args::ArgumentParser parser("This is a git-like program", "Valid commands are init and add");
+ args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"});
+ parser.Prog(argv[0]);
+ args::Flag version(parser, "version", "Show the version of this program", {"version"});
+ args::ValueFlag<std::string> htmlpath(parser, "html-path", "Specify the html path", {"html-path"});
+ args::MapPositional<std::string, commandtype> command(parser, "command", "Command to execute", map);
+ command.KickOut(true);
+ try
+ {
+ auto next = parser.ParseArgs(args);
+ std::cout << std::boolalpha;
+ std::cout << "Before command options:" << std::endl;
+ std::cout << "Version called: " << bool{version} << std::endl;
+ std::cout << "html-path called: " << bool{htmlpath} << ", value: " << args::get(htmlpath) << std::endl;
+ if (command)
+ {
+ args::get(command)(argv[0], next, std::end(args));
+ } else
+ {
+ std::cout << parser;
+ }
+ }
+ catch (args::Help)
+ {
+ std::cout << parser;
+ return 0;
+ }
+ catch (args::Error e)
+ {
+ std::cerr << e.what() << std::endl;
+ std::cerr << parser;
+ return 1;
+ }
+ return 0;
+}
+
+void Init(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs)
+{
+ std::cout << "In Init" << std::endl;
+ args::ArgumentParser parser("");
+ parser.Prog(progname + " init");
+ args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"});
+ args::ValueFlag<std::string> templatedir(parser, "template-directory", "directory from which templates will be used", {"template"});
+ args::Flag bare(parser, "bare", "create a bare repository", {"bare"});
+ args::Flag quiet(parser, "quiet", "be quiet", {'q', "quiet"});
+ args::Positional<std::string> directory(parser, "directory", "The directory to create in", ".");
+ try
+ {
+ parser.ParseArgs(beginargs, endargs);
+ std::cout << std::boolalpha;
+ std::cout << "templatedir: " << bool{templatedir} << ", value: " << args::get(templatedir) << std::endl;
+ std::cout << "bare: " << bool{bare} << std::endl;
+ std::cout << "quiet: " << bool{quiet} << std::endl;
+ std::cout << "directory: " << bool{directory} << ", value: " << args::get(directory) << std::endl;
+ }
+ catch (args::Help)
+ {
+ std::cout << parser;
+ return;
+ }
+ catch (args::ParseError e)
+ {
+ std::cerr << e.what() << std::endl;
+ std::cerr << parser;
+ return;
+ }
+}
+
+void Add(const std::string &progname, std::vector<std::string>::const_iterator beginargs, std::vector<std::string>::const_iterator endargs)
+{
+ std::cout << "In Add" << std::endl;
+ args::ArgumentParser parser("");
+ parser.Prog(progname + " add");
+ args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"});
+ args::Flag dryrun(parser, "dryrun", "dry run", {'n', "dry-run"});
+ args::Flag verbose(parser, "verbose", "be verbose", {'v', "verbose"});
+ args::Flag refresh(parser, "refresh", "Don't add, only refresh the index", {"refresh"});
+ args::PositionalList<std::string> pathspec(parser, "pathspec", "pathspecs");
+ try
+ {
+ parser.ParseArgs(beginargs, endargs);
+ std::cout << std::boolalpha;
+ std::cout << "dryrun: " << bool{dryrun} << std::endl;;
+ std::cout << "verbose: " << bool{verbose} << std::endl;
+ std::cout << "refresh: " << bool{refresh} << std::endl;
+ std::cout << "pathspec: " << bool{pathspec} << std::endl;
+ if (pathspec)
+ {
+ std::cout << "values: " << std::endl;
+ for (const auto &spec: args::get(pathspec))
+ {
+ std::cout << " - " << spec << std::endl;
+ }
+ }
+ }
+ catch (args::Help)
+ {
+ std::cout << parser;
+ return;
+ }
+ catch (args::ParseError e)
+ {
+ std::cerr << e.what() << std::endl;
+ std::cerr << parser;
+ return;
+ }
+}