mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
Merge branch 'master' into basic-enum
This commit is contained in:
commit
3c57be7adf
@ -1,6 +1,7 @@
|
||||
## Version 1.1 (in progress)
|
||||
* Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12)
|
||||
* Added `app.parse_order()` with original parse order
|
||||
* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns.
|
||||
|
||||
## Version 1.0
|
||||
* Cleanup using `clang-tidy` and `clang-format`
|
||||
|
@ -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. |
|
||||
| [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. |
|
||||
| [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"};
|
||||
|
||||
std::string filename = "default";
|
||||
app.add_option("-f,--file", file, "A help string");
|
||||
app.add_option("-f,--file", filename, "A help string");
|
||||
|
||||
try {
|
||||
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
|
||||
* `.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).
|
||||
* `.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
|
||||
|
||||
|
@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp)
|
||||
add_cli_exe(subcommands subcommands.cpp)
|
||||
add_cli_exe(groups groups.cpp)
|
||||
add_cli_exe(inter_argument_order inter_argument_order.cpp)
|
||||
add_cli_exe(prefix_command prefix_command.cpp)
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
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(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()) {
|
||||
if(option == foo) {
|
||||
keyval.emplace_back("foo", foos.back());
|
||||
@ -38,11 +39,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// 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;
|
||||
for(auto &pair : keyval) {
|
||||
std::cout << pair.first << " : " << pair.second << std::endl;
|
||||
}
|
||||
}
|
||||
|
32
examples/prefix_command.cpp
Normal file
32
examples/prefix_command.cpp
Normal 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;
|
||||
}
|
@ -57,6 +57,9 @@ class App {
|
||||
/// If true, allow extra arguments (ie, don't throw an error).
|
||||
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.
|
||||
std::function<void()> callback_;
|
||||
|
||||
@ -152,6 +155,12 @@ class App {
|
||||
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.
|
||||
App *ignore_case(bool value = true) {
|
||||
ignore_case_ = value;
|
||||
@ -949,7 +958,7 @@ class App {
|
||||
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, " ") + "]");
|
||||
}
|
||||
}
|
||||
@ -1060,7 +1069,15 @@ class App {
|
||||
else {
|
||||
args.pop_back();
|
||||
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
|
||||
|
@ -433,6 +433,10 @@ class Option {
|
||||
/// Set the default value string representation
|
||||
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:
|
||||
|
@ -190,6 +190,22 @@ TEST(THelp, ExcludesPositional) {
|
||||
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) {
|
||||
CLI::App app{"My prog"};
|
||||
|
||||
|
@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) {
|
||||
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 {
|
||||
|
||||
CLI::App *start;
|
||||
|
Loading…
x
Reference in New Issue
Block a user