Commit 9f81385bcd14e6dcd741fae6396713582a332f56
Committed by
Henry Schreiner
1 parent
7254ea79
Option delimiter (#240)
* move delimiter controls to Option vs in the App callbacks
Showing
5 changed files
with
151 additions
and
104 deletions
README.md
| ... | ... | @@ -273,6 +273,7 @@ Before parsing, you can set the following options: |
| 273 | 273 | - `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments). |
| 274 | 274 | - `->ignore_underscore()`: Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with "optionone". This does not apply to short form options since they only have one character |
| 275 | 275 | - `->disable_flag_override()`: from the command line long form flag option can be assigned a value on the command line using the `=` notation `--flag=value`. If this behavior is not desired, the `disable_flag_override()` disables it and will generate an exception if it is done on the command line. The `=` does not work with short form flag options. |
| 276 | +- `->delimiter(char)`: allows specification of a custom delimiter for separating single arguments into vector arguments, for example specifying `->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3 elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a vector value | |
| 276 | 277 | - `->description(str)`: Set/change the description. |
| 277 | 278 | - `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which do not inherit their default but always start with a specific policy). |
| 278 | 279 | - `->check(CLI::IsMember(...))`: Require an option be a member of a given set. See below for options. |
| ... | ... | @@ -338,9 +339,7 @@ In most cases the fastest and easiest way is to return the results through a cal |
| 338 | 339 | |
| 339 | 340 | - `results()`: retrieves a vector of strings with all the results in the order they were given. |
| 340 | 341 | - `results(variable_to_bind_to)`: gets the results according to the MultiOptionPolicy and converts them just like the `add_option_function` with a variable. |
| 341 | -- `results(vector_type_variable,delimiter)`: gets the results to a vector type and uses a delimiter to further split the values | |
| 342 | -- `Value=as<type>()`: returns the result or default value directly as the specified type if possible. | |
| 343 | -- `Vector_value=as<type>(delimiter): same the results function with the delimiter but returns the value directly. | |
| 342 | +- `Value=as<type>()`: returns the result or default value directly as the specified type if possible, can be vector to return all results, and a non-vector to get the result according to the MultiOptionPolicy in place. | |
| 344 | 343 | |
| 345 | 344 | ### Subcommands |
| 346 | 345 | |
| ... | ... | @@ -434,7 +433,7 @@ arguments, use `.config_to_str(default_also=false, prefix="", write_description= |
| 434 | 433 | |
| 435 | 434 | Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`, `footer`, and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well. |
| 436 | 435 | |
| 437 | -Options have defaults for `group`, `required`, `disable_flag_override`,`multi_option_policy`, `ignore_underscore`, and `ignore_case`. To set these defaults, you should set the `option_defaults()` object, for example: | |
| 436 | +Options have defaults for `group`, `required`, `disable_flag_override`,`multi_option_policy`, `ignore_underscore`,`delimiter`, and `ignore_case`. To set these defaults, you should set the `option_defaults()` object, for example: | |
| 438 | 437 | |
| 439 | 438 | ```cpp |
| 440 | 439 | app.option_defaults()->required(); | ... | ... |
include/CLI/App.hpp
| ... | ... | @@ -435,28 +435,20 @@ class App { |
| 435 | 435 | return opt; |
| 436 | 436 | } |
| 437 | 437 | |
| 438 | - /// Add option for vectors (no default) | |
| 438 | + /// Add option for vectors | |
| 439 | 439 | template <typename T> |
| 440 | 440 | Option *add_option(std::string option_name, |
| 441 | 441 | std::vector<T> &variable, ///< The variable vector to set |
| 442 | - std::string option_description = "", | |
| 443 | - char delimiter = '\0') { | |
| 442 | + std::string option_description = "") { | |
| 444 | 443 | |
| 445 | - CLI::callback_t fun = [&variable, delimiter](CLI::results_t res) { | |
| 444 | + CLI::callback_t fun = [&variable](CLI::results_t res) { | |
| 446 | 445 | bool retval = true; |
| 447 | 446 | variable.clear(); |
| 447 | + variable.reserve(res.size()); | |
| 448 | 448 | for(const auto &elem : res) { |
| 449 | - if((delimiter != '\0') && (elem.find_first_of(delimiter) != std::string::npos)) { | |
| 450 | - for(const auto &var : CLI::detail::split(elem, delimiter)) { | |
| 451 | - if(!var.empty()) { | |
| 452 | - variable.emplace_back(); | |
| 453 | - retval &= detail::lexical_cast(var, variable.back()); | |
| 454 | - } | |
| 455 | - } | |
| 456 | - } else { | |
| 457 | - variable.emplace_back(); | |
| 458 | - retval &= detail::lexical_cast(elem, variable.back()); | |
| 459 | - } | |
| 449 | + | |
| 450 | + variable.emplace_back(); | |
| 451 | + retval &= detail::lexical_cast(elem, variable.back()); | |
| 460 | 452 | } |
| 461 | 453 | return (!variable.empty()) && retval; |
| 462 | 454 | }; |
| ... | ... | @@ -466,35 +458,28 @@ class App { |
| 466 | 458 | return opt; |
| 467 | 459 | } |
| 468 | 460 | |
| 469 | - /// Add option for vectors | |
| 461 | + /// Add option for vectors with defaulted argument | |
| 470 | 462 | template <typename T> |
| 471 | 463 | Option *add_option(std::string option_name, |
| 472 | 464 | std::vector<T> &variable, ///< The variable vector to set |
| 473 | 465 | std::string option_description, |
| 474 | - bool defaulted, | |
| 475 | - char delimiter = '\0') { | |
| 466 | + bool defaulted) { | |
| 476 | 467 | |
| 477 | - CLI::callback_t fun = [&variable, delimiter](CLI::results_t res) { | |
| 468 | + CLI::callback_t fun = [&variable](CLI::results_t res) { | |
| 478 | 469 | bool retval = true; |
| 479 | 470 | variable.clear(); |
| 471 | + variable.reserve(res.size()); | |
| 480 | 472 | for(const auto &elem : res) { |
| 481 | - if((delimiter != '\0') && (elem.find_first_of(delimiter) != std::string::npos)) { | |
| 482 | - for(const auto &var : CLI::detail::split(elem, delimiter)) { | |
| 483 | - if(!var.empty()) { | |
| 484 | - variable.emplace_back(); | |
| 485 | - retval &= detail::lexical_cast(var, variable.back()); | |
| 486 | - } | |
| 487 | - } | |
| 488 | - } else { | |
| 489 | - variable.emplace_back(); | |
| 490 | - retval &= detail::lexical_cast(elem, variable.back()); | |
| 491 | - } | |
| 473 | + | |
| 474 | + variable.emplace_back(); | |
| 475 | + retval &= detail::lexical_cast(elem, variable.back()); | |
| 492 | 476 | } |
| 493 | 477 | return (!variable.empty()) && retval; |
| 494 | 478 | }; |
| 495 | 479 | |
| 496 | 480 | Option *opt = add_option(option_name, fun, option_description, defaulted); |
| 497 | 481 | opt->type_name(detail::type_name<T>())->type_size(-1); |
| 482 | + | |
| 498 | 483 | if(defaulted) |
| 499 | 484 | opt->default_str("[" + detail::join(variable) + "]"); |
| 500 | 485 | return opt; |
| ... | ... | @@ -504,25 +489,15 @@ class App { |
| 504 | 489 | template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy> |
| 505 | 490 | Option *add_option_function(std::string option_name, |
| 506 | 491 | const std::function<bool(const T &)> &func, ///< the callback to execute |
| 507 | - std::string option_description = "", | |
| 508 | - char delimiter = '\0') { | |
| 492 | + std::string option_description = "") { | |
| 509 | 493 | |
| 510 | - CLI::callback_t fun = [func, delimiter](CLI::results_t res) { | |
| 494 | + CLI::callback_t fun = [func](CLI::results_t res) { | |
| 511 | 495 | T values; |
| 512 | 496 | bool retval = true; |
| 513 | 497 | values.reserve(res.size()); |
| 514 | 498 | for(const auto &elem : res) { |
| 515 | - if((delimiter != '\0') && (elem.find_first_of(delimiter) != std::string::npos)) { | |
| 516 | - for(const auto &var : CLI::detail::split(elem, delimiter)) { | |
| 517 | - if(!var.empty()) { | |
| 518 | - values.emplace_back(); | |
| 519 | - retval &= detail::lexical_cast(var, values.back()); | |
| 520 | - } | |
| 521 | - } | |
| 522 | - } else { | |
| 523 | - values.emplace_back(); | |
| 524 | - retval &= detail::lexical_cast(elem, values.back()); | |
| 525 | - } | |
| 499 | + values.emplace_back(); | |
| 500 | + retval &= detail::lexical_cast(elem, values.back()); | |
| 526 | 501 | } |
| 527 | 502 | if(retval) { |
| 528 | 503 | return func(values); |
| ... | ... | @@ -2032,6 +2007,7 @@ class App { |
| 2032 | 2007 | // Make sure we always eat the minimum for unlimited vectors |
| 2033 | 2008 | int collected = 0; |
| 2034 | 2009 | // deal with flag like things |
| 2010 | + int count = 0; | |
| 2035 | 2011 | if(num == 0) { |
| 2036 | 2012 | auto res = op->get_flag_value(arg_name, value); |
| 2037 | 2013 | op->add_result(res); |
| ... | ... | @@ -2039,20 +2015,22 @@ class App { |
| 2039 | 2015 | } |
| 2040 | 2016 | // --this=value |
| 2041 | 2017 | else if(!value.empty()) { |
| 2018 | + op->add_result(value, count); | |
| 2019 | + parse_order_.push_back(op.get()); | |
| 2020 | + collected += count; | |
| 2042 | 2021 | // If exact number expected |
| 2043 | 2022 | if(num > 0) |
| 2044 | - num--; | |
| 2045 | - op->add_result(value); | |
| 2046 | - parse_order_.push_back(op.get()); | |
| 2047 | - collected += 1; | |
| 2023 | + num = (num >= count) ? num - count : 0; | |
| 2024 | + | |
| 2048 | 2025 | // -Trest |
| 2049 | 2026 | } else if(!rest.empty()) { |
| 2050 | - if(num > 0) | |
| 2051 | - num--; | |
| 2052 | - op->add_result(rest); | |
| 2027 | + op->add_result(rest, count); | |
| 2053 | 2028 | parse_order_.push_back(op.get()); |
| 2054 | 2029 | rest = ""; |
| 2055 | - collected += 1; | |
| 2030 | + collected += count; | |
| 2031 | + // If exact number expected | |
| 2032 | + if(num > 0) | |
| 2033 | + num = (num >= count) ? num - count : 0; | |
| 2056 | 2034 | } |
| 2057 | 2035 | |
| 2058 | 2036 | // Unlimited vector parser |
| ... | ... | @@ -2065,10 +2043,10 @@ class App { |
| 2065 | 2043 | if(_count_remaining_positionals() > 0) |
| 2066 | 2044 | break; |
| 2067 | 2045 | } |
| 2068 | - op->add_result(args.back()); | |
| 2046 | + op->add_result(args.back(), count); | |
| 2069 | 2047 | parse_order_.push_back(op.get()); |
| 2070 | 2048 | args.pop_back(); |
| 2071 | - collected++; | |
| 2049 | + collected += count; | |
| 2072 | 2050 | } |
| 2073 | 2051 | |
| 2074 | 2052 | // Allow -- to end an unlimited list and "eat" it |
| ... | ... | @@ -2077,11 +2055,11 @@ class App { |
| 2077 | 2055 | |
| 2078 | 2056 | } else { |
| 2079 | 2057 | while(num > 0 && !args.empty()) { |
| 2080 | - num--; | |
| 2081 | 2058 | std::string current_ = args.back(); |
| 2082 | 2059 | args.pop_back(); |
| 2083 | - op->add_result(current_); | |
| 2060 | + op->add_result(current_, count); | |
| 2084 | 2061 | parse_order_.push_back(op.get()); |
| 2062 | + num -= count; | |
| 2085 | 2063 | } |
| 2086 | 2064 | |
| 2087 | 2065 | if(num > 0) { | ... | ... |
include/CLI/Option.hpp
| ... | ... | @@ -52,7 +52,8 @@ template <typename CRTP> class OptionBase { |
| 52 | 52 | bool configurable_{true}; |
| 53 | 53 | /// Disable overriding flag values with '=value' |
| 54 | 54 | bool disable_flag_override_{false}; |
| 55 | - | |
| 55 | + /// Specify a delimiter character for vector arguments | |
| 56 | + char delimiter_{'\0'}; | |
| 56 | 57 | /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too) |
| 57 | 58 | MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw}; |
| 58 | 59 | |
| ... | ... | @@ -64,6 +65,7 @@ template <typename CRTP> class OptionBase { |
| 64 | 65 | other->ignore_underscore(ignore_underscore_); |
| 65 | 66 | other->configurable(configurable_); |
| 66 | 67 | other->disable_flag_override(disable_flag_override_); |
| 68 | + other->delimiter(delimiter_); | |
| 67 | 69 | other->multi_option_policy(multi_option_policy_); |
| 68 | 70 | } |
| 69 | 71 | |
| ... | ... | @@ -106,6 +108,7 @@ template <typename CRTP> class OptionBase { |
| 106 | 108 | /// The status of configurable |
| 107 | 109 | bool get_disable_flag_override() const { return disable_flag_override_; } |
| 108 | 110 | |
| 111 | + char get_delimiter() const { return delimiter_; } | |
| 109 | 112 | /// The status of the multi option policy |
| 110 | 113 | MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; } |
| 111 | 114 | |
| ... | ... | @@ -137,6 +140,12 @@ template <typename CRTP> class OptionBase { |
| 137 | 140 | configurable_ = value; |
| 138 | 141 | return static_cast<CRTP *>(this); |
| 139 | 142 | } |
| 143 | + | |
| 144 | + /// Allow in a configuration file | |
| 145 | + CRTP *delimiter(char value = '\0') { | |
| 146 | + delimiter_ = value; | |
| 147 | + return static_cast<CRTP *>(this); | |
| 148 | + } | |
| 140 | 149 | }; |
| 141 | 150 | |
| 142 | 151 | /// This is a version of OptionBase that only supports setting values, |
| ... | ... | @@ -165,11 +174,17 @@ class OptionDefaults : public OptionBase<OptionDefaults> { |
| 165 | 174 | return this; |
| 166 | 175 | } |
| 167 | 176 | |
| 168 | - /// Ignore underscores in the option name | |
| 177 | + /// Disable overriding flag values with an '=<value>' segment | |
| 169 | 178 | OptionDefaults *disable_flag_override(bool value = true) { |
| 170 | 179 | disable_flag_override_ = value; |
| 171 | 180 | return this; |
| 172 | 181 | } |
| 182 | + | |
| 183 | + /// set a delimiter character to split up single arguments to treat as multiple inputs | |
| 184 | + OptionDefaults *delimiter(char value = '\0') { | |
| 185 | + delimiter_ = value; | |
| 186 | + return this; | |
| 187 | + } | |
| 173 | 188 | }; |
| 174 | 189 | |
| 175 | 190 | class Option : public OptionBase<Option> { |
| ... | ... | @@ -793,19 +808,23 @@ class Option : public OptionBase<Option> { |
| 793 | 808 | |
| 794 | 809 | /// Puts a result at the end |
| 795 | 810 | Option *add_result(std::string s) { |
| 796 | - results_.push_back(std::move(s)); | |
| 811 | + _add_result(std::move(s)); | |
| 812 | + callback_run_ = false; | |
| 813 | + return this; | |
| 814 | + } | |
| 815 | + | |
| 816 | + /// Puts a result at the end and get a count of the number of arguments actually added | |
| 817 | + Option *add_result(std::string s, int &count) { | |
| 818 | + count = _add_result(std::move(s)); | |
| 797 | 819 | callback_run_ = false; |
| 798 | 820 | return this; |
| 799 | 821 | } |
| 800 | 822 | |
| 801 | 823 | /// Puts a result at the end |
| 802 | 824 | Option *add_result(std::vector<std::string> s) { |
| 803 | - if(results_.empty()) { | |
| 804 | - results_ = std::move(s); | |
| 805 | - } else { | |
| 806 | - results_.insert(results_.end(), s.begin(), s.end()); | |
| 825 | + for(auto &str : s) { | |
| 826 | + _add_result(std::move(str)); | |
| 807 | 827 | } |
| 808 | - | |
| 809 | 828 | callback_run_ = false; |
| 810 | 829 | return this; |
| 811 | 830 | } |
| ... | ... | @@ -850,22 +869,13 @@ class Option : public OptionBase<Option> { |
| 850 | 869 | } |
| 851 | 870 | } |
| 852 | 871 | /// get the results as a vector of a particular type |
| 853 | - template <typename T> void results(std::vector<T> &output, char delim = '\0') const { | |
| 872 | + template <typename T> void results(std::vector<T> &output) const { | |
| 854 | 873 | output.clear(); |
| 855 | 874 | bool retval = true; |
| 856 | 875 | |
| 857 | 876 | for(const auto &elem : results_) { |
| 858 | - if(delim != '\0') { | |
| 859 | - for(const auto &var : CLI::detail::split(elem, delim)) { | |
| 860 | - if(!var.empty()) { | |
| 861 | - output.emplace_back(); | |
| 862 | - retval &= detail::lexical_cast(var, output.back()); | |
| 863 | - } | |
| 864 | - } | |
| 865 | - } else { | |
| 866 | - output.emplace_back(); | |
| 867 | - retval &= detail::lexical_cast(elem, output.back()); | |
| 868 | - } | |
| 877 | + output.emplace_back(); | |
| 878 | + retval &= detail::lexical_cast(elem, output.back()); | |
| 869 | 879 | } |
| 870 | 880 | |
| 871 | 881 | if(!retval) { |
| ... | ... | @@ -874,20 +884,12 @@ class Option : public OptionBase<Option> { |
| 874 | 884 | } |
| 875 | 885 | |
| 876 | 886 | /// return the results as a particular type |
| 877 | - template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy> T as() const { | |
| 887 | + template <typename T> T as() const { | |
| 878 | 888 | T output; |
| 879 | 889 | results(output); |
| 880 | 890 | return output; |
| 881 | 891 | } |
| 882 | 892 | |
| 883 | - /// get the results as a vector of a particular type | |
| 884 | - template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy> | |
| 885 | - T as(char delim = '\0') const { | |
| 886 | - T output; | |
| 887 | - results(output, delim); | |
| 888 | - return output; | |
| 889 | - } | |
| 890 | - | |
| 891 | 893 | /// See if the callback has been run already |
| 892 | 894 | bool get_callback_run() const { return callback_run_; } |
| 893 | 895 | |
| ... | ... | @@ -935,6 +937,28 @@ class Option : public OptionBase<Option> { |
| 935 | 937 | |
| 936 | 938 | /// Get the typename for this option |
| 937 | 939 | std::string get_type_name() const { return type_name_(); } |
| 940 | + | |
| 941 | + private: | |
| 942 | + int _add_result(std::string &&result) { | |
| 943 | + int count = 0; | |
| 944 | + if(delimiter_ == '\0') { | |
| 945 | + results_.push_back(std::move(result)); | |
| 946 | + ++count; | |
| 947 | + } else { | |
| 948 | + if((result.find_first_of(delimiter_) != std::string::npos)) { | |
| 949 | + for(const auto &var : CLI::detail::split(result, delimiter_)) { | |
| 950 | + if(!var.empty()) { | |
| 951 | + results_.push_back(var); | |
| 952 | + ++count; | |
| 953 | + } | |
| 954 | + } | |
| 955 | + } else { | |
| 956 | + results_.push_back(std::move(result)); | |
| 957 | + ++count; | |
| 958 | + } | |
| 959 | + } | |
| 960 | + return count; | |
| 961 | + } | |
| 938 | 962 | }; |
| 939 | 963 | |
| 940 | 964 | } // namespace CLI | ... | ... |
tests/AppTest.cpp
| ... | ... | @@ -1956,17 +1956,17 @@ TEST_F(TApp, CustomUserSepParse) { |
| 1956 | 1956 | |
| 1957 | 1957 | std::vector<int> vals = {1, 2, 3}; |
| 1958 | 1958 | args = {"--idx", "1,2,3"}; |
| 1959 | - auto opt = app.add_option("--idx", vals, "", ','); | |
| 1959 | + auto opt = app.add_option("--idx", vals)->delimiter(','); | |
| 1960 | 1960 | run(); |
| 1961 | 1961 | EXPECT_EQ(vals, std::vector<int>({1, 2, 3})); |
| 1962 | 1962 | std::vector<int> vals2; |
| 1963 | 1963 | // check that the results vector gets the results in the same way |
| 1964 | - opt->results(vals2, ','); | |
| 1964 | + opt->results(vals2); | |
| 1965 | 1965 | EXPECT_EQ(vals2, vals); |
| 1966 | 1966 | |
| 1967 | 1967 | app.remove_option(opt); |
| 1968 | 1968 | |
| 1969 | - app.add_option("--idx", vals, "", true, ','); | |
| 1969 | + app.add_option("--idx", vals, "", true)->delimiter(','); | |
| 1970 | 1970 | run(); |
| 1971 | 1971 | EXPECT_EQ(vals, std::vector<int>({1, 2, 3})); |
| 1972 | 1972 | } |
| ... | ... | @@ -1979,8 +1979,7 @@ TEST_F(TApp, DefaultUserSepParse) { |
| 1979 | 1979 | auto opt = app.add_option("--idx", vals, ""); |
| 1980 | 1980 | run(); |
| 1981 | 1981 | EXPECT_EQ(vals, std::vector<std::string>({"1 2 3", "4 5 6"})); |
| 1982 | - app.remove_option(opt); | |
| 1983 | - app.add_option("--idx", vals, "", true); | |
| 1982 | + opt->delimiter(','); | |
| 1984 | 1983 | run(); |
| 1985 | 1984 | EXPECT_EQ(vals, std::vector<std::string>({"1 2 3", "4 5 6"})); |
| 1986 | 1985 | } |
| ... | ... | @@ -1989,7 +1988,7 @@ TEST_F(TApp, DefaultUserSepParse) { |
| 1989 | 1988 | TEST_F(TApp, BadUserSepParse) { |
| 1990 | 1989 | |
| 1991 | 1990 | std::vector<int> vals; |
| 1992 | - app.add_option("--idx", vals, ""); | |
| 1991 | + app.add_option("--idx", vals); | |
| 1993 | 1992 | |
| 1994 | 1993 | args = {"--idx", "1,2,3"}; |
| 1995 | 1994 | |
| ... | ... | @@ -2001,13 +2000,13 @@ TEST_F(TApp, CustomUserSepParse2) { |
| 2001 | 2000 | |
| 2002 | 2001 | std::vector<int> vals = {1, 2, 3}; |
| 2003 | 2002 | args = {"--idx", "1,2,"}; |
| 2004 | - auto opt = app.add_option("--idx", vals, "", ','); | |
| 2003 | + auto opt = app.add_option("--idx", vals)->delimiter(','); | |
| 2005 | 2004 | run(); |
| 2006 | 2005 | EXPECT_EQ(vals, std::vector<int>({1, 2})); |
| 2007 | 2006 | |
| 2008 | 2007 | app.remove_option(opt); |
| 2009 | 2008 | |
| 2010 | - app.add_option("--idx", vals, "", true, ','); | |
| 2009 | + app.add_option("--idx", vals, "", true)->delimiter(','); | |
| 2011 | 2010 | run(); |
| 2012 | 2011 | EXPECT_EQ(vals, std::vector<int>({1, 2})); |
| 2013 | 2012 | } |
| ... | ... | @@ -2020,13 +2019,28 @@ TEST_F(TApp, CustomUserSepParseFunction) { |
| 2020 | 2019 | [&vals](std::vector<int> v) { |
| 2021 | 2020 | vals = std::move(v); |
| 2022 | 2021 | return true; |
| 2023 | - }, | |
| 2024 | - "", | |
| 2025 | - ','); | |
| 2022 | + }) | |
| 2023 | + ->delimiter(','); | |
| 2026 | 2024 | run(); |
| 2027 | 2025 | EXPECT_EQ(vals, std::vector<int>({1, 2, 3})); |
| 2028 | 2026 | } |
| 2029 | 2027 | |
| 2028 | +// delimiter removal | |
| 2029 | +TEST_F(TApp, CustomUserSepParseToggle) { | |
| 2030 | + | |
| 2031 | + std::vector<std::string> vals; | |
| 2032 | + args = {"--idx", "1,2,3"}; | |
| 2033 | + auto opt = app.add_option("--idx", vals)->delimiter(','); | |
| 2034 | + run(); | |
| 2035 | + EXPECT_EQ(vals, std::vector<std::string>({"1", "2", "3"})); | |
| 2036 | + opt->delimiter('\0'); | |
| 2037 | + run(); | |
| 2038 | + EXPECT_EQ(vals, std::vector<std::string>({"1,2,3"})); | |
| 2039 | + opt->delimiter(','); | |
| 2040 | + run(); | |
| 2041 | + EXPECT_EQ(vals, std::vector<std::string>({"1", "2", "3"})); | |
| 2042 | +} | |
| 2043 | + | |
| 2030 | 2044 | // #209 |
| 2031 | 2045 | TEST_F(TApp, CustomUserSepParse3) { |
| 2032 | 2046 | |
| ... | ... | @@ -2035,12 +2049,12 @@ TEST_F(TApp, CustomUserSepParse3) { |
| 2035 | 2049 | "1", |
| 2036 | 2050 | "," |
| 2037 | 2051 | "2"}; |
| 2038 | - auto opt = app.add_option("--idx", vals, "", ','); | |
| 2052 | + auto opt = app.add_option("--idx", vals)->delimiter(','); | |
| 2039 | 2053 | run(); |
| 2040 | 2054 | EXPECT_EQ(vals, std::vector<int>({1, 2})); |
| 2041 | 2055 | app.remove_option(opt); |
| 2042 | 2056 | |
| 2043 | - app.add_option("--idx", vals, "", false, ','); | |
| 2057 | + app.add_option("--idx", vals, "", false)->delimiter(','); | |
| 2044 | 2058 | run(); |
| 2045 | 2059 | EXPECT_EQ(vals, std::vector<int>({1, 2})); |
| 2046 | 2060 | } |
| ... | ... | @@ -2050,13 +2064,13 @@ TEST_F(TApp, CustomUserSepParse4) { |
| 2050 | 2064 | |
| 2051 | 2065 | std::vector<int> vals; |
| 2052 | 2066 | args = {"--idx", "1, 2"}; |
| 2053 | - auto opt = app.add_option("--idx", vals, "", ','); | |
| 2067 | + auto opt = app.add_option("--idx", vals)->delimiter(','); | |
| 2054 | 2068 | run(); |
| 2055 | 2069 | EXPECT_EQ(vals, std::vector<int>({1, 2})); |
| 2056 | 2070 | |
| 2057 | 2071 | app.remove_option(opt); |
| 2058 | 2072 | |
| 2059 | - app.add_option("--idx", vals, "", true, ','); | |
| 2073 | + app.add_option("--idx", vals, "", true)->delimiter(','); | |
| 2060 | 2074 | run(); |
| 2061 | 2075 | EXPECT_EQ(vals, std::vector<int>({1, 2})); |
| 2062 | 2076 | } | ... | ... |
tests/NewParseTest.cpp
| ... | ... | @@ -77,6 +77,38 @@ TEST_F(TApp, BuiltinComplex) { |
| 77 | 77 | EXPECT_DOUBLE_EQ(3, comp.imag()); |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | +TEST_F(TApp, BuiltinComplexWithDelimiter) { | |
| 81 | + cx comp{1, 2}; | |
| 82 | + app.add_complex("-c,--complex", comp, "", true)->delimiter('+'); | |
| 83 | + | |
| 84 | + args = {"-c", "4+3i"}; | |
| 85 | + | |
| 86 | + std::string help = app.help(); | |
| 87 | + EXPECT_THAT(help, HasSubstr("1")); | |
| 88 | + EXPECT_THAT(help, HasSubstr("2")); | |
| 89 | + EXPECT_THAT(help, HasSubstr("COMPLEX")); | |
| 90 | + | |
| 91 | + EXPECT_DOUBLE_EQ(1, comp.real()); | |
| 92 | + EXPECT_DOUBLE_EQ(2, comp.imag()); | |
| 93 | + | |
| 94 | + run(); | |
| 95 | + | |
| 96 | + EXPECT_DOUBLE_EQ(4, comp.real()); | |
| 97 | + EXPECT_DOUBLE_EQ(3, comp.imag()); | |
| 98 | + | |
| 99 | + args = {"-c", "5+-3i"}; | |
| 100 | + run(); | |
| 101 | + | |
| 102 | + EXPECT_DOUBLE_EQ(5, comp.real()); | |
| 103 | + EXPECT_DOUBLE_EQ(-3, comp.imag()); | |
| 104 | + | |
| 105 | + args = {"-c", "6", "-4i"}; | |
| 106 | + run(); | |
| 107 | + | |
| 108 | + EXPECT_DOUBLE_EQ(6, comp.real()); | |
| 109 | + EXPECT_DOUBLE_EQ(-4, comp.imag()); | |
| 110 | +} | |
| 111 | + | |
| 80 | 112 | TEST_F(TApp, BuiltinComplexIgnoreI) { |
| 81 | 113 | cx comp{1, 2}; |
| 82 | 114 | app.add_complex("-c,--complex", comp); | ... | ... |