Skip to content

Commit

Permalink
Support for multi-size MultiOptionPolicies
Browse files Browse the repository at this point in the history
  • Loading branch information
henryiii committed Apr 2, 2018
1 parent c062128 commit ddcca94
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 11 deletions.
2 changes: 1 addition & 1 deletion include/CLI/Error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class IncorrectConstruction : public ConstructionError {
return IncorrectConstruction("Option " + name + " is not defined");
}
static IncorrectConstruction MultiOptionPolicy(std::string name) {
return IncorrectConstruction(name + ": multi_option_policy only works for flags and single value options");
return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
}
};

Expand Down
37 changes: 27 additions & 10 deletions include/CLI/Option.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,8 @@ class Option : public OptionBase<Option> {

/// Take the last argument if given multiple times (or another policy)
Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
// TODO: This can support multiple options
if(get_type_size() != 0 && get_expected() != 1)

if(get_items_expected() < 0)
throw IncorrectConstruction::MultiOptionPolicy(single_name());
multi_option_policy_ = value;
return this;
Expand All @@ -393,12 +393,25 @@ class Option : public OptionBase<Option> {
/// The number of times the option expects to be included
int get_expected() const { return expected_; }

/// The total number of expected values (including the type)
/// \breif The total number of expected values (including the type)
/// This is positive if exactly this number is expected, and negitive for at least N values
///
/// v = fabs(size_type*expected)
/// !MultiOptionPolicy::Throw
/// | Expected < 0 | Expected == 0 | Expected > 0
/// Size < 0 | -v | 0 | -v
/// Size == 0 | 0 | 0 | 0
/// Size > 0 | -v | 0 | -v // Expected must be 1
///
/// MultiOptionPolicy::Throw
/// | Expected < 0 | Expected == 0 | Expected > 0
/// Size < 0 | -v | 0 | v
/// Size == 0 | 0 | 0 | 0
/// Size > 0 | v | 0 | v // Expected must be 1
///
int get_items_expected() const {
// type_size == 0, return 0
// type_size > 1, return type_size_
// type_size < 0, return -type_size * expected;
return type_size_ < 0 ? -1 * type_size_ * expected_ : type_size_;
return std::abs(type_size_ * expected_) *
((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1));
}

/// True if this has a default value
Expand Down Expand Up @@ -518,14 +531,18 @@ class Option : public OptionBase<Option> {

bool local_result;

// Num items expected or length of vector, always at least 1
// Only valid for a trimming policy
int trim_size = std::min(std::max(std::abs(get_items_expected()), 1), static_cast<int>(results_.size()));

// Operation depends on the policy setting
if(multi_option_policy_ == MultiOptionPolicy::TakeLast) {
// TODO: add non-1 size arguments here
results_t partial_result = {results_.back()};
// Allow multi-option sizes (including 0)
results_t partial_result{results_.end() - trim_size, results_.end()};
local_result = !callback_(partial_result);

} else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) {
results_t partial_result = {results_.at(0)};
results_t partial_result{results_.begin(), results_.begin() + trim_size};
local_result = !callback_(partial_result);

} else if(multi_option_policy_ == MultiOptionPolicy::Join) {
Expand Down
35 changes: 35 additions & 0 deletions tests/AppTest.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "app_helper.hpp"
#include <cstdlib>
#include <complex>

TEST_F(TApp, OneFlagShort) {
app.add_flag("-c,--count");
Expand Down Expand Up @@ -290,6 +291,40 @@ TEST_F(TApp, JoinOpt2) {
EXPECT_EQ(str, "one\ntwo");
}

TEST_F(TApp, TakeLastOptMulti) {
std::vector<int> vals;
app.add_option("--long", vals)->expected(2)->take_last();

args = {"--long", "1", "2", "3"};

run();

EXPECT_EQ(vals, std::vector<int>({2, 3}));
}

TEST_F(TApp, TakeFirstOptMulti) {
std::vector<int> vals;
app.add_option("--long", vals)->expected(2)->take_first();

args = {"--long", "1", "2", "3"};

run();

EXPECT_EQ(vals, std::vector<int>({1, 2}));
}

TEST_F(TApp, ComplexOptMulti) {
std::complex<double> val;
app.add_complex("--long", val)->take_first();

args = {"--long", "1", "2", "3", "4"};

run();

EXPECT_FLOAT_EQ(val.real(), 1);
EXPECT_FLOAT_EQ(val.imag(), 2);
}

TEST_F(TApp, MissingValueNonRequiredOpt) {
int count;
app.add_option("-c,--count", count);
Expand Down

0 comments on commit ddcca94

Please sign in to comment.