Commit f059528559b7ee890bac0b76b977310374a8c675

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent 447bda04

Support for multi-size MultiOptionPolicies

include/CLI/Error.hpp
... ... @@ -105,7 +105,7 @@ class IncorrectConstruction : public ConstructionError {
105 105 return IncorrectConstruction("Option " + name + " is not defined");
106 106 }
107 107 static IncorrectConstruction MultiOptionPolicy(std::string name) {
108   - return IncorrectConstruction(name + ": multi_option_policy only works for flags and single value options");
  108 + return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
109 109 }
110 110 };
111 111  
... ...
include/CLI/Option.hpp
... ... @@ -383,8 +383,8 @@ class Option : public OptionBase<Option> {
383 383  
384 384 /// Take the last argument if given multiple times (or another policy)
385 385 Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
386   - // TODO: This can support multiple options
387   - if(get_type_size() != 0 && get_expected() != 1)
  386 +
  387 + if(get_items_expected() < 0)
388 388 throw IncorrectConstruction::MultiOptionPolicy(single_name());
389 389 multi_option_policy_ = value;
390 390 return this;
... ... @@ -400,12 +400,25 @@ class Option : public OptionBase&lt;Option&gt; {
400 400 /// The number of times the option expects to be included
401 401 int get_expected() const { return expected_; }
402 402  
403   - /// The total number of expected values (including the type)
  403 + /// \breif The total number of expected values (including the type)
  404 + /// This is positive if exactly this number is expected, and negitive for at least N values
  405 + ///
  406 + /// v = fabs(size_type*expected)
  407 + /// !MultiOptionPolicy::Throw
  408 + /// | Expected < 0 | Expected == 0 | Expected > 0
  409 + /// Size < 0 | -v | 0 | -v
  410 + /// Size == 0 | 0 | 0 | 0
  411 + /// Size > 0 | -v | 0 | -v // Expected must be 1
  412 + ///
  413 + /// MultiOptionPolicy::Throw
  414 + /// | Expected < 0 | Expected == 0 | Expected > 0
  415 + /// Size < 0 | -v | 0 | v
  416 + /// Size == 0 | 0 | 0 | 0
  417 + /// Size > 0 | v | 0 | v // Expected must be 1
  418 + ///
404 419 int get_items_expected() const {
405   - // type_size == 0, return 0
406   - // type_size > 1, return type_size_
407   - // type_size < 0, return -type_size * expected;
408   - return type_size_ < 0 ? -1 * type_size_ * expected_ : type_size_;
  420 + return std::abs(type_size_ * expected_) *
  421 + ((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1));
409 422 }
410 423  
411 424 /// True if this has a default value
... ... @@ -525,14 +538,18 @@ class Option : public OptionBase&lt;Option&gt; {
525 538  
526 539 bool local_result;
527 540  
  541 + // Num items expected or length of vector, always at least 1
  542 + // Only valid for a trimming policy
  543 + int trim_size = std::min(std::max(std::abs(get_items_expected()), 1), static_cast<int>(results_.size()));
  544 +
528 545 // Operation depends on the policy setting
529 546 if(multi_option_policy_ == MultiOptionPolicy::TakeLast) {
530   - // TODO: add non-1 size arguments here
531   - results_t partial_result = {results_.back()};
  547 + // Allow multi-option sizes (including 0)
  548 + results_t partial_result{results_.end() - trim_size, results_.end()};
532 549 local_result = !callback_(partial_result);
533 550  
534 551 } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) {
535   - results_t partial_result = {results_.at(0)};
  552 + results_t partial_result{results_.begin(), results_.begin() + trim_size};
536 553 local_result = !callback_(partial_result);
537 554  
538 555 } else if(multi_option_policy_ == MultiOptionPolicy::Join) {
... ...
tests/AppTest.cpp
1 1 #include "app_helper.hpp"
2 2 #include <cstdlib>
  3 +#include <complex>
3 4  
4 5 TEST_F(TApp, OneFlagShort) {
5 6 app.add_flag("-c,--count");
... ... @@ -290,6 +291,40 @@ TEST_F(TApp, JoinOpt2) {
290 291 EXPECT_EQ(str, "one\ntwo");
291 292 }
292 293  
  294 +TEST_F(TApp, TakeLastOptMulti) {
  295 + std::vector<int> vals;
  296 + app.add_option("--long", vals)->expected(2)->take_last();
  297 +
  298 + args = {"--long", "1", "2", "3"};
  299 +
  300 + run();
  301 +
  302 + EXPECT_EQ(vals, std::vector<int>({2, 3}));
  303 +}
  304 +
  305 +TEST_F(TApp, TakeFirstOptMulti) {
  306 + std::vector<int> vals;
  307 + app.add_option("--long", vals)->expected(2)->take_first();
  308 +
  309 + args = {"--long", "1", "2", "3"};
  310 +
  311 + run();
  312 +
  313 + EXPECT_EQ(vals, std::vector<int>({1, 2}));
  314 +}
  315 +
  316 +TEST_F(TApp, ComplexOptMulti) {
  317 + std::complex<double> val;
  318 + app.add_complex("--long", val)->take_first();
  319 +
  320 + args = {"--long", "1", "2", "3", "4"};
  321 +
  322 + run();
  323 +
  324 + EXPECT_FLOAT_EQ(val.real(), 1);
  325 + EXPECT_FLOAT_EQ(val.imag(), 2);
  326 +}
  327 +
293 328 TEST_F(TApp, MissingValueNonRequiredOpt) {
294 329 int count;
295 330 app.add_option("-c,--count", count);
... ...