From 3b04cd62af5ff2b29c65e561e0c51511ac390aa3 Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Fri, 2 Jun 2017 12:51:27 -0400 Subject: [PATCH 1/3] Adding parse order capture --- include/CLI/App.hpp | 21 +++++++++++++++++++-- tests/AppTest.cpp | 17 +++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index bae90197..8a17c80b 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -80,6 +80,9 @@ class App { /// /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator. missing_t missing_; + + /// This is a list of pointers to options with the orignal parse order + std::vector parse_order_; ///@} /// @name Subcommands @@ -710,6 +713,11 @@ class App { return local_name == name_to_check; } + + /// This gets a vector of pointers with the original parse order + const std::vector &parse_order() const { + return parse_order_; + } ///@} @@ -949,6 +957,7 @@ class App { (static_cast(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) { opt->add_result(positional); + parse_order_.push_back(opt.get()); args.pop_back(); return; } @@ -1011,18 +1020,21 @@ class App { int num = op->get_expected(); - if(num == 0) + if(num == 0) { op->add_result(""); - else if(rest != "") { + parse_order_.push_back(op.get()); + } else if(rest != "") { if(num > 0) num--; op->add_result(rest); + parse_order_.push_back(op.get()); rest = ""; } if(num == -1) { while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { op->add_result(args.back()); + parse_order_.push_back(op.get()); args.pop_back(); } } else @@ -1031,6 +1043,7 @@ class App { std::string current_ = args.back(); args.pop_back(); op->add_result(current_); + parse_order_.push_back(op.get()); } if(rest != "") { @@ -1075,19 +1088,23 @@ class App { if(num != -1) num--; op->add_result(value); + parse_order_.push_back(op.get()); } else if(num == 0) { op->add_result(""); + parse_order_.push_back(op.get()); } if(num == -1) { while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { op->add_result(args.back()); + parse_order_.push_back(op.get()); args.pop_back(); } } else while(num > 0 && !args.empty()) { num--; op->add_result(args.back()); + parse_order_.push_back(op.get()); args.pop_back(); } return; diff --git a/tests/AppTest.cpp b/tests/AppTest.cpp index ca19f33a..283054dd 100644 --- a/tests/AppTest.cpp +++ b/tests/AppTest.cpp @@ -501,6 +501,23 @@ TEST_F(TApp, VectorFancyOpts) { EXPECT_THROW(run(), CLI::ParseError); } +TEST_F(TApp, OriginalOrder) { + std::vector st1; + CLI::Option* op1 = app.add_option("-a", st1); + std::vector st2; + CLI::Option* op2 = app.add_option("-b", st2); + + args = {"-a", "1", "-b", "2", "-a3", "-a", "4"}; + + run(); + + EXPECT_EQ(st1, std::vector({1,3,4})); + EXPECT_EQ(st2, std::vector({2})); + + EXPECT_EQ(app.parse_order(), std::vector({op1, op2, op1, op1})); + +} + TEST_F(TApp, RequiresFlags) { CLI::Option *opt = app.add_flag("-s,--string"); app.add_flag("--both")->requires(opt); From 11df3becfb76971fb57a7ffc58ca3064205e0bd2 Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Sun, 4 Jun 2017 13:19:10 -0400 Subject: [PATCH 2/3] Show parse order is preserved --- CHANGELOG.md | 3 ++ README.md | 2 +- examples/CMakeLists.txt | 1 + examples/inter_argument_order.cpp | 50 +++++++++++++++++++++++++++++++ include/CLI/App.hpp | 2 +- 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 examples/inter_argument_order.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 82a1a17d..a066720d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## Version 1.1 (in progress) +* Added `app.parse_order()` with original parse order + ## Version 1.0 * Cleanup using `clang-tidy` and `clang-format` * Small improvements to Timers, easier to subclass Error diff --git a/README.md b/README.md index 533b23ee..402d5efb 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ On the command line, options can be given as: * `--file=filename` (equals) 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. -If you set `.allow_extras()` on the main `App`, the parse function will return the left over arguments instead of throwing an error. +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()`. If `--` is present in the command line, everything after that is positional only. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b4efebdb..f41d948e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -17,3 +17,4 @@ endfunction() 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) diff --git a/examples/inter_argument_order.cpp b/examples/inter_argument_order.cpp new file mode 100644 index 00000000..8056523b --- /dev/null +++ b/examples/inter_argument_order.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) { + CLI::App app; + + std::vector foos; + auto foo = app.add_option("--foo,-f", foos); + + std::vector bars; + auto bar = app.add_option("--bar", bars); + + app.add_flag("--z,--x"); // Random other flags + + // Standard parsing lines (copy and paste in) + try { + app.parse(argc, argv); + } catch (const CLI::ParseError &e) { + return app.exit(e); + } + + // I perfer using the back and popping + std::reverse(std::begin(foos), std::end(foos)); + std::reverse(std::begin(bars), std::end(bars)); + + std::vector> keyval; + for(auto option : app.parse_order()) { + if(option == foo) { + keyval.emplace_back("foo", foos.back()); + foos.pop_back(); + } + if(option == bar) { + keyval.emplace_back("bar", bars.back()); + bars.pop_back(); + } + } + + // 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; + } +} + + diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 8a17c80b..d5ed30d6 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -254,7 +254,7 @@ class App { variable.emplace_back(); retval &= detail::lexical_cast(a, variable.back()); } - return variable.size() > 0 && retval; + return (!variable.empty()) && retval; }; Option *opt = add_option(name, fun, description, defaulted); From 18163306293386e20fb87b298f8c881093a4a908 Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Sun, 4 Jun 2017 13:23:01 -0400 Subject: [PATCH 3/3] clang-formatting new source --- examples/inter_argument_order.cpp | 8 +++----- include/CLI/App.hpp | 10 ++++------ tests/AppTest.cpp | 17 ++++++++--------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/examples/inter_argument_order.cpp b/examples/inter_argument_order.cpp index 8056523b..6d570e1b 100644 --- a/examples/inter_argument_order.cpp +++ b/examples/inter_argument_order.cpp @@ -17,7 +17,7 @@ int main(int argc, char **argv) { // Standard parsing lines (copy and paste in) try { app.parse(argc, argv); - } catch (const CLI::ParseError &e) { + } catch(const CLI::ParseError &e) { return app.exit(e); } @@ -25,7 +25,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> keyval; + std::vector> keyval; for(auto option : app.parse_order()) { if(option == foo) { keyval.emplace_back("foo", foos.back()); @@ -41,10 +41,8 @@ int main(int argc, char **argv) { std::string name; int value; - for(auto& tuple : keyval) { + for(auto &tuple : keyval) { std::tie(name, value) = tuple; std::cout << name << " : " << value << std::endl; } } - - diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index d5ed30d6..f4729cda 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -80,9 +80,9 @@ class App { /// /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator. missing_t missing_; - + /// This is a list of pointers to options with the orignal parse order - std::vector parse_order_; + std::vector