1
0
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:
Henry Fredrick Schreiner 2017-06-04 13:39:00 -04:00
commit fd0ca5aa69
9 changed files with 111 additions and 16 deletions

View File

@ -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`

View File

@ -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

View File

@ -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)

View 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;
}
}

View File

@ -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;

View File

@ -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);