Commit 447bda047ff97b15feb9f3e4af4edbf526fdf05b

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent de06d50f

Adding size instead of expected + unchangable

include/CLI/App.hpp
@@ -835,7 +835,7 @@ class App { @@ -835,7 +835,7 @@ class App {
835 std::string value; 835 std::string value;
836 836
837 // Non-flags 837 // Non-flags
838 - if(opt->get_expected() != 0) { 838 + if(opt->get_type_size() != 0) {
839 839
840 // If the option was found on command line 840 // If the option was found on command line
841 if(opt->count() > 0) 841 if(opt->count() > 0)
@@ -1069,7 +1069,7 @@ class App { @@ -1069,7 +1069,7 @@ class App {
1069 /// Currently checks to see if multiple positionals exist with -1 args 1069 /// Currently checks to see if multiple positionals exist with -1 args
1070 void _validate() const { 1070 void _validate() const {
1071 auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) { 1071 auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1072 - return opt->get_expected() == -1 && opt->get_positional(); 1072 + return opt->get_items_expected() < 0 && opt->get_positional();
1073 }); 1073 });
1074 if(count > 1) 1074 if(count > 1)
1075 throw InvalidError(name_); 1075 throw InvalidError(name_);
@@ -1189,8 +1189,8 @@ class App { @@ -1189,8 +1189,8 @@ class App {
1189 // Required or partially filled 1189 // Required or partially filled
1190 if(opt->get_required() || opt->count() != 0) { 1190 if(opt->get_required() || opt->count() != 0) {
1191 // Make sure enough -N arguments parsed (+N is already handled in parsing function) 1191 // Make sure enough -N arguments parsed (+N is already handled in parsing function)
1192 - if(opt->get_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_expected()))  
1193 - throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_expected()); 1192 + if(opt->get_items_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_items_expected()))
  1193 + throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_items_expected());
1194 1194
1195 // Required but empty 1195 // Required but empty
1196 if(opt->get_required() && opt->count() == 0) 1196 if(opt->get_required() && opt->count() == 0)
@@ -1260,7 +1260,7 @@ class App { @@ -1260,7 +1260,7 @@ class App {
1260 1260
1261 if(op->results_.empty()) { 1261 if(op->results_.empty()) {
1262 // Flag parsing 1262 // Flag parsing
1263 - if(op->get_expected() == 0) { 1263 + if(op->get_type_size() == 0) {
1264 if(current.inputs.size() == 1) { 1264 if(current.inputs.size() == 1) {
1265 std::string val = current.inputs.at(0); 1265 std::string val = current.inputs.at(0);
1266 val = detail::to_lower(val); 1266 val = detail::to_lower(val);
@@ -1320,9 +1320,9 @@ class App { @@ -1320,9 +1320,9 @@ class App {
1320 size_t _count_remaining_positionals(bool required = false) const { 1320 size_t _count_remaining_positionals(bool required = false) const {
1321 size_t retval = 0; 1321 size_t retval = 0;
1322 for(const Option_p &opt : options_) 1322 for(const Option_p &opt : options_)
1323 - if(opt->get_positional() && (!required || opt->get_required()) && opt->get_expected() > 0 &&  
1324 - static_cast<int>(opt->count()) < opt->get_expected())  
1325 - retval = static_cast<size_t>(opt->get_expected()) - opt->count(); 1323 + if(opt->get_positional() && (!required || opt->get_required()) && opt->get_items_expected() > 0 &&
  1324 + static_cast<int>(opt->count()) < opt->get_items_expected())
  1325 + retval = static_cast<size_t>(opt->get_items_expected()) - opt->count();
1326 1326
1327 return retval; 1327 return retval;
1328 } 1328 }
@@ -1334,7 +1334,7 @@ class App { @@ -1334,7 +1334,7 @@ class App {
1334 for(const Option_p &opt : options_) { 1334 for(const Option_p &opt : options_) {
1335 // Eat options, one by one, until done 1335 // Eat options, one by one, until done
1336 if(opt->get_positional() && 1336 if(opt->get_positional() &&
1337 - (static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) { 1337 + (static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) {
1338 1338
1339 opt->add_result(positional); 1339 opt->add_result(positional);
1340 parse_order_.push_back(opt.get()); 1340 parse_order_.push_back(opt.get());
@@ -1421,7 +1421,7 @@ class App { @@ -1421,7 +1421,7 @@ class App {
1421 // Get a reference to the pointer to make syntax bearable 1421 // Get a reference to the pointer to make syntax bearable
1422 Option_p &op = *op_ptr; 1422 Option_p &op = *op_ptr;
1423 1423
1424 - int num = op->get_expected(); 1424 + int num = op->get_items_expected();
1425 1425
1426 // Make sure we always eat the minimum for unlimited vectors 1426 // Make sure we always eat the minimum for unlimited vectors
1427 int collected = 0; 1427 int collected = 0;
@@ -1459,7 +1459,7 @@ class App { @@ -1459,7 +1459,7 @@ class App {
1459 1459
1460 // If there are any unlimited positionals, those also take priority 1460 // If there are any unlimited positionals, those also take priority
1461 if(std::any_of(std::begin(options_), std::end(options_), [](const Option_p &opt) { 1461 if(std::any_of(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1462 - return opt->get_positional() && opt->get_expected() < 0; 1462 + return opt->get_positional() && opt->get_items_expected() < 0;
1463 })) 1463 }))
1464 break; 1464 break;
1465 } 1465 }
include/CLI/Error.hpp
@@ -91,6 +91,9 @@ class IncorrectConstruction : public ConstructionError { @@ -91,6 +91,9 @@ class IncorrectConstruction : public ConstructionError {
91 static IncorrectConstruction Set0Opt(std::string name) { 91 static IncorrectConstruction Set0Opt(std::string name) {
92 return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); 92 return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
93 } 93 }
  94 + static IncorrectConstruction SetFlag(std::string name) {
  95 + return IncorrectConstruction(name + ": Cannot set an expected number for flags");
  96 + }
94 static IncorrectConstruction ChangeNotVector(std::string name) { 97 static IncorrectConstruction ChangeNotVector(std::string name) {
95 return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); 98 return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
96 } 99 }
include/CLI/Option.hpp
@@ -180,11 +180,13 @@ class Option : public OptionBase&lt;Option&gt; { @@ -180,11 +180,13 @@ class Option : public OptionBase&lt;Option&gt; {
180 /// @name Configuration 180 /// @name Configuration
181 ///@{ 181 ///@{
182 182
183 - /// The number of expected values, 0 for flag, -1 for unlimited vector  
184 - int expected_{1}; 183 + /// The number of arguments that make up one option. -1=unlimited (vector-like), 0=flag, 1=normal option,
  184 + /// 2=complex/pair, etc. Set only when the option is created; this is intrinsic to the type. Eventually, -2 may mean
  185 + /// vector of pairs.
  186 + int type_size_{1};
185 187
186 - /// A private setting to allow args to not be able to accept incorrect expected values  
187 - bool changeable_{false}; 188 + /// The number of expected values, type_size_ must be < 0. Ignored for flag. N < 0 means at least -N values.
  189 + int expected_{1};
188 190
189 /// A list of validators to run on each value parsed 191 /// A list of validators to run on each value parsed
190 std::vector<std::function<std::string(std::string &)>> validators_; 192 std::vector<std::function<std::string(std::string &)>> validators_;
@@ -244,14 +246,25 @@ class Option : public OptionBase&lt;Option&gt; { @@ -244,14 +246,25 @@ class Option : public OptionBase&lt;Option&gt; {
244 /// @name Setting options 246 /// @name Setting options
245 ///@{ 247 ///@{
246 248
247 - /// Set the number of expected arguments (Flags bypass this) 249 + /// Set the number of expected arguments (Flags don't use this)
248 Option *expected(int value) { 250 Option *expected(int value) {
249 - if(expected_ == value)  
250 - return this; 251 + // Break if this is a flag
  252 + if(type_size_ == 0)
  253 + throw IncorrectConstruction::SetFlag(single_name());
  254 +
  255 + // Setting 0 is not allowed
251 else if(value == 0) 256 else if(value == 0)
252 throw IncorrectConstruction::Set0Opt(single_name()); 257 throw IncorrectConstruction::Set0Opt(single_name());
253 - else if(!changeable_) 258 +
  259 + // No change is okay, quit now
  260 + else if(expected_ == value)
  261 + return this;
  262 +
  263 + // Type must be a vector
  264 + else if(type_size_ >= 0)
254 throw IncorrectConstruction::ChangeNotVector(single_name()); 265 throw IncorrectConstruction::ChangeNotVector(single_name());
  266 +
  267 + // TODO: Can support multioption for non-1 values (except for join)
255 else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw) 268 else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
256 throw IncorrectConstruction::AfterMultiOpt(single_name()); 269 throw IncorrectConstruction::AfterMultiOpt(single_name());
257 270
@@ -368,9 +381,10 @@ class Option : public OptionBase&lt;Option&gt; { @@ -368,9 +381,10 @@ class Option : public OptionBase&lt;Option&gt; {
368 return this; 381 return this;
369 } 382 }
370 383
371 - /// Take the last argument if given multiple times 384 + /// Take the last argument if given multiple times (or another policy)
372 Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) { 385 Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
373 - if(get_expected() != 0 && get_expected() != 1) 386 + // TODO: This can support multiple options
  387 + if(get_type_size() != 0 && get_expected() != 1)
374 throw IncorrectConstruction::MultiOptionPolicy(single_name()); 388 throw IncorrectConstruction::MultiOptionPolicy(single_name());
375 multi_option_policy_ = value; 389 multi_option_policy_ = value;
376 return this; 390 return this;
@@ -381,8 +395,19 @@ class Option : public OptionBase&lt;Option&gt; { @@ -381,8 +395,19 @@ class Option : public OptionBase&lt;Option&gt; {
381 ///@{ 395 ///@{
382 396
383 /// The number of arguments the option expects 397 /// The number of arguments the option expects
  398 + int get_type_size() const { return type_size_; }
  399 +
  400 + /// The number of times the option expects to be included
384 int get_expected() const { return expected_; } 401 int get_expected() const { return expected_; }
385 402
  403 + /// The total number of expected values (including the type)
  404 + 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_;
  409 + }
  410 +
386 /// True if this has a default value 411 /// True if this has a default value
387 int get_default() const { return default_; } 412 int get_default() const { return default_; }
388 413
@@ -428,7 +453,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -428,7 +453,7 @@ class Option : public OptionBase&lt;Option&gt; {
428 return out; 453 return out;
429 } 454 }
430 455
431 - /// The most discriptive name available 456 + /// The most descriptive name available
432 std::string single_name() const { 457 std::string single_name() const {
433 if(!lnames_.empty()) 458 if(!lnames_.empty())
434 return std::string("--") + lnames_[0]; 459 return std::string("--") + lnames_[0];
@@ -456,7 +481,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -456,7 +481,7 @@ class Option : public OptionBase&lt;Option&gt; {
456 std::string help_aftername() const { 481 std::string help_aftername() const {
457 std::stringstream out; 482 std::stringstream out;
458 483
459 - if(get_expected() != 0) { 484 + if(get_type_size() != 0) {
460 if(!typeval_.empty()) 485 if(!typeval_.empty())
461 out << " " << typeval_; 486 out << " " << typeval_;
462 if(!defaultval_.empty()) 487 if(!defaultval_.empty())
@@ -502,18 +527,23 @@ class Option : public OptionBase&lt;Option&gt; { @@ -502,18 +527,23 @@ class Option : public OptionBase&lt;Option&gt; {
502 527
503 // Operation depends on the policy setting 528 // Operation depends on the policy setting
504 if(multi_option_policy_ == MultiOptionPolicy::TakeLast) { 529 if(multi_option_policy_ == MultiOptionPolicy::TakeLast) {
  530 + // TODO: add non-1 size arguments here
505 results_t partial_result = {results_.back()}; 531 results_t partial_result = {results_.back()};
506 local_result = !callback_(partial_result); 532 local_result = !callback_(partial_result);
  533 +
507 } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) { 534 } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) {
508 results_t partial_result = {results_.at(0)}; 535 results_t partial_result = {results_.at(0)};
509 local_result = !callback_(partial_result); 536 local_result = !callback_(partial_result);
  537 +
510 } else if(multi_option_policy_ == MultiOptionPolicy::Join) { 538 } else if(multi_option_policy_ == MultiOptionPolicy::Join) {
511 results_t partial_result = {detail::join(results_, "\n")}; 539 results_t partial_result = {detail::join(results_, "\n")};
512 local_result = !callback_(partial_result); 540 local_result = !callback_(partial_result);
  541 +
513 } else { 542 } else {
514 - if((expected_ > 0 && results_.size() != static_cast<size_t>(expected_)) ||  
515 - (expected_ < 0 && results_.size() < static_cast<size_t>(-expected_)))  
516 - throw ArgumentMismatch(single_name(), expected_, results_.size()); 543 + // For now, vector of non size 1 types are not supported but possibility included here
  544 + if((get_items_expected() > 0 && results_.size() != static_cast<size_t>(get_items_expected())) ||
  545 + (get_items_expected() < 0 && results_.size() < static_cast<size_t>(-get_items_expected())))
  546 + throw ArgumentMismatch(single_name(), get_items_expected(), results_.size());
517 else 547 else
518 local_result = !callback_(results_); 548 local_result = !callback_(results_);
519 } 549 }
@@ -595,13 +625,14 @@ class Option : public OptionBase&lt;Option&gt; { @@ -595,13 +625,14 @@ class Option : public OptionBase&lt;Option&gt; {
595 /// @name Custom options 625 /// @name Custom options
596 ///@{ 626 ///@{
597 627
598 - /// Set a custom option, typestring, expected; locks changeable unless expected is -1  
599 - void set_custom_option(std::string typeval, int expected = 1) { 628 + /// Set a custom option, typestring, type_size
  629 + void set_custom_option(std::string typeval, int type_size = 1) {
600 typeval_ = typeval; 630 typeval_ = typeval;
601 - expected_ = expected;  
602 - if(expected == 0) 631 + type_size_ = type_size;
  632 + if(type_size_ == 0)
603 required_ = false; 633 required_ = false;
604 - changeable_ = expected < 0; 634 + if(type_size < 0)
  635 + expected_ = -1;
605 } 636 }
606 637
607 /// Set the default value string representation 638 /// Set the default value string representation
tests/CreationTest.cpp
@@ -125,7 +125,7 @@ TEST_F(TApp, IncorrectConstructionFlagPositional3) { @@ -125,7 +125,7 @@ TEST_F(TApp, IncorrectConstructionFlagPositional3) {
125 125
126 TEST_F(TApp, IncorrectConstructionFlagExpected) { 126 TEST_F(TApp, IncorrectConstructionFlagExpected) {
127 auto cat = app.add_flag("--cat"); 127 auto cat = app.add_flag("--cat");
128 - EXPECT_NO_THROW(cat->expected(0)); 128 + EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction);
129 EXPECT_THROW(cat->expected(1), CLI::IncorrectConstruction); 129 EXPECT_THROW(cat->expected(1), CLI::IncorrectConstruction);
130 } 130 }
131 131