Commit 21559bd1228d1666a34d75350ba658cd480b2688

Authored by Henry Schreiner
Committed by GitHub
2 parents 100db357 18163306

Merge pull request #17 from CLIUtils/parse-order

Adding parse order capture
CHANGELOG.md
  1 +## Version 1.1 (in progress)
  2 +* Added `app.parse_order()` with original parse order
  3 +
1 ## Version 1.0 4 ## Version 1.0
2 * Cleanup using `clang-tidy` and `clang-format` 5 * Cleanup using `clang-tidy` and `clang-format`
3 * Small improvements to Timers, easier to subclass Error 6 * Small improvements to Timers, easier to subclass Error
README.md
@@ -164,7 +164,7 @@ On the command line, options can be given as: @@ -164,7 +164,7 @@ On the command line, options can be given as:
164 * `--file=filename` (equals) 164 * `--file=filename` (equals)
165 165
166 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. 166 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.
167 -If you set `.allow_extras()` on the main `App`, the parse function will return the left over arguments instead of throwing an error. 167 +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()`.
168 If `--` is present in the command line, 168 If `--` is present in the command line,
169 everything after that is positional only. 169 everything after that is positional only.
170 170
examples/CMakeLists.txt
@@ -17,3 +17,4 @@ endfunction() @@ -17,3 +17,4 @@ endfunction()
17 add_cli_exe(simple simple.cpp) 17 add_cli_exe(simple simple.cpp)
18 add_cli_exe(subcommands subcommands.cpp) 18 add_cli_exe(subcommands subcommands.cpp)
19 add_cli_exe(groups groups.cpp) 19 add_cli_exe(groups groups.cpp)
  20 +add_cli_exe(inter_argument_order inter_argument_order.cpp)
examples/inter_argument_order.cpp 0 → 100644
  1 +#include <CLI/CLI.hpp>
  2 +#include <iostream>
  3 +#include <vector>
  4 +#include <tuple>
  5 +
  6 +int main(int argc, char **argv) {
  7 + CLI::App app;
  8 +
  9 + std::vector<int> foos;
  10 + auto foo = app.add_option("--foo,-f", foos);
  11 +
  12 + std::vector<int> bars;
  13 + auto bar = app.add_option("--bar", bars);
  14 +
  15 + app.add_flag("--z,--x"); // Random other flags
  16 +
  17 + // Standard parsing lines (copy and paste in)
  18 + try {
  19 + app.parse(argc, argv);
  20 + } catch(const CLI::ParseError &e) {
  21 + return app.exit(e);
  22 + }
  23 +
  24 + // I perfer using the back and popping
  25 + std::reverse(std::begin(foos), std::end(foos));
  26 + std::reverse(std::begin(bars), std::end(bars));
  27 +
  28 + std::vector<std::tuple<std::string, int>> keyval;
  29 + for(auto option : app.parse_order()) {
  30 + if(option == foo) {
  31 + keyval.emplace_back("foo", foos.back());
  32 + foos.pop_back();
  33 + }
  34 + if(option == bar) {
  35 + keyval.emplace_back("bar", bars.back());
  36 + bars.pop_back();
  37 + }
  38 + }
  39 +
  40 + // 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;
  47 + }
  48 +}
include/CLI/App.hpp
@@ -81,6 +81,9 @@ class App { @@ -81,6 +81,9 @@ class App {
81 /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator. 81 /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator.
82 missing_t missing_; 82 missing_t missing_;
83 83
  84 + /// This is a list of pointers to options with the orignal parse order
  85 + std::vector<Option *> parse_order_;
  86 +
84 ///@} 87 ///@}
85 /// @name Subcommands 88 /// @name Subcommands
86 ///@{ 89 ///@{
@@ -251,7 +254,7 @@ class App { @@ -251,7 +254,7 @@ class App {
251 variable.emplace_back(); 254 variable.emplace_back();
252 retval &= detail::lexical_cast(a, variable.back()); 255 retval &= detail::lexical_cast(a, variable.back());
253 } 256 }
254 - return variable.size() > 0 && retval; 257 + return (!variable.empty()) && retval;
255 }; 258 };
256 259
257 Option *opt = add_option(name, fun, description, defaulted); 260 Option *opt = add_option(name, fun, description, defaulted);
@@ -711,6 +714,9 @@ class App { @@ -711,6 +714,9 @@ class App {
711 return local_name == name_to_check; 714 return local_name == name_to_check;
712 } 715 }
713 716
  717 + /// This gets a vector of pointers with the original parse order
  718 + const std::vector<Option *> &parse_order() const { return parse_order_; }
  719 +
714 ///@} 720 ///@}
715 721
716 protected: 722 protected:
@@ -949,6 +955,7 @@ class App { @@ -949,6 +955,7 @@ class App {
949 (static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) { 955 (static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) {
950 956
951 opt->add_result(positional); 957 opt->add_result(positional);
  958 + parse_order_.push_back(opt.get());
952 args.pop_back(); 959 args.pop_back();
953 return; 960 return;
954 } 961 }
@@ -1011,18 +1018,21 @@ class App { @@ -1011,18 +1018,21 @@ class App {
1011 1018
1012 int num = op->get_expected(); 1019 int num = op->get_expected();
1013 1020
1014 - if(num == 0) 1021 + if(num == 0) {
1015 op->add_result(""); 1022 op->add_result("");
1016 - else if(rest != "") { 1023 + parse_order_.push_back(op.get());
  1024 + } else if(rest != "") {
1017 if(num > 0) 1025 if(num > 0)
1018 num--; 1026 num--;
1019 op->add_result(rest); 1027 op->add_result(rest);
  1028 + parse_order_.push_back(op.get());
1020 rest = ""; 1029 rest = "";
1021 } 1030 }
1022 1031
1023 if(num == -1) { 1032 if(num == -1) {
1024 while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { 1033 while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {
1025 op->add_result(args.back()); 1034 op->add_result(args.back());
  1035 + parse_order_.push_back(op.get());
1026 args.pop_back(); 1036 args.pop_back();
1027 } 1037 }
1028 } else 1038 } else
@@ -1031,6 +1041,7 @@ class App { @@ -1031,6 +1041,7 @@ class App {
1031 std::string current_ = args.back(); 1041 std::string current_ = args.back();
1032 args.pop_back(); 1042 args.pop_back();
1033 op->add_result(current_); 1043 op->add_result(current_);
  1044 + parse_order_.push_back(op.get());
1034 } 1045 }
1035 1046
1036 if(rest != "") { 1047 if(rest != "") {
@@ -1075,19 +1086,23 @@ class App { @@ -1075,19 +1086,23 @@ class App {
1075 if(num != -1) 1086 if(num != -1)
1076 num--; 1087 num--;
1077 op->add_result(value); 1088 op->add_result(value);
  1089 + parse_order_.push_back(op.get());
1078 } else if(num == 0) { 1090 } else if(num == 0) {
1079 op->add_result(""); 1091 op->add_result("");
  1092 + parse_order_.push_back(op.get());
1080 } 1093 }
1081 1094
1082 if(num == -1) { 1095 if(num == -1) {
1083 while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { 1096 while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {
1084 op->add_result(args.back()); 1097 op->add_result(args.back());
  1098 + parse_order_.push_back(op.get());
1085 args.pop_back(); 1099 args.pop_back();
1086 } 1100 }
1087 } else 1101 } else
1088 while(num > 0 && !args.empty()) { 1102 while(num > 0 && !args.empty()) {
1089 num--; 1103 num--;
1090 op->add_result(args.back()); 1104 op->add_result(args.back());
  1105 + parse_order_.push_back(op.get());
1091 args.pop_back(); 1106 args.pop_back();
1092 } 1107 }
1093 return; 1108 return;
tests/AppTest.cpp
@@ -501,6 +501,22 @@ TEST_F(TApp, VectorFancyOpts) { @@ -501,6 +501,22 @@ TEST_F(TApp, VectorFancyOpts) {
501 EXPECT_THROW(run(), CLI::ParseError); 501 EXPECT_THROW(run(), CLI::ParseError);
502 } 502 }
503 503
  504 +TEST_F(TApp, OriginalOrder) {
  505 + std::vector<int> st1;
  506 + CLI::Option *op1 = app.add_option("-a", st1);
  507 + std::vector<int> st2;
  508 + CLI::Option *op2 = app.add_option("-b", st2);
  509 +
  510 + args = {"-a", "1", "-b", "2", "-a3", "-a", "4"};
  511 +
  512 + run();
  513 +
  514 + EXPECT_EQ(st1, std::vector<int>({1, 3, 4}));
  515 + EXPECT_EQ(st2, std::vector<int>({2}));
  516 +
  517 + EXPECT_EQ(app.parse_order(), std::vector<CLI::Option *>({op1, op2, op1, op1}));
  518 +}
  519 +
504 TEST_F(TApp, RequiresFlags) { 520 TEST_F(TApp, RequiresFlags) {
505 CLI::Option *opt = app.add_flag("-s,--string"); 521 CLI::Option *opt = app.add_flag("-s,--string");
506 app.add_flag("--both")->requires(opt); 522 app.add_flag("--both")->requires(opt);