Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indexed tuple validation #310

Merged
merged 2 commits into from
Sep 3, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a bug mentioned earlier that -N can match when it should be a number, correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it should fix the bug mentioned here

}
}
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