命令行参数解析,在写一些命令行的程序时需要用到,args.hxx类似于Python/ target=_blank class=infotextkey>Python的argparse,但在C++中,它具有静态类型检查,并且速度更快(也允许完全嵌套的组逻辑,而Python的argparse没有),支持自定义类型解析,子命令等。使用上,只需要在工程中include “args.hxx” 就可以正常使用,符合C++11规范,兼容性更好。
API文档地址: https://taywee.Github.io/args
源码地址: at https://github.com/Taywee/args
以下的所有代码示例都将是完整的代码示例,并带有一些输出。
#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.
...
#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;
}
运行输出:
% ./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
#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;
}
运行结果:
% ./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
这里我们使用std::tuple
#include <iostream>
#include <tuple>
std::istream& operator>>(std::istream& is, std::tuple<int, int>& ints)
{
is >> std::get<0>(ints);
is.get();
is >> std::get<1>(ints);
return is;
}
#include <args.hxx>
struct DoublesReader
{
void operator()(const std::string &name, const std::string &value, std::tuple<double, double> &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)
{
args::ArgumentParser parser("This is a test program.");
args::Positional<std::tuple<int, int>> ints(parser, "INTS", "This takes a pair of integers.");
args::Positional<std::tuple<double, double>, DoublesReader> doubles(parser, "DOUBLES", "This takes a pair of doubles.");
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;
}
if (ints)
{
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>(args::get(doubles)) << " and " << std::get<1>(args::get(doubles)) << std::endl;
}
return 0;
}
运行输出:
% ./test -h
Argument could not be matched: 'h'
./test [INTS] [DOUBLES]
This is a test program.
OPTIONS:
INTS This takes a pair of integers.
DOUBLES This takes a pair of doubles.
% ./test 5
ints found: 5 and 0
% ./test 5,8
ints found: 5 and 8
% ./test 5,8 2.4,8
ints found: 5 and 8
doubles found: 2.4 and 8
% ./test 5,8 2.4,
terminate called after throwing an instance of 'std::invalid_argument'
what(): stod
zsh: abort ./test 5,8 2.4,
% ./test 5,8 2.4
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::basic_string: __pos (which is 4) > this->size() (which is 3)
zsh: abort ./test 5,8 2.4
% ./test 5,8 2.4-7
ints found: 5 and 8
doubles found: 2.4 and 7
% ./test 5,8 2.4,-7
ints found: 5 and 8
doubles found: 2.4 and -7
更多范例请参考github上的手册:https://github.com/Taywee/args