Skip to content

Commit

Permalink
add separate condition for index into vectors
Browse files Browse the repository at this point in the history
remove restrictions on tuple size, and add some additional tests and modified documentation

fix some issues with the negative number check

add some test for indexed validation on tuple

allow specific validators for specific elements in a type with multiple values, or to just apply to the last given argument
  • Loading branch information
phlptp committed Aug 19, 2019
1 parent 06ab2d0 commit b7b2bb7
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 160 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ After I wrote this, I also found the following libraries:
| [Clara][] | Simple library built for the excellent [Catch][] testing framework. Unique syntax, limited scope. |
| [Argh!][] | Very minimalistic C++11 parser, single header. Don't have many features. No help generation?!?! At least it's exception-free. |
| [CLI][] | Custom language and parser. Huge build-system overkill for very little benefit. Last release in 2009, but still occasionally active. |
|[argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. |
| [argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. |

See [Awesome C++][] for a less-biased list of parsers. You can also find other single file libraries at [Single file libs][].

Expand Down Expand Up @@ -194,15 +194,15 @@ While all options internally are the same type, there are several ways to add an
app.add_option(option_name, help_str="") // 🆕

app.add_option(option_name,
variable_to_bind_to, // bool, int, float, vector, 🆕 enum, or string-like, or anything with a defined conversion from a string or that takes an int🚧, double🚧, or string in a constructor. Also allowed are tuples(up to 5 elements) and tuple like structures such as std::array or std::pair.
variable_to_bind_to, // bool, int, float, vector, 🆕 enum, or string-like, or anything with a defined conversion from a string or that takes an int🚧, double🚧, or string in a constructor. Also allowed are tuples🚧,std::array🚧 or std::pair🚧.
help_string="")

app.add_option_function<type>(option_name,
function <void(const type &value)>, // 🆕 int, bool, float, enum, or string-like, or anything with a defined conversion from a string, or a vector of any of the previous objects.
help_string="")

app.add_complex(... // Special case: support for complex numbers
//🚧There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value.
//🚧There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
app.add_option<typename T, typename XC>(option_name,
T &output, // output must be assignable or constructible from a value of type XC
help_string="")
Expand Down Expand Up @@ -261,7 +261,7 @@ otherwise the output would default to a string. The add_option can be used with

Type such as optional<int>, optional<double>, and optional<string> are supported directly, other optional types can be added using the two parameter template. See [CLI11 Internals][] for information on how this could done and how you can add your own converters for additional types.

Vector types can also be used int the two parameter template overload
Vector types can also be used in the two parameter template overload
```
std::vector<double> v1;
app.add_option<std::vector<double>,int>("--vs",v1);
Expand Down
12 changes: 9 additions & 3 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1859,8 +1859,14 @@ class App {
return detail::Classifier::SUBCOMMAND;
if(detail::split_long(current, dummy1, dummy2))
return detail::Classifier::LONG;
if(detail::split_short(current, dummy1, dummy2))
if(detail::split_short(current, dummy1, dummy2)) {
if(dummy1[0] >= '0' && dummy1[0] <= '9') {
if(get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {
return detail::Classifier::NONE;
}
}
return detail::Classifier::SHORT;
}
if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
return detail::Classifier::WINDOWS;
if((current == "++") && !name_.empty() && parent_ != nullptr)
Expand Down Expand Up @@ -2314,7 +2320,7 @@ class App {
(opt->get_items_expected() < 0 && opt->count() == 0lu)) {
if(validate_positionals_) {
std::string pos = positional;
pos = opt->_validate(pos);
pos = opt->_validate(pos, 0);
if(!pos.empty()) {
continue;
}
Expand All @@ -2334,7 +2340,7 @@ class App {
(static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) {
if(validate_positionals_) {
std::string pos = positional;
pos = opt->_validate(pos);
pos = opt->_validate(pos, 0);
if(!pos.empty()) {
continue;
}
Expand Down
39 changes: 30 additions & 9 deletions include/CLI/Option.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,17 @@ class Option : public OptionBase<Option> {
if((validator_name.empty()) && (!validators_.empty())) {
return &(validators_.front());
}
throw OptionNotFound(std::string("Validator ") + validator_name + " Not Found");
throw OptionNotFound(std::string{"Validator "} + validator_name + " Not Found");
}

/// Get a Validator by index NOTE: this may not be the order of definition
Validator *get_validator(int index) {
if(index >= 0 && index < static_cast<int>(validators_.size())) {
return &(validators_[index]);
}
throw OptionNotFound("Validator index is not valid");
}

/// Sets required options
Option *needs(Option *opt) {
auto tup = needs_.insert(opt);
Expand Down Expand Up @@ -679,8 +688,17 @@ class Option : public OptionBase<Option> {

// Run the validators (can change the string)
if(!validators_.empty()) {
int index = 0;
// this is not available until multi_option_policy with type_size_>0 is enabled and functional
// if(type_size_ > 0 && multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
// index = type_size_ - static_cast<int>(results_.size());
//}
if(type_size_ < 0 && multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) { // for vector operations
index = expected_ - static_cast<int>(results_.size());
}
for(std::string &result : results_) {
auto err_msg = _validate(result);
auto err_msg = _validate(result, index);
++index;
if(!err_msg.empty())
throw ValidationError(get_name(), err_msg);
}
Expand Down Expand Up @@ -977,16 +995,19 @@ class Option : public OptionBase<Option> {

private:
// Run a result through the validators
std::string _validate(std::string &result) {
std::string _validate(std::string &result, int index) {
std::string err_msg;
for(const auto &vali : validators_) {
try {
err_msg = vali(result);
} catch(const ValidationError &err) {
err_msg = err.what();
auto v = vali.get_application_index();
if(v == -1 || v == index) {
try {
err_msg = vali(result);
} catch(const ValidationError &err) {
err_msg = err.what();
}
if(!err_msg.empty())
break;
}
if(!err_msg.empty())
break;
}
return err_msg;
}
Expand Down
Loading

0 comments on commit b7b2bb7

Please sign in to comment.