1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 04:33:53 +00:00

Merge branch 'master' into basic-enum

This commit is contained in:
Henry Fredrick Schreiner 2017-06-05 09:00:18 -04:00
commit 3c57be7adf
9 changed files with 92 additions and 10 deletions

View File

@ -1,6 +1,7 @@
## 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 * Added `app.parse_order()` with original parse order
* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns.
## Version 1.0 ## Version 1.0
* Cleanup using `clang-tidy` and `clang-format` * Cleanup using `clang-tidy` and `clang-format`

View File

@ -47,7 +47,7 @@ After I wrote this, I also found the following libraries:
|---------|-------------------| |---------|-------------------|
| [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. | | [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). | | [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. | | [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. Nice callback system. |
| [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. | | [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. | | [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. |
@ -90,7 +90,7 @@ To set up, add options, and run, your main function will look something like thi
CLI::App app{"App description"}; CLI::App app{"App description"};
std::string filename = "default"; std::string filename = "default";
app.add_option("-f,--file", file, "A help string"); app.add_option("-f,--file", filename, "A help string");
try { try {
app.parse(argc, argv); app.parse(argc, argv);
@ -194,6 +194,7 @@ There are several options that are supported on the main app and subcommands. Th
* `.parsed()`: True if this subcommand was given on the command line * `.parsed()`: True if this subcommand was given on the command line
* `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point. * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
* `.allow_extras()`: Do not throw an error if extra arguments are left over (Only useful on the main `App`, as that's the one that throws errors). * `.allow_extras()`: Do not throw an error if extra arguments are left over (Only useful on the main `App`, as that's the one that throws errors).
* `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognised item. It is ideal for allowing your app to be a "prefix" to calling another app.
## Configuration file ## Configuration file

View File

@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp)
add_cli_exe(subcommands subcommands.cpp) add_cli_exe(subcommands subcommands.cpp)
add_cli_exe(groups groups.cpp) add_cli_exe(groups groups.cpp)
add_cli_exe(inter_argument_order inter_argument_order.cpp) add_cli_exe(inter_argument_order inter_argument_order.cpp)
add_cli_exe(prefix_command prefix_command.cpp)

View File

@ -2,6 +2,7 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
#include <algorithm>
int main(int argc, char **argv) { int main(int argc, char **argv) {
CLI::App app; CLI::App app;
@ -25,7 +26,7 @@ int main(int argc, char **argv) {
std::reverse(std::begin(foos), std::end(foos)); std::reverse(std::begin(foos), std::end(foos));
std::reverse(std::begin(bars), std::end(bars)); std::reverse(std::begin(bars), std::end(bars));
std::vector<std::tuple<std::string, int>> keyval; std::vector<std::pair<std::string, int>> keyval;
for(auto option : app.parse_order()) { for(auto option : app.parse_order()) {
if(option == foo) { if(option == foo) {
keyval.emplace_back("foo", foos.back()); keyval.emplace_back("foo", foos.back());
@ -38,11 +39,7 @@ int main(int argc, char **argv) {
} }
// Prove the vector is correct // Prove the vector is correct
std::string name; for(auto &pair : keyval) {
int value; std::cout << pair.first << " : " << pair.second << std::endl;
for(auto &tuple : keyval) {
std::tie(name, value) = tuple;
std::cout << name << " : " << value << std::endl;
} }
} }

View File

@ -0,0 +1,32 @@
#include "CLI/CLI.hpp"
int main(int argc, char **argv) {
CLI::App app("Prefix command app");
app.prefix_command();
std::vector<int> vals;
app.add_option("--vals,-v", vals)
->expected(1);
std::vector<std::string> more_comms;
try {
more_comms = app.parse(argc, argv);
} catch(const CLI::ParseError &e) {
return app.exit(e);
}
std::cout << "Prefix:";
for(int v : vals)
std::cout << v << ":";
std::cout << std::endl << "Remaining commands: ";
// Perfer to loop over from beginning, not "pop" order
std::reverse(std::begin(more_comms), std::end(more_comms));
for(auto com : more_comms)
std::cout << com << " ";
std::cout << std::endl;
return 0;
}

View File

@ -57,6 +57,9 @@ class App {
/// If true, allow extra arguments (ie, don't throw an error). /// If true, allow extra arguments (ie, don't throw an error).
bool allow_extras_{false}; bool allow_extras_{false};
/// If true, return immediatly on an unrecognised option (implies allow_extras)
bool prefix_command_{false};
/// This is a function that runs when complete. Great for subcommands. Can throw. /// This is a function that runs when complete. Great for subcommands. Can throw.
std::function<void()> callback_; std::function<void()> callback_;
@ -152,6 +155,12 @@ class App {
return this; return this;
} }
/// Do not parse anything after the first unrecongnised option and return
App *prefix_command(bool allow = true) {
prefix_command_ = allow;
return this;
}
/// Ignore case. Subcommand inherit value. /// Ignore case. Subcommand inherit value.
App *ignore_case(bool value = true) { App *ignore_case(bool value = true) {
ignore_case_ = value; ignore_case_ = value;
@ -949,7 +958,7 @@ class App {
return val.first != detail::Classifer::POSITIONAL_MARK; return val.first != detail::Classifer::POSITIONAL_MARK;
}); });
if(num_left_over > 0 && !allow_extras_) if(num_left_over > 0 && !(allow_extras_ || prefix_command_))
throw ExtrasError("[" + detail::rjoin(args, " ") + "]"); throw ExtrasError("[" + detail::rjoin(args, " ") + "]");
} }
} }
@ -1060,8 +1069,16 @@ class App {
else { else {
args.pop_back(); args.pop_back();
missing()->emplace_back(detail::Classifer::NONE, positional); missing()->emplace_back(detail::Classifer::NONE, positional);
if(prefix_command_) {
while(!args.empty()) {
missing()->emplace_back(detail::Classifer::NONE, args.back());
args.pop_back();
} }
} }
}
}
/// Parse a subcommand, modify args and continue /// Parse a subcommand, modify args and continue
/// ///

View File

@ -433,6 +433,10 @@ class Option {
/// Set the default value string representation /// Set the default value string representation
void set_default_val(std::string val) { defaultval_ = val; } void set_default_val(std::string val) { defaultval_ = val; }
/// Set the type name displayed on this option
void set_type_name(std::string val) {typeval_ = val;}
///@} ///@}
protected: protected:

View File

@ -190,6 +190,22 @@ TEST(THelp, ExcludesPositional) {
EXPECT_THAT(help, HasSubstr("Excludes: op1")); EXPECT_THAT(help, HasSubstr("Excludes: op1"));
} }
TEST(THelp, ManualSetters) {
CLI::App app{"My prog"};
int x;
CLI::Option *op1 = app.add_option("--op", x);
op1->set_default_val("12");
op1->set_type_name("BIGGLES");
std::string help = app.help();
EXPECT_THAT(help, HasSubstr("=12"));
EXPECT_THAT(help, HasSubstr("BIGGLES"));
}
TEST(THelp, Subcom) { TEST(THelp, Subcom) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};

View File

@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) {
EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound); EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound);
} }
TEST_F(TApp, PrefixProgram) {
app.prefix_command();
app.add_flag("--simple");
args = {"--simple", "other", "--simple", "--mine"};
auto ret_args = run();
EXPECT_EQ(ret_args, std::vector<std::string>({"--mine", "--simple", "other"}));
}
struct SubcommandProgram : public TApp { struct SubcommandProgram : public TApp {
CLI::App *start; CLI::App *start;