1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 12:43:52 +00:00

Remove double vector parse

This commit is contained in:
Henry Fredrick Schreiner 2017-03-02 13:10:32 -05:00
parent e870e264c3
commit 84add337bb
4 changed files with 37 additions and 67 deletions

View File

@ -1,5 +1,6 @@
## Version 0.6 ## Version 0.6
* Simplified parsing to use `vector<string>` only
* Fixed fallthrough, made it optional as well (default: off): `.fallthrough()`. * Fixed fallthrough, made it optional as well (default: off): `.fallthrough()`.
* Added string versions of `->requires()` and `->excludes()` for consistency. * Added string versions of `->requires()` and `->excludes()` for consistency.
* Renamed protected members for internal consistency, grouped docs. * Renamed protected members for internal consistency, grouped docs.

View File

@ -41,7 +41,6 @@ This library was built to supply the Application object for the GooFit CUDA/OMP
* Ini configuration support is basic (long options only, no vector support), is more needed? Could it be tied to the subcommand system? * Ini configuration support is basic (long options only, no vector support), is more needed? Could it be tied to the subcommand system?
* Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object. * Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object.
* Test "adding to cmake" method * Test "adding to cmake" method
* Maybe simplify the `vector<vector>` structure of options, it is more powerful but unlikely to be needed.
See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details. See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details.
@ -117,7 +116,6 @@ app.add_config(option_name,
required=false) required=false)
App* subcom = app.add_subcommand(name, discription); App* subcom = app.add_subcommand(name, discription);
``` ```
An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`.
@ -145,7 +143,7 @@ The add commands return a pointer to an internally stored `Option`. If you set t
* `->check(CLI::NonexistentPath)`: Requires that the path does not exist. * `->check(CLI::NonexistentPath)`: Requires that the path does not exist.
* `->check(CLI::Range(min,max))`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0. * `->check(CLI::Range(min,max))`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.
These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `bool(std::string)`. If you want to change the default help option, it is available through `get_help_ptr`. These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `bool(std::string)`. If you want to change the default help option, it is available through `get_help_ptr`. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results.
On the command line, options can be given as: On the command line, options can be given as:
@ -201,17 +199,16 @@ Also, in a related note, the `App` you get a pointer to is stored in the parent
## How it works ## How it works
Every `add_` option you have seen so far depends on one method that takes a lambda function. Each of these methods is just making a different lambda function with capture to populate the option. The function has full access to the vector of vector of strings, so it knows how many times an option was passed, and how many arguments each passing received (flags add empty strings to keep the counts correct). The lambda returns `true` if it could validate the option strings, and Every `add_` option you have seen so far depends on one method that takes a lambda function. Each of these methods is just making a different lambda function with capture to populate the option. The function has full access to the vector of strings, so it knows how many times an option was passed or how many arguments it recieved (flags add empty strings to keep the counts correct). The lambda returns `true` if it could validate the option strings, and
`false` if it failed. `false` if it failed.
### Example ### Example
~~~python ```cpp
app.add_option("--fancy-count", [](std::vector<std::vector<std::string>> val){ app.add_option("--fancy-count", [](std::vector<std::string> val){
std::cout << "This option was given " << val.size() << " times." << std::endl std::cout << "This option was given " << val.size() << " times." << std::endl
<< "The first time, it received " << val.at(0).size() << " items" << std::endl;
}); });
~~~ ```
## Contributing ## Contributing
@ -221,7 +218,7 @@ To contribute, open an [issue](https://github.com/henryiii/CLI11/issues) or [pul
If you use the [`rang`](https://github.com/agauniyal/rang/wiki) library to add color to your terminal in a safe, multi-platform way, you can combine it with CLI11 nicely: If you use the [`rang`](https://github.com/agauniyal/rang/wiki) library to add color to your terminal in a safe, multi-platform way, you can combine it with CLI11 nicely:
``` ```cpp
std::atexit([](){std::cout << rang::style::reset;}); std::atexit([](){std::cout << rang::style::reset;});
try { try {
app.parse(argc, argv); app.parse(argc, argv);

View File

@ -227,13 +227,9 @@ public:
CLI::callback_t fun = [&variable](CLI::results_t res){ CLI::callback_t fun = [&variable](CLI::results_t res){
if(res.size()!=1) { if(res.size()!=1)
return false; return false;
} return detail::lexical_cast(res[0], variable);
if(res[0].size()!=1) {
return false;
}
return detail::lexical_cast(res[0][0], variable);
}; };
Option* opt = add_option(name, fun, description, defaulted); Option* opt = add_option(name, fun, description, defaulted);
@ -258,11 +254,10 @@ public:
CLI::callback_t fun = [&variable](CLI::results_t res){ CLI::callback_t fun = [&variable](CLI::results_t res){
bool retval = true; bool retval = true;
variable.clear(); variable.clear();
for(const auto &a : res) for(const auto &a : res) {
for(const auto &b : a) { variable.emplace_back();
variable.emplace_back(); retval &= detail::lexical_cast(a, variable.back());
retval &= detail::lexical_cast(b, variable.back()); }
}
return variable.size() > 0 && retval; return variable.size() > 0 && retval;
}; };
@ -291,7 +286,7 @@ public:
return opt; return opt;
} }
/// Add option for flag /// Add option for flag integer
template<typename T, template<typename T,
enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy> enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy>
Option* add_flag( Option* add_flag(
@ -350,10 +345,7 @@ public:
if(res.size()!=1) { if(res.size()!=1) {
return false; return false;
} }
if(res[0].size()!=1) { bool retval = detail::lexical_cast(res[0], member);
return false;
}
bool retval = detail::lexical_cast(res[0][0], member);
if(!retval) if(!retval)
return false; return false;
return std::find(std::begin(options), std::end(options), member) != std::end(options); return std::find(std::begin(options), std::end(options), member) != std::end(options);
@ -383,10 +375,7 @@ public:
if(res.size()!=1) { if(res.size()!=1) {
return false; return false;
} }
if(res[0].size()!=1) { member = detail::to_lower(res[0]);
return false;
}
member = detail::to_lower(res.at(0).at(0));
auto iter = std::find_if(std::begin(options), std::end(options), auto iter = std::find_if(std::begin(options), std::end(options),
[&member](std::string val){return detail::to_lower(val) == member;}); [&member](std::string val){return detail::to_lower(val) == member;});
if(iter == std::end(options)) if(iter == std::end(options))
@ -558,7 +547,7 @@ public:
std::stringstream out; std::stringstream out;
for(const Option_p &opt : options_) { for(const Option_p &opt : options_) {
if(opt->lnames_.size() > 0 && opt->count() > 0 && opt->get_expected() > 0) if(opt->lnames_.size() > 0 && opt->count() > 0 && opt->get_expected() > 0)
out << opt->lnames_[0] << "=" << detail::join(opt->flatten_results()) << std::endl; out << opt->lnames_[0] << "=" << detail::join(opt->results()) << std::endl;
} }
return out.str(); return out.str();
} }
@ -763,8 +752,7 @@ protected:
if (opt->count() == 0 && opt->envname_ != "") { if (opt->count() == 0 && opt->envname_ != "") {
char *ename = std::getenv(opt->envname_.c_str()); char *ename = std::getenv(opt->envname_.c_str());
if(ename != nullptr) { if(ename != nullptr) {
opt->get_new(); opt->add_result(std::string(ename));
opt->add_result(0, std::string(ename));
} }
} }
} }
@ -851,8 +839,7 @@ protected:
&& opt->count() < opt->get_expected() && opt->count() < opt->get_expected()
) { ) {
opt->get_new(); opt->add_result(positional);
opt->add_result(0, positional);
args.pop_back(); args.pop_back();
return; return;
} }
@ -915,29 +902,28 @@ protected:
// Get a reference to the pointer to make syntax bearable // Get a reference to the pointer to make syntax bearable
Option_p& op = *op_ptr; Option_p& op = *op_ptr;
int vnum = op->get_new();
int num = op->get_expected(); int num = op->get_expected();
if(num == 0) if(num == 0)
op->add_result(vnum, ""); op->add_result("");
else if(rest!="") { else if(rest!="") {
if (num > 0) if (num > 0)
num--; num--;
op->add_result(vnum, rest); op->add_result(rest);
rest = ""; rest = "";
} }
if(num == -1) { if(num == -1) {
while(args.size()>0 && _recognize(args.back()) == detail::Classifer::NONE) { while(args.size()>0 && _recognize(args.back()) == detail::Classifer::NONE) {
op->add_result(vnum, args.back()); op->add_result(args.back());
args.pop_back(); args.pop_back();
} }
} else while(num>0 && args.size() > 0) { } else while(num>0 && args.size() > 0) {
num--; num--;
std::string current_ = args.back(); std::string current_ = args.back();
args.pop_back(); args.pop_back();
op->add_result(vnum, current_); op->add_result(current_);
} }
if(rest != "") { if(rest != "") {
@ -980,25 +966,23 @@ protected:
if(!overwrite && op->count() > 0) if(!overwrite && op->count() > 0)
return; return;
int vnum = op->get_new();
int num = op->get_expected(); int num = op->get_expected();
if(value != "") { if(value != "") {
if(num!=-1) num--; if(num!=-1) num--;
op->add_result(vnum, value); op->add_result(value);
} else if (num == 0) { } else if (num == 0) {
op->add_result(vnum, ""); op->add_result("");
} }
if(num == -1) { if(num == -1) {
while(args.size() > 0 && _recognize(args.back()) == detail::Classifer::NONE) { while(args.size() > 0 && _recognize(args.back()) == detail::Classifer::NONE) {
op->add_result(vnum, args.back()); op->add_result(args.back());
args.pop_back(); args.pop_back();
} }
} else while(num>0 && args.size()>0) { } else while(num>0 && args.size()>0) {
num--; num--;
op->add_result(vnum,args.back()); op->add_result(args.back());
args.pop_back(); args.pop_back();
} }
return; return;

View File

@ -17,7 +17,7 @@
namespace CLI { namespace CLI {
typedef std::vector<std::vector<std::string>> results_t; typedef std::vector<std::string> results_t;
typedef std::function<bool(results_t)> callback_t; typedef std::function<bool(results_t)> callback_t;
class Option; class Option;
@ -121,16 +121,13 @@ public:
/// Count the total number of times an option was passed /// Count the total number of times an option was passed
int count() const { int count() const {
int out = 0; return results_.size();
for(const std::vector<std::string>& vec : results_)
out += vec.size();
return out;
} }
/// This class is true if option is passed. /// This class is true if option is passed.
operator bool() const { operator bool() const {
return results_.size() > 0; return count() > 0;
} }
/// Clear the parsed results (mostly for testing) /// Clear the parsed results (mostly for testing)
@ -372,9 +369,9 @@ public:
/// Process the callback /// Process the callback
void run_callback() const { void run_callback() const {
if(!callback_(results_)) if(!callback_(results_))
throw ConversionError(get_name() + "=" + detail::join(flatten_results())); throw ConversionError(get_name() + "=" + detail::join(results_));
if(validators_.size()>0) { if(validators_.size()>0) {
for(const std::string & result : flatten_results()) for(const std::string & result : results_)
for(const std::function<bool(std::string)> &vali : validators_) for(const std::function<bool(std::string)> &vali : validators_)
if(!vali(result)) if(!vali(result))
throw ValidationError(get_name() + "=" + result); throw ValidationError(get_name() + "=" + result);
@ -440,23 +437,14 @@ public:
/// Puts a result at position r /// Puts a result at position r
void add_result(int r, std::string s) { void add_result(std::string s) {
results_.at(r).push_back(s); results_.push_back(s);
}
/// Starts a new results vector (used for r in add_result)
int get_new() {
results_.emplace_back();
return results_.size() - 1;
} }
/// Produce a flattened vector of results, vs. a vector of vectors. /// Get a copy of the results
std::vector<std::string> flatten_results() const { std::vector<std::string> results() const {
std::vector<std::string> output; return results_;
for(const std::vector<std::string> result : results_)
output.insert(std::end(output), std::begin(result), std::end(result));
return output;
} }
///@} ///@}