mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-05-09 16:13:54 +00:00
Merge branch 'master' into basic-enum
This commit is contained in:
commit
fd0ca5aa69
@ -1,5 +1,6 @@
|
|||||||
## Version 1.1 (in progress)
|
## Version 1.1 (in progress)
|
||||||
* Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12)
|
* Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12)
|
||||||
|
* Added `app.parse_order()` with original parse order
|
||||||
|
|
||||||
## Version 1.0
|
## Version 1.0
|
||||||
* Cleanup using `clang-tidy` and `clang-format`
|
* Cleanup using `clang-tidy` and `clang-format`
|
||||||
|
34
README.md
34
README.md
@ -31,15 +31,25 @@ An acceptable CLI parser library should be all of the following:
|
|||||||
* Work with standard types, simple custom types, and extendible to exotic types.
|
* Work with standard types, simple custom types, and extendible to exotic types.
|
||||||
* Permissively licenced.
|
* Permissively licenced.
|
||||||
|
|
||||||
The major CLI parsers for C++ include:
|
The major CLI parsers for C++ include (with my biased opinions):
|
||||||
|
|
||||||
* [Boost Program Options]: A great library if you already depend on Boost, but its pre-C++11 syntax is really odd and setting up the correct call in the main function is poorly documented (and is nearly a page of code). A simple wrapper for the Boost library was originally developed, but was discarded as CLI11 became more powerful. The idea of capturing a value and setting it originated with Boost PO.
|
| Library | My biased opinion |
|
||||||
* [The Lean Mean C++ Option Parser]: One header file is great, but the syntax is atrocious, in my opinion. It was quite impractical to wrap the syntax or to use in a complex project. It seems to handle standard parsing quite well.
|
|---------|-------------------|
|
||||||
* [TCLAP]: The not-quite-standard command line parsing causes common shortcuts to fail. It also seems to be poorly supported, with only minimal bugfixes accepted. Header only, but in quite a few files. Has not managed to get enough support to move to GitHub yet. No subcommands. Produces wrapped values.
|
| [Boost Program Options] | A great library if you already depend on Boost, but its pre-C++11 syntax is really odd and setting up the correct call in the main function is poorly documented (and is nearly a page of code). A simple wrapper for the Boost library was originally developed, but was discarded as CLI11 became more powerful. The idea of capturing a value and setting it originated with Boost PO. |
|
||||||
* [Cxxopts]: C++11, single file, and nice CMake support, but requires regex, therefore GCC 4.8 (CentOS 7 default) does not work. Syntax closely based on Boost PO, so not ideal but familiar.
|
| [The Lean Mean C++ Option Parser] | One header file is great, but the syntax is atrocious, in my opinion. It was quite impractical to wrap the syntax or to use in a complex project. It seems to handle standard parsing quite well. |
|
||||||
* [DocOpt]: Completely different approach to program options in C++11, you write the docs and the interface is generated. Too fragile and specialized.
|
| [TCLAP] | The not-quite-standard command line parsing causes common shortcuts to fail. It also seems to be poorly supported, with only minimal bugfixes accepted. Header only, but in quite a few files. Has not managed to get enough support to move to GitHub yet. No subcommands. Produces wrapped values. |
|
||||||
* [GFlags]: The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars.
|
| [Cxxopts] | C++11, single file, and nice CMake support, but requires regex, therefore GCC 4.8 (CentOS 7 default) does not work. Syntax closely based on Boost PO, so not ideal but familiar. |
|
||||||
* [GetOpt]: Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors).
|
| [DocOpt] | Completely different approach to program options in C++11, you write the docs and the interface is generated. Too fragile and specialized. |
|
||||||
|
|
||||||
|
After I wrote this, I also found the following libraries:
|
||||||
|
|
||||||
|
| Library | My biased opinion |
|
||||||
|
|---------|-------------------|
|
||||||
|
| [GFlags] | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. |
|
||||||
|
| [GetOpt] | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors). |
|
||||||
|
| [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. |
|
||||||
|
| [Args] | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose. |
|
||||||
|
| [Argument Aggregator] | I'm a big fan of the [fmt] library, and the try-catch statement looks familiar. :thumbsup: Doesn't seem to support subcommands. |
|
||||||
|
|
||||||
None of these libraries fulfill all the above requirements. As you probably have already guessed, CLI11 does.
|
None of these libraries fulfill all the above requirements. As you probably have already guessed, CLI11 does.
|
||||||
So, this library was designed to provide a great syntax, good compiler compatibility, and minimal installation fuss.
|
So, this library was designed to provide a great syntax, good compiler compatibility, and minimal installation fuss.
|
||||||
@ -154,7 +164,7 @@ On the command line, options can be given as:
|
|||||||
* `--file=filename` (equals)
|
* `--file=filename` (equals)
|
||||||
|
|
||||||
Extra positional arguments will cause the program to exit, so at least one positional option with a vector is recommended if you want to allow extraneous arguments.
|
Extra positional arguments will cause the program to exit, so at least one positional option with a vector is recommended if you want to allow extraneous arguments.
|
||||||
If you set `.allow_extras()` on the main `App`, the parse function will return the left over arguments instead of throwing an error.
|
If you set `.allow_extras()` on the main `App`, the parse function will return the left over arguments instead of throwing an error. You can access a vector of pointers to the parsed options in the original order using `parse_order()`.
|
||||||
If `--` is present in the command line,
|
If `--` is present in the command line,
|
||||||
everything after that is positional only.
|
everything after that is positional only.
|
||||||
|
|
||||||
@ -306,4 +316,8 @@ CLI11 was developed at the [University of Cincinnati] to support of the [GooFit]
|
|||||||
[DIANA/HEP]: http://diana-hep.org
|
[DIANA/HEP]: http://diana-hep.org
|
||||||
[NSF Award 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
|
[NSF Award 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
|
||||||
[University of Cincinnati]: http://www.uc.edu
|
[University of Cincinnati]: http://www.uc.edu
|
||||||
[GitBook]: https://henryiii.gitbooks.io/cli11/content
|
[GitBook]: https://henryiii.gitbooks.io/cli11/content
|
||||||
|
[ProgramOptions.hxx]: https://github.com/Fytch/ProgramOptions.hxx
|
||||||
|
[Argument Aggregator]: https://github.com/vietjtnguyen/argagg
|
||||||
|
[Args]: https://github.com/Taywee/args
|
||||||
|
[fmt]: https://github.com/fmtlib/fmt
|
||||||
|
@ -14,6 +14,7 @@ function(add_cli_exe T)
|
|||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
add_cli_exe(try try.cpp)
|
add_cli_exe(simple simple.cpp)
|
||||||
add_cli_exe(try1 try1.cpp)
|
add_cli_exe(subcommands subcommands.cpp)
|
||||||
add_cli_exe(try2 try2.cpp)
|
add_cli_exe(groups groups.cpp)
|
||||||
|
add_cli_exe(inter_argument_order inter_argument_order.cpp)
|
||||||
|
48
examples/inter_argument_order.cpp
Normal file
48
examples/inter_argument_order.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include <CLI/CLI.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
CLI::App app;
|
||||||
|
|
||||||
|
std::vector<int> foos;
|
||||||
|
auto foo = app.add_option("--foo,-f", foos);
|
||||||
|
|
||||||
|
std::vector<int> bars;
|
||||||
|
auto bar = app.add_option("--bar", bars);
|
||||||
|
|
||||||
|
app.add_flag("--z,--x"); // Random other flags
|
||||||
|
|
||||||
|
// Standard parsing lines (copy and paste in)
|
||||||
|
try {
|
||||||
|
app.parse(argc, argv);
|
||||||
|
} catch(const CLI::ParseError &e) {
|
||||||
|
return app.exit(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// I perfer using the back and popping
|
||||||
|
std::reverse(std::begin(foos), std::end(foos));
|
||||||
|
std::reverse(std::begin(bars), std::end(bars));
|
||||||
|
|
||||||
|
std::vector<std::tuple<std::string, int>> keyval;
|
||||||
|
for(auto option : app.parse_order()) {
|
||||||
|
if(option == foo) {
|
||||||
|
keyval.emplace_back("foo", foos.back());
|
||||||
|
foos.pop_back();
|
||||||
|
}
|
||||||
|
if(option == bar) {
|
||||||
|
keyval.emplace_back("bar", bars.back());
|
||||||
|
bars.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prove the vector is correct
|
||||||
|
std::string name;
|
||||||
|
int value;
|
||||||
|
|
||||||
|
for(auto &tuple : keyval) {
|
||||||
|
std::tie(name, value) = tuple;
|
||||||
|
std::cout << name << " : " << value << std::endl;
|
||||||
|
}
|
||||||
|
}
|
@ -81,6 +81,9 @@ class App {
|
|||||||
/// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator.
|
/// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator.
|
||||||
missing_t missing_;
|
missing_t missing_;
|
||||||
|
|
||||||
|
/// This is a list of pointers to options with the orignal parse order
|
||||||
|
std::vector<Option *> parse_order_;
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
/// @name Subcommands
|
/// @name Subcommands
|
||||||
///@{
|
///@{
|
||||||
@ -291,7 +294,7 @@ class App {
|
|||||||
variable.emplace_back();
|
variable.emplace_back();
|
||||||
retval &= detail::lexical_cast(a, variable.back());
|
retval &= detail::lexical_cast(a, variable.back());
|
||||||
}
|
}
|
||||||
return variable.size() > 0 && retval;
|
return (!variable.empty()) && retval;
|
||||||
};
|
};
|
||||||
|
|
||||||
Option *opt = add_option(name, fun, description, defaulted);
|
Option *opt = add_option(name, fun, description, defaulted);
|
||||||
@ -805,6 +808,9 @@ class App {
|
|||||||
return local_name == name_to_check;
|
return local_name == name_to_check;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This gets a vector of pointers with the original parse order
|
||||||
|
const std::vector<Option *> &parse_order() const { return parse_order_; }
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -1043,6 +1049,7 @@ class App {
|
|||||||
(static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) {
|
(static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) {
|
||||||
|
|
||||||
opt->add_result(positional);
|
opt->add_result(positional);
|
||||||
|
parse_order_.push_back(opt.get());
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1105,18 +1112,21 @@ class App {
|
|||||||
|
|
||||||
int num = op->get_expected();
|
int num = op->get_expected();
|
||||||
|
|
||||||
if(num == 0)
|
if(num == 0) {
|
||||||
op->add_result("");
|
op->add_result("");
|
||||||
else if(rest != "") {
|
parse_order_.push_back(op.get());
|
||||||
|
} else if(rest != "") {
|
||||||
if(num > 0)
|
if(num > 0)
|
||||||
num--;
|
num--;
|
||||||
op->add_result(rest);
|
op->add_result(rest);
|
||||||
|
parse_order_.push_back(op.get());
|
||||||
rest = "";
|
rest = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(num == -1) {
|
if(num == -1) {
|
||||||
while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {
|
while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {
|
||||||
op->add_result(args.back());
|
op->add_result(args.back());
|
||||||
|
parse_order_.push_back(op.get());
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -1125,6 +1135,7 @@ class App {
|
|||||||
std::string current_ = args.back();
|
std::string current_ = args.back();
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
op->add_result(current_);
|
op->add_result(current_);
|
||||||
|
parse_order_.push_back(op.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rest != "") {
|
if(rest != "") {
|
||||||
@ -1169,19 +1180,23 @@ class App {
|
|||||||
if(num != -1)
|
if(num != -1)
|
||||||
num--;
|
num--;
|
||||||
op->add_result(value);
|
op->add_result(value);
|
||||||
|
parse_order_.push_back(op.get());
|
||||||
} else if(num == 0) {
|
} else if(num == 0) {
|
||||||
op->add_result("");
|
op->add_result("");
|
||||||
|
parse_order_.push_back(op.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(num == -1) {
|
if(num == -1) {
|
||||||
while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {
|
while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {
|
||||||
op->add_result(args.back());
|
op->add_result(args.back());
|
||||||
|
parse_order_.push_back(op.get());
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
while(num > 0 && !args.empty()) {
|
while(num > 0 && !args.empty()) {
|
||||||
num--;
|
num--;
|
||||||
op->add_result(args.back());
|
op->add_result(args.back());
|
||||||
|
parse_order_.push_back(op.get());
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -530,6 +530,22 @@ TEST_F(TApp, VectorFancyOpts) {
|
|||||||
EXPECT_THROW(run(), CLI::ParseError);
|
EXPECT_THROW(run(), CLI::ParseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, OriginalOrder) {
|
||||||
|
std::vector<int> st1;
|
||||||
|
CLI::Option *op1 = app.add_option("-a", st1);
|
||||||
|
std::vector<int> st2;
|
||||||
|
CLI::Option *op2 = app.add_option("-b", st2);
|
||||||
|
|
||||||
|
args = {"-a", "1", "-b", "2", "-a3", "-a", "4"};
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
EXPECT_EQ(st1, std::vector<int>({1, 3, 4}));
|
||||||
|
EXPECT_EQ(st2, std::vector<int>({2}));
|
||||||
|
|
||||||
|
EXPECT_EQ(app.parse_order(), std::vector<CLI::Option *>({op1, op2, op1, op1}));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TApp, RequiresFlags) {
|
TEST_F(TApp, RequiresFlags) {
|
||||||
CLI::Option *opt = app.add_flag("-s,--string");
|
CLI::Option *opt = app.add_flag("-s,--string");
|
||||||
app.add_flag("--both")->requires(opt);
|
app.add_flag("--both")->requires(opt);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user