Commit 3c57be7adf373f730e0dffe0f0f4272ddc94fa0c
Merge branch 'master' into basic-enum
Showing
9 changed files
with
92 additions
and
10 deletions
CHANGELOG.md
| 1 | 1 | ## Version 1.1 (in progress) |
| 2 | 2 | * Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12) |
| 3 | 3 | * Added `app.parse_order()` with original parse order |
| 4 | +* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns. | |
| 4 | 5 | |
| 5 | 6 | ## Version 1.0 |
| 6 | 7 | * Cleanup using `clang-tidy` and `clang-format` | ... | ... |
README.md
| ... | ... | @@ -47,7 +47,7 @@ After I wrote this, I also found the following libraries: |
| 47 | 47 | |---------|-------------------| |
| 48 | 48 | | [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. | |
| 49 | 49 | | [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). | |
| 50 | -| [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. | | |
| 50 | +| [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. Nice callback system. | | |
| 51 | 51 | | [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. | |
| 52 | 52 | | [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. | |
| 53 | 53 | |
| ... | ... | @@ -90,7 +90,7 @@ To set up, add options, and run, your main function will look something like thi |
| 90 | 90 | CLI::App app{"App description"}; |
| 91 | 91 | |
| 92 | 92 | std::string filename = "default"; |
| 93 | -app.add_option("-f,--file", file, "A help string"); | |
| 93 | +app.add_option("-f,--file", filename, "A help string"); | |
| 94 | 94 | |
| 95 | 95 | try { |
| 96 | 96 | app.parse(argc, argv); |
| ... | ... | @@ -194,6 +194,7 @@ There are several options that are supported on the main app and subcommands. Th |
| 194 | 194 | * `.parsed()`: True if this subcommand was given on the command line |
| 195 | 195 | * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point. |
| 196 | 196 | * `.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). |
| 197 | +* `.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. | |
| 197 | 198 | |
| 198 | 199 | ## Configuration file |
| 199 | 200 | ... | ... |
examples/CMakeLists.txt
examples/inter_argument_order.cpp
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | #include <iostream> |
| 3 | 3 | #include <vector> |
| 4 | 4 | #include <tuple> |
| 5 | +#include <algorithm> | |
| 5 | 6 | |
| 6 | 7 | int main(int argc, char **argv) { |
| 7 | 8 | CLI::App app; |
| ... | ... | @@ -25,7 +26,7 @@ int main(int argc, char **argv) { |
| 25 | 26 | std::reverse(std::begin(foos), std::end(foos)); |
| 26 | 27 | std::reverse(std::begin(bars), std::end(bars)); |
| 27 | 28 | |
| 28 | - std::vector<std::tuple<std::string, int>> keyval; | |
| 29 | + std::vector<std::pair<std::string, int>> keyval; | |
| 29 | 30 | for(auto option : app.parse_order()) { |
| 30 | 31 | if(option == foo) { |
| 31 | 32 | keyval.emplace_back("foo", foos.back()); |
| ... | ... | @@ -38,11 +39,7 @@ int main(int argc, char **argv) { |
| 38 | 39 | } |
| 39 | 40 | |
| 40 | 41 | // Prove the vector is correct |
| 41 | - std::string name; | |
| 42 | - int value; | |
| 43 | - | |
| 44 | - for(auto &tuple : keyval) { | |
| 45 | - std::tie(name, value) = tuple; | |
| 46 | - std::cout << name << " : " << value << std::endl; | |
| 42 | + for(auto &pair : keyval) { | |
| 43 | + std::cout << pair.first << " : " << pair.second << std::endl; | |
| 47 | 44 | } |
| 48 | 45 | } | ... | ... |
examples/prefix_command.cpp
0 → 100644
| 1 | +#include "CLI/CLI.hpp" | |
| 2 | + | |
| 3 | +int main(int argc, char **argv) { | |
| 4 | + | |
| 5 | + CLI::App app("Prefix command app"); | |
| 6 | + app.prefix_command(); | |
| 7 | + | |
| 8 | + std::vector<int> vals; | |
| 9 | + app.add_option("--vals,-v", vals) | |
| 10 | + ->expected(1); | |
| 11 | + | |
| 12 | + std::vector<std::string> more_comms; | |
| 13 | + try { | |
| 14 | + more_comms = app.parse(argc, argv); | |
| 15 | + } catch(const CLI::ParseError &e) { | |
| 16 | + return app.exit(e); | |
| 17 | + } | |
| 18 | + | |
| 19 | + std::cout << "Prefix:"; | |
| 20 | + for(int v : vals) | |
| 21 | + std::cout << v << ":"; | |
| 22 | + | |
| 23 | + std::cout << std::endl << "Remaining commands: "; | |
| 24 | + | |
| 25 | + // Perfer to loop over from beginning, not "pop" order | |
| 26 | + std::reverse(std::begin(more_comms), std::end(more_comms)); | |
| 27 | + for(auto com : more_comms) | |
| 28 | + std::cout << com << " "; | |
| 29 | + std::cout << std::endl; | |
| 30 | + | |
| 31 | + return 0; | |
| 32 | +} | ... | ... |
include/CLI/App.hpp
| ... | ... | @@ -57,6 +57,9 @@ class App { |
| 57 | 57 | /// If true, allow extra arguments (ie, don't throw an error). |
| 58 | 58 | bool allow_extras_{false}; |
| 59 | 59 | |
| 60 | + /// If true, return immediatly on an unrecognised option (implies allow_extras) | |
| 61 | + bool prefix_command_{false}; | |
| 62 | + | |
| 60 | 63 | /// This is a function that runs when complete. Great for subcommands. Can throw. |
| 61 | 64 | std::function<void()> callback_; |
| 62 | 65 | |
| ... | ... | @@ -152,6 +155,12 @@ class App { |
| 152 | 155 | return this; |
| 153 | 156 | } |
| 154 | 157 | |
| 158 | + /// Do not parse anything after the first unrecongnised option and return | |
| 159 | + App *prefix_command(bool allow = true) { | |
| 160 | + prefix_command_ = allow; | |
| 161 | + return this; | |
| 162 | + } | |
| 163 | + | |
| 155 | 164 | /// Ignore case. Subcommand inherit value. |
| 156 | 165 | App *ignore_case(bool value = true) { |
| 157 | 166 | ignore_case_ = value; |
| ... | ... | @@ -949,7 +958,7 @@ class App { |
| 949 | 958 | return val.first != detail::Classifer::POSITIONAL_MARK; |
| 950 | 959 | }); |
| 951 | 960 | |
| 952 | - if(num_left_over > 0 && !allow_extras_) | |
| 961 | + if(num_left_over > 0 && !(allow_extras_ || prefix_command_)) | |
| 953 | 962 | throw ExtrasError("[" + detail::rjoin(args, " ") + "]"); |
| 954 | 963 | } |
| 955 | 964 | } |
| ... | ... | @@ -1060,7 +1069,15 @@ class App { |
| 1060 | 1069 | else { |
| 1061 | 1070 | args.pop_back(); |
| 1062 | 1071 | missing()->emplace_back(detail::Classifer::NONE, positional); |
| 1072 | + | |
| 1073 | + if(prefix_command_) { | |
| 1074 | + while(!args.empty()) { | |
| 1075 | + missing()->emplace_back(detail::Classifer::NONE, args.back()); | |
| 1076 | + args.pop_back(); | |
| 1077 | + } | |
| 1078 | + } | |
| 1063 | 1079 | } |
| 1080 | + | |
| 1064 | 1081 | } |
| 1065 | 1082 | |
| 1066 | 1083 | /// Parse a subcommand, modify args and continue | ... | ... |
include/CLI/Option.hpp
| ... | ... | @@ -433,6 +433,10 @@ class Option { |
| 433 | 433 | /// Set the default value string representation |
| 434 | 434 | void set_default_val(std::string val) { defaultval_ = val; } |
| 435 | 435 | |
| 436 | + | |
| 437 | + /// Set the type name displayed on this option | |
| 438 | + void set_type_name(std::string val) {typeval_ = val;} | |
| 439 | + | |
| 436 | 440 | ///@} |
| 437 | 441 | |
| 438 | 442 | protected: | ... | ... |
tests/HelpTest.cpp
| ... | ... | @@ -190,6 +190,22 @@ TEST(THelp, ExcludesPositional) { |
| 190 | 190 | EXPECT_THAT(help, HasSubstr("Excludes: op1")); |
| 191 | 191 | } |
| 192 | 192 | |
| 193 | +TEST(THelp, ManualSetters) { | |
| 194 | + | |
| 195 | + CLI::App app{"My prog"}; | |
| 196 | + | |
| 197 | + int x; | |
| 198 | + | |
| 199 | + CLI::Option *op1 = app.add_option("--op", x); | |
| 200 | + op1->set_default_val("12"); | |
| 201 | + op1->set_type_name("BIGGLES"); | |
| 202 | + | |
| 203 | + std::string help = app.help(); | |
| 204 | + | |
| 205 | + EXPECT_THAT(help, HasSubstr("=12")); | |
| 206 | + EXPECT_THAT(help, HasSubstr("BIGGLES")); | |
| 207 | +} | |
| 208 | + | |
| 193 | 209 | TEST(THelp, Subcom) { |
| 194 | 210 | CLI::App app{"My prog"}; |
| 195 | 211 | ... | ... |
tests/SubcommandTest.cpp
| ... | ... | @@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) { |
| 231 | 231 | EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound); |
| 232 | 232 | } |
| 233 | 233 | |
| 234 | +TEST_F(TApp, PrefixProgram) { | |
| 235 | + | |
| 236 | + app.prefix_command(); | |
| 237 | + | |
| 238 | + app.add_flag("--simple"); | |
| 239 | + | |
| 240 | + args = {"--simple", "other", "--simple", "--mine"}; | |
| 241 | + auto ret_args = run(); | |
| 242 | + | |
| 243 | + EXPECT_EQ(ret_args, std::vector<std::string>({"--mine", "--simple", "other"})); | |
| 244 | + | |
| 245 | +} | |
| 246 | + | |
| 234 | 247 | struct SubcommandProgram : public TApp { |
| 235 | 248 | |
| 236 | 249 | CLI::App *start; | ... | ... |