Browse by type
This library is considered feature-complete by its creator. It will still receive bug fixes, and good pull requests will be accepted, but no new major functionality or API changes will be added to this codebase.
A simple, small, flexible, single-header C++11 argument parsing library.
This is designed to appear somewhat similar to Python's argparse, but in C++, with static type checking, and hopefully a lot faster (also allowing fully nestable group logic, where Python's argparse does not).
UTF-8 support is limited at best. No normalization is performed, so non-ascii characters are very best kept out of flags, and combined glyphs are probably going to mess up help output if you use them. Most UTF-8 necessary for internationalization should work for most cases, though heavily combinatory UTF alphabets may wreak havoc.
This program is MIT-licensed, so you can use the header as-is with no restrictions. I'd appreciate attribution in a README, Man page, or something if you are feeling generous, but all that's required is that you don't remove the license and my name from the header of the args.hxx file in source redistributions (ie. don't pretend that you wrote it). I do welcome additions and updates wherever you feel like contributing code.
The API documentation can be found at https://taywee.github.io/args
The code can be downloaded at https://github.com/Taywee/args
There are also somewhat extensive examples below.
You can find all the test cases in
https://github.com/Taywee/args/tree/master/test. Each test is a small
self-contained .cxx file with its own main(), so they double as
focused usage examples.
It:
--foo bar, requiring --foo=bar, or the inverse, or the
same for short options).--foo first
second, where --foo slurps both arguments).There are tons of things this library does not do!
++foo and
--foo in the same parser), though shortopt and longopt prefixes can be
different.sudo make install
Or, to install it somewhere special (default is /usr/local):
sudo make install DESTDIR=/opt/mydir
You can also copy the file into your source tree, if you want to be absolutely sure you keep a stable API between projects.
If you prefer other installation methods, many standard ones are available and included, including CMake, conan, buck, and meson. An example CMake file using args is included in the examples directory.
make doc/man
sudo make installman
This requires Doxygen
doxygen Doxyfile
Your docs are now in doc/html
Simply add the following entry to your .tipi/deps file
{
"taywee/args": { "@": "6.4.1" }
}
You can optionally remove the @ section to target HEAD easily.
Create an ArgumentParser, modify its attributes to fit your needs, add arguments through regular argument objects (or create your own), and match them with an args::Matcher object (check its construction details in the doxygen documentation.
Then you can either call it with args::ArgumentParser::ParseCLI for the full command line with program name, or args::ArgumentParser::ParseArgs with just the arguments to be parsed. The argument and group variables can then be interpreted as a boolean to see if they've been matched.
All variables can be pulled (including the boolean match status for regular args::Flag variables) with args::get.
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.
Yes. The test/ directory contains the regression suite, with each test
compiled into its own executable and run via CMake's ctest. Tests run on
every push and pull request via
GitHub Actions, covering Linux,
macOS, and Windows in both Debug and Release builds.
To run them locally:
cmake -B build
cmake --build build
ctest --test-dir build --output-on-failure
Or via the Makefile wrapper:
make runtests
The tests don't depend on any third-party framework — test/test_helpers.hxx
provides a handful of inline assertion helpers (test::require,
test::require_throws_as, etc.) and that's it. To debug a single failure,
just run the corresponding executable directly, e.g.
./build/argstest-help_flag.
All the code examples here will be complete code examples, with some output.
#include <iostream>
#include <args.hxx>
int main(int argc, char **argv)
{
args::ArgumentParser parser("This is a test program.", "This goes after the options.");
args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"});
args::CompletionFlag completion(parser, {"complete"});
try
{
parser.ParseCLI(argc, argv);
}
catch (const args::Completion& e)
{
std::cout << e.what();
return 0;
}
catch (const args::Help&)
{
std::cout << parser;
return 0;
}
catch (const args::ParseError& e)
{
std::cerr << e.what() << std::endl;
std::cerr << parser;
return 1;
}
return 0;
}
% ./test
% ./test -h
./test {OPTIONS}
This is a test program.
OPTIONS:
-h, --help Display this help menu
This goes after the options.
%
#include <iostream>
#include <args.hxx>
int main(int argc, char **argv)
{
args::ArgumentParser parser("This is a test program.", "This goes after the options.");
args::Group group(parser, "This group is all exclusive:", args::Group::Validators::Xor);
args::Flag foo(group, "foo", "The foo flag", {'f', "foo"});
args::Flag bar(group, "bar", "The bar flag", {'b'});
args::Flag baz(group, "baz", "The baz flag", {"baz"});
try
{
parser.ParseCLI(argc, argv);
}
catch (args::Help)
{
std::cout << parser;
return 0;
}
catch (args::ParseError e)
{
std::cerr << e.what() << std::endl;
std::cerr << parser;
return 1;
}
catch (args::ValidationError e)
{
std::cerr << e.what() << std::endl;
std::cerr << parser;
return 1;
}
if (foo) { std::cout << "foo" << std::endl; }
if (bar) { std::cout << "bar" << std::endl; }
if (baz) { std::cout << "baz" << std::endl; }
return 0;
}
% ./test
Group validation failed somewhere!
./test {OPTIONS}
This is a test program.
OPTIONS:
This group is all exclusive:
-f, --foo The foo flag
-b The bar flag
--baz The baz flag
This goes after the options.
% ./test -f
foo
% ./test --foo
foo
% ./test --foo -f
foo
% ./test -b
bar
% ./test --baz
baz
% ./test --baz -f
Group validation failed somewhere!
./test {OPTIONS}
This is a test program.
...
% ./test --baz -fb
Group validation failed somewhere!
./test {OPTIONS}
...
%
#include <iostream>
#include <args.hxx>
int main(int argc, char **argv)
{
args::ArgumentParser parser("This is a test program.", "This goes after the options.");
args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"});
args::ValueFlag<int> integer(parser, "integer", "The integer flag", {'i'});
args::ValueFlagList<char> characters(parser, "characters", "The character flag", {'c'});
args::Positional<std::string> foo(parser, "foo", "The foo position");
args::PositionalList<double> numbers(parser, "numbers", "The numbers position list");
try
{
parser.ParseCLI(argc, argv);
}
catch (args::Help)
{
std::cout << parser;
return 0;
}
catch (args::ParseError e)
{
std::cerr << e.what() << std::endl;
std::cerr << parser;
return 1;
}
catch (args::ValidationError e)
{
std::cerr << e.what() << std::endl;
std::cerr << parser;
return 1;
}
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;
}
% ./test -h
./test {OPTIONS} [foo] [numbers...]
This is a test program.
OPTIONS:
-h, --help Display this help menu
-i integer The integer flag
-c characters The character flag
foo The foo position
numbers The numbers position list
"--" can be used to terminate flag options and force all following
arguments to be treated as positional options
This goes after the options.
% ./test -i 5
i: 5
% ./test -i 5.2
Argument 'integer' received invalid value type '5.2'
./test {OPTIONS} [foo] [numbers...]
% ./test -c 1 -c 2 -c 3
c: 1
c: 2
c: 3
%
% ./test 1 2 3 4 5 6 7 8 9
f: 1
n: 2
n: 3
n: 4
n: 5
n: 6
n: 7
n: 8
n: 9
% ./test 1 2 3 4 5 6 7 8 9 a
Argument 'numbers' received invalid value type 'a'
./test {OPTIONS} [foo] [numbers...]
This is a test program.
...
```cpp
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 gitdir(arguments, "path", "", {"git-dir"}); args::HelpFlag h(arguments, "help", "help", {'h', "help"}); args::PositionalList pathsList(arguments, "paths", "files to commit");
try
{
p.ParseCLI(argc, argv);
if (add)
{
std::cout << "Add";
}
else
{
std::cout << "Commit";
}
for (auto &&path : path