Commit 21559bd1228d1666a34d75350ba658cd480b2688
Committed by
GitHub
Merge pull request #17 from CLIUtils/parse-order
Adding parse order capture
Showing
6 changed files
with
87 additions
and
4 deletions
CHANGELOG.md
README.md
| ... | ... | @@ -164,7 +164,7 @@ On the command line, options can be given as: |
| 164 | 164 | * `--file=filename` (equals) |
| 165 | 165 | |
| 166 | 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 | 168 | If `--` is present in the command line, |
| 169 | 169 | everything after that is positional only. |
| 170 | 170 | ... | ... |
examples/CMakeLists.txt
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 | 81 | /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator. |
| 82 | 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 | 88 | /// @name Subcommands |
| 86 | 89 | ///@{ |
| ... | ... | @@ -251,7 +254,7 @@ class App { |
| 251 | 254 | variable.emplace_back(); |
| 252 | 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 | 260 | Option *opt = add_option(name, fun, description, defaulted); |
| ... | ... | @@ -711,6 +714,9 @@ class App { |
| 711 | 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 | 722 | protected: |
| ... | ... | @@ -949,6 +955,7 @@ class App { |
| 949 | 955 | (static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) { |
| 950 | 956 | |
| 951 | 957 | opt->add_result(positional); |
| 958 | + parse_order_.push_back(opt.get()); | |
| 952 | 959 | args.pop_back(); |
| 953 | 960 | return; |
| 954 | 961 | } |
| ... | ... | @@ -1011,18 +1018,21 @@ class App { |
| 1011 | 1018 | |
| 1012 | 1019 | int num = op->get_expected(); |
| 1013 | 1020 | |
| 1014 | - if(num == 0) | |
| 1021 | + if(num == 0) { | |
| 1015 | 1022 | op->add_result(""); |
| 1016 | - else if(rest != "") { | |
| 1023 | + parse_order_.push_back(op.get()); | |
| 1024 | + } else if(rest != "") { | |
| 1017 | 1025 | if(num > 0) |
| 1018 | 1026 | num--; |
| 1019 | 1027 | op->add_result(rest); |
| 1028 | + parse_order_.push_back(op.get()); | |
| 1020 | 1029 | rest = ""; |
| 1021 | 1030 | } |
| 1022 | 1031 | |
| 1023 | 1032 | if(num == -1) { |
| 1024 | 1033 | while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { |
| 1025 | 1034 | op->add_result(args.back()); |
| 1035 | + parse_order_.push_back(op.get()); | |
| 1026 | 1036 | args.pop_back(); |
| 1027 | 1037 | } |
| 1028 | 1038 | } else |
| ... | ... | @@ -1031,6 +1041,7 @@ class App { |
| 1031 | 1041 | std::string current_ = args.back(); |
| 1032 | 1042 | args.pop_back(); |
| 1033 | 1043 | op->add_result(current_); |
| 1044 | + parse_order_.push_back(op.get()); | |
| 1034 | 1045 | } |
| 1035 | 1046 | |
| 1036 | 1047 | if(rest != "") { |
| ... | ... | @@ -1075,19 +1086,23 @@ class App { |
| 1075 | 1086 | if(num != -1) |
| 1076 | 1087 | num--; |
| 1077 | 1088 | op->add_result(value); |
| 1089 | + parse_order_.push_back(op.get()); | |
| 1078 | 1090 | } else if(num == 0) { |
| 1079 | 1091 | op->add_result(""); |
| 1092 | + parse_order_.push_back(op.get()); | |
| 1080 | 1093 | } |
| 1081 | 1094 | |
| 1082 | 1095 | if(num == -1) { |
| 1083 | 1096 | while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { |
| 1084 | 1097 | op->add_result(args.back()); |
| 1098 | + parse_order_.push_back(op.get()); | |
| 1085 | 1099 | args.pop_back(); |
| 1086 | 1100 | } |
| 1087 | 1101 | } else |
| 1088 | 1102 | while(num > 0 && !args.empty()) { |
| 1089 | 1103 | num--; |
| 1090 | 1104 | op->add_result(args.back()); |
| 1105 | + parse_order_.push_back(op.get()); | |
| 1091 | 1106 | args.pop_back(); |
| 1092 | 1107 | } |
| 1093 | 1108 | return; | ... | ... |
tests/AppTest.cpp
| ... | ... | @@ -501,6 +501,22 @@ TEST_F(TApp, VectorFancyOpts) { |
| 501 | 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 | 520 | TEST_F(TApp, RequiresFlags) { |
| 505 | 521 | CLI::Option *opt = app.add_flag("-s,--string"); |
| 506 | 522 | app.add_flag("--both")->requires(opt); | ... | ... |