Commit 3f9fafd916e4ca20b4d05cfadea93c517d695f05

Authored by Philip Top
Committed by Henry Schreiner
1 parent 6ad2641d

make immediate_callback inheritable, add tests for that and for valid strings, s…

…ome cleanup in the README.md

Apply suggestions from code review

Co-Authored-By: phlptp <top1@llnl.gov>
allow callbacks for option_groups, and allow some other characters as flags
README.md
@@ -236,7 +236,7 @@ Option_group *app.add_option_group(name,description); // 🚧 @@ -236,7 +236,7 @@ Option_group *app.add_option_group(name,description); // 🚧
236 -app.add_mutable_set_ignore_case_underscore(... // πŸ†• String only 236 -app.add_mutable_set_ignore_case_underscore(... // πŸ†• String only
237 ``` 237 ```
238 238
239 -An option name must start with a alphabetic character, underscore, or a number🚧. For long options, after the first character '.', and '-' are also valid. For the `add_flag*` functions '{' has special meaning. 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`. 239 +An option name must start with a alphabetic character, underscore, a number 🚧, '?'🚧, or '@'🚧. For long options, after the first character '.', and '-' are also valid characters. For the `add_flag*` functions '{' has special meaning. 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`.
240 240
241 The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed. The type can be any type supported by the `add_option` function. 241 The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed. The type can be any type supported by the `add_option` function.
242 242
@@ -511,9 +511,10 @@ There are several options that are supported on the main app and subcommands and @@ -511,9 +511,10 @@ There are several options that are supported on the main app and subcommands and
511 511
512 #### Callbacks 512 #### Callbacks
513 A subcommand has two optional callbacks that are executed at different stages of processing. The `preparse_callback` 🚧 is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process. For the main app the first argument is considered the program name, for subcommands the first argument is the subcommand name. For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group. 513 A subcommand has two optional callbacks that are executed at different stages of processing. The `preparse_callback` 🚧 is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process. For the main app the first argument is considered the program name, for subcommands the first argument is the subcommand name. For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group.
514 -The second callback is executed after parsing. Depending on the status of the `immediate_callback` flag 🚧. This is either immediately after the parsing of the subcommand. Or if the flag is false, once after parsing of all arguments. If the immediate_callback is set then the callback can be executed multiple times. If the main app or subcommand has a config file, no data from the config file will be reflected in immediate_callback. 514 +The second callback is executed after parsing. The behavior depends on the status of the `immediate_callback` flag 🚧. If true, this runs immediately after the parsing of the subcommand. Or if the flag is false, once after parsing of all arguments. If the `immediate_callback` is set then the callback can be executed multiple times if the subcommand list given multiple times. If the main app or subcommand has a config file, no data from the config file will be reflected in immediate_callback. `immediate_callback()` has no effect on the main app, though it can be inherited. For option_groups `immediate_callback` causes the callback to be run prior to other option groups and options in the main app, effectively giving the options in the group priority.
515 515
516 For example say an application was set up like 516 For example say an application was set up like
  517 +
517 ```cpp 518 ```cpp
518 app.callback(ac); 519 app.callback(ac);
519 sub1=app.add_subcommand("sub1")->callback(c1)->preparse_callback(pc1)->immediate_callback(); 520 sub1=app.add_subcommand("sub1")->callback(c1)->preparse_callback(pc1)->immediate_callback();
@@ -523,6 +524,7 @@ app.preparse_callback( pa1); @@ -523,6 +524,7 @@ app.preparse_callback( pa1);
523 ... A bunch of other options 524 ... A bunch of other options
524 525
525 ``` 526 ```
  527 +
526 Then the command line is given as 528 Then the command line is given as
527 529
528 ``` 530 ```
@@ -534,8 +536,8 @@ program --opt1 opt1_val sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub @@ -534,8 +536,8 @@ program --opt1 opt1_val sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub
534 * c1 will be called when the `sub2` command is encountered 536 * c1 will be called when the `sub2` command is encountered
535 * pc2 will be called with value of 6 after the sub2 command is encountered. 537 * pc2 will be called with value of 6 after the sub2 command is encountered.
536 * c1 will be called again after the second sub2 command is encountered 538 * c1 will be called again after the second sub2 command is encountered
537 -* ac will be called after completing the parse  
538 * c2 will be called once after processing all arguments 539 * c2 will be called once after processing all arguments
  540 +* ac will be called after completing the parse and all lower level callbacks have been executed
539 541
540 A subcommand is considered terminated when one of the following conditions are met. 542 A subcommand is considered terminated when one of the following conditions are met.
541 1. There are no more arguments to process 543 1. There are no more arguments to process
@@ -549,20 +551,26 @@ If the `immediate_callback` flag is set then all contained options are processed @@ -549,20 +551,26 @@ If the `immediate_callback` flag is set then all contained options are processed
549 551
550 #### Option groups 🚧 552 #### Option groups 🚧
551 553
552 -The subcommand method 554 +The subcommand method
  555 +
553 ```cpp 556 ```cpp
554 .add_option_group(name,description) 557 .add_option_group(name,description)
555 ``` 558 ```
  559 +
556 Will create an option Group, and return a pointer to it. An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements. They allow specific sets of options to be composed and controlled as a collective. For an example see [range test](./tests/ranges.cpp). Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App also work on option groups. Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through 560 Will create an option Group, and return a pointer to it. An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements. They allow specific sets of options to be composed and controlled as a collective. For an example see [range test](./tests/ranges.cpp). Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App also work on option groups. Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through
  561 +
557 ```cpp 562 ```cpp
558 ogroup->add_option(option_pointer) 563 ogroup->add_option(option_pointer)
559 ``` 564 ```
  565 +
560 ```cpp 566 ```cpp
561 ogroup->add_options(option_pointer) 567 ogroup->add_options(option_pointer)
562 ``` 568 ```
  569 +
563 ```cpp 570 ```cpp
564 ogroup->add_options(option1,option2,option3,...) 571 ogroup->add_options(option1,option2,option3,...)
565 ``` 572 ```
  573 +
566 The option pointers used in this function must be options defined in the parent application of the option group otherwise an error will be generated. 574 The option pointers used in this function must be options defined in the parent application of the option group otherwise an error will be generated.
567 Options in an option group are searched for a command line match after any options in the main app, so any positionals in the main app would be matched first. So care must be taken to make sure of the order when using positional arguments and option groups. 575 Options in an option group are searched for a command line match after any options in the main app, so any positionals in the main app would be matched first. So care must be taken to make sure of the order when using positional arguments and option groups.
568 Option groups work well with `excludes` and `require_options` methods, as an Application will treat an option group as a single option for the purpose of counting and requirements. Option groups allow specifying requirements such as requiring 1 of 3 options in one group and 1 of 3 options in a different group. Option groups can contain other groups as well. Disabling an option group will turn off all options within the group. 576 Option groups work well with `excludes` and `require_options` methods, as an Application will treat an option group as a single option for the purpose of counting and requirements. Option groups allow specifying requirements such as requiring 1 of 3 options in one group and 1 of 3 options in a different group. Option groups can contain other groups as well. Disabling an option group will turn off all options within the group.
@@ -601,7 +609,7 @@ arguments, use `.config_to_str(default_also=false, prefix=&quot;&quot;, write_description= @@ -601,7 +609,7 @@ arguments, use `.config_to_str(default_also=false, prefix=&quot;&quot;, write_description=
601 609
602 ### Inheriting defaults 610 ### Inheriting defaults
603 611
604 -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. 612 +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`,`immediate_callback` and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well.
605 613
606 Options have defaults for `group`, `required`, `multi_option_policy`, `ignore_case`, πŸ†• `ignore_underscore`, 🚧 `delimiter`, and 🚧 `disable_flag_override`. To set these defaults, you should set the `option_defaults()` object, for example: 614 Options have defaults for `group`, `required`, `multi_option_policy`, `ignore_case`, πŸ†• `ignore_underscore`, 🚧 `delimiter`, and 🚧 `disable_flag_override`. To set these defaults, you should set the `option_defaults()` object, for example:
607 615
include/CLI/App.hpp
@@ -94,7 +94,7 @@ class App { @@ -94,7 +94,7 @@ class App {
94 bool pre_parse_called_{false}; 94 bool pre_parse_called_{false};
95 95
96 /// Flag indicating that the callback for the subcommand should be executed immediately on parse completion which is 96 /// Flag indicating that the callback for the subcommand should be executed immediately on parse completion which is
97 - /// before help or ini files are processed. 97 + /// before help or ini files are processed. INHERITABLE
98 bool immediate_callback_{false}; 98 bool immediate_callback_{false};
99 99
100 /// This is a function that runs prior to the start of parsing 100 /// This is a function that runs prior to the start of parsing
@@ -246,6 +246,7 @@ class App { @@ -246,6 +246,7 @@ class App {
246 allow_extras_ = parent_->allow_extras_; 246 allow_extras_ = parent_->allow_extras_;
247 allow_config_extras_ = parent_->allow_config_extras_; 247 allow_config_extras_ = parent_->allow_config_extras_;
248 prefix_command_ = parent_->prefix_command_; 248 prefix_command_ = parent_->prefix_command_;
  249 + immediate_callback_ = parent_->immediate_callback_;
249 ignore_case_ = parent_->ignore_case_; 250 ignore_case_ = parent_->ignore_case_;
250 ignore_underscore_ = parent_->ignore_underscore_; 251 ignore_underscore_ = parent_->ignore_underscore_;
251 fallthrough_ = parent_->fallthrough_; 252 fallthrough_ = parent_->fallthrough_;
@@ -320,7 +321,7 @@ class App { @@ -320,7 +321,7 @@ class App {
320 return this; 321 return this;
321 } 322 }
322 323
323 - /// Set the subcommand to be enabled by default, so on clear(), at the start of each parse it is enabled(not 324 + /// Set the subcommand to be enabled by default, so on clear(), at the start of each parse it is enabled (not
324 /// disabled) 325 /// disabled)
325 App *enabled_by_default(bool enable = true) { 326 App *enabled_by_default(bool enable = true) {
326 enabled_by_default_ = enable; 327 enabled_by_default_ = enable;
@@ -1778,15 +1779,26 @@ class App { @@ -1778,15 +1779,26 @@ class App {
1778 app->_configure(); 1779 app->_configure();
1779 } 1780 }
1780 } 1781 }
1781 - /// Internal function to run (App) callback, top down 1782 + /// Internal function to run (App) callback, bottom up
1782 void run_callback() { 1783 void run_callback() {
1783 pre_callback(); 1784 pre_callback();
1784 - if(callback_ && (parsed_ > 0))  
1785 - callback_(); 1785 + // run the callbacks for the received subcommands
1786 for(App *subc : get_subcommands()) { 1786 for(App *subc : get_subcommands()) {
1787 - if((subc->get_name().empty()) || (!subc->immediate_callback_)) 1787 + if(!subc->immediate_callback_)
1788 subc->run_callback(); 1788 subc->run_callback();
1789 } 1789 }
  1790 + // now run callbacks for option_groups
  1791 + for(auto &subc : subcommands_) {
  1792 + if(!subc->immediate_callback_ && subc->name_.empty() && subc->count_all() > 0) {
  1793 + subc->run_callback();
  1794 + }
  1795 + }
  1796 + // finally run the main callback
  1797 + if(callback_ && (parsed_ > 0)) {
  1798 + if(!name_.empty() || count_all() > 0) {
  1799 + callback_();
  1800 + }
  1801 + }
1790 } 1802 }
1791 1803
1792 /// Check to see if a subcommand is valid. Give up immediately if subcommand max has been reached. 1804 /// Check to see if a subcommand is valid. Give up immediately if subcommand max has been reached.
@@ -1817,7 +1829,7 @@ class App { @@ -1817,7 +1829,7 @@ class App {
1817 return detail::Classifier::SHORT; 1829 return detail::Classifier::SHORT;
1818 if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2))) 1830 if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
1819 return detail::Classifier::WINDOWS; 1831 return detail::Classifier::WINDOWS;
1820 - if((current == "++") && (!name_.empty())) 1832 + if((current == "++") && !name_.empty())
1821 return detail::Classifier::SUBCOMMAND_TERMINATOR; 1833 return detail::Classifier::SUBCOMMAND_TERMINATOR;
1822 return detail::Classifier::NONE; 1834 return detail::Classifier::NONE;
1823 } 1835 }
@@ -1872,13 +1884,24 @@ class App { @@ -1872,13 +1884,24 @@ class App {
1872 } 1884 }
1873 1885
1874 for(App_p &sub : subcommands_) { 1886 for(App_p &sub : subcommands_) {
1875 - if((sub->get_name().empty()) || (!sub->immediate_callback_)) 1887 + if(sub->get_name().empty() || !sub->immediate_callback_)
1876 sub->_process_env(); 1888 sub->_process_env();
1877 } 1889 }
1878 } 1890 }
1879 1891
1880 /// Process callbacks. Runs on *all* subcommands. 1892 /// Process callbacks. Runs on *all* subcommands.
1881 void _process_callbacks() { 1893 void _process_callbacks() {
  1894 +
  1895 + for(App_p &sub : subcommands_) {
  1896 + // process the priority option_groups first
  1897 + if(sub->get_name().empty() && sub->immediate_callback_) {
  1898 + if(sub->count_all() > 0) {
  1899 + sub->_process_callbacks();
  1900 + sub->run_callback();
  1901 + }
  1902 + }
  1903 + }
  1904 +
1882 for(const Option_p &opt : options_) { 1905 for(const Option_p &opt : options_) {
1883 if(opt->count() > 0 && !opt->get_callback_run()) { 1906 if(opt->count() > 0 && !opt->get_callback_run()) {
1884 opt->run_callback(); 1907 opt->run_callback();
@@ -1886,8 +1909,9 @@ class App { @@ -1886,8 +1909,9 @@ class App {
1886 } 1909 }
1887 1910
1888 for(App_p &sub : subcommands_) { 1911 for(App_p &sub : subcommands_) {
1889 - if((sub->get_name().empty()) || (!sub->immediate_callback_)) 1912 + if(!sub->immediate_callback_) {
1890 sub->_process_callbacks(); 1913 sub->_process_callbacks();
  1914 + }
1891 } 1915 }
1892 } 1916 }
1893 1917
@@ -1979,13 +2003,12 @@ class App { @@ -1979,13 +2003,12 @@ class App {
1979 for(App_p &sub : subcommands_) { 2003 for(App_p &sub : subcommands_) {
1980 if(sub->disabled_) 2004 if(sub->disabled_)
1981 continue; 2005 continue;
1982 - if((sub->name_.empty()) && (sub->count_all() > 0)) { 2006 + if(sub->name_.empty() && sub->count_all() > 0) {
1983 ++used_options; 2007 ++used_options;
1984 } 2008 }
1985 } 2009 }
1986 2010
1987 - if((require_option_min_ > used_options) ||  
1988 - ((require_option_max_ > 0) && (require_option_max_ < used_options))) { 2011 + if(require_option_min_ > used_options || (require_option_max_ > 0 && require_option_max_ < used_options)) {
1989 auto option_list = detail::join(options_, [](const Option_p &ptr) { return ptr->get_name(false, true); }); 2012 auto option_list = detail::join(options_, [](const Option_p &ptr) { return ptr->get_name(false, true); });
1990 if(option_list.compare(0, 10, "-h,--help,") == 0) { 2013 if(option_list.compare(0, 10, "-h,--help,") == 0) {
1991 option_list.erase(0, 10); 2014 option_list.erase(0, 10);
@@ -2001,25 +2024,25 @@ class App { @@ -2001,25 +2024,25 @@ class App {
2001 for(App_p &sub : subcommands_) { 2024 for(App_p &sub : subcommands_) {
2002 if(sub->disabled_) 2025 if(sub->disabled_)
2003 continue; 2026 continue;
2004 - if((sub->name_.empty()) && (sub->required_ == false)) { 2027 + if(sub->name_.empty() && sub->required_ == false) {
2005 if(sub->count_all() == 0) { 2028 if(sub->count_all() == 0) {
2006 - if((require_option_min_ > 0) && (require_option_min_ <= used_options)) { 2029 + if(require_option_min_ > 0 && require_option_min_ <= used_options) {
2007 continue; 2030 continue;
2008 // if we have met the requirement and there is nothing in this option group skip checking 2031 // if we have met the requirement and there is nothing in this option group skip checking
2009 // requirements 2032 // requirements
2010 } 2033 }
2011 - if((require_option_max_ > 0) && (used_options >= require_option_min_)) { 2034 + if(require_option_max_ > 0 && used_options >= require_option_min_) {
2012 continue; 2035 continue;
2013 // if we have met the requirement and there is nothing in this option group skip checking 2036 // if we have met the requirement and there is nothing in this option group skip checking
2014 // requirements 2037 // requirements
2015 } 2038 }
2016 } 2039 }
2017 } 2040 }
2018 - if((sub->count() > 0) || (sub->name_.empty())) { 2041 + if(sub->count() > 0 || sub->name_.empty()) {
2019 sub->_process_requirements(); 2042 sub->_process_requirements();
2020 } 2043 }
2021 2044
2022 - if((sub->required_) && (sub->count_all() == 0)) { 2045 + if(sub->required_ && sub->count_all() == 0) {
2023 throw(CLI::RequiredError(sub->get_display_name())); 2046 throw(CLI::RequiredError(sub->get_display_name()));
2024 } 2047 }
2025 } 2048 }
@@ -2225,14 +2248,13 @@ class App { @@ -2225,14 +2248,13 @@ class App {
2225 } 2248 }
2226 } 2249 }
2227 } 2250 }
2228 - /// let the parent deal with it if possible 2251 + // let the parent deal with it if possible
2229 if(parent_ != nullptr && fallthrough_) 2252 if(parent_ != nullptr && fallthrough_)
2230 return _get_fallthrough_parent()->_parse_positional(args); 2253 return _get_fallthrough_parent()->_parse_positional(args);
2231 2254
2232 /// Try to find a local subcommand that is repeated 2255 /// Try to find a local subcommand that is repeated
2233 auto com = _find_subcommand(args.back(), true, false); 2256 auto com = _find_subcommand(args.back(), true, false);
2234 - if((com != nullptr) &&  
2235 - ((require_subcommand_max_ == 0) || (require_subcommand_max_ > parsed_subcommands_.size()))) { 2257 + if(com != nullptr && (require_subcommand_max_ == 0 || require_subcommand_max_ > parsed_subcommands_.size())) {
2236 args.pop_back(); 2258 args.pop_back();
2237 com->_parse(args); 2259 com->_parse(args);
2238 return true; 2260 return true;
@@ -2241,8 +2263,8 @@ class App { @@ -2241,8 +2263,8 @@ class App {
2241 /// subcommand in a broader way, if one exists let the parent deal with it 2263 /// subcommand in a broader way, if one exists let the parent deal with it
2242 auto parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this; 2264 auto parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
2243 com = parent_app->_find_subcommand(args.back(), true, false); 2265 com = parent_app->_find_subcommand(args.back(), true, false);
2244 - if((com != nullptr) && ((com->parent_->require_subcommand_max_ == 0) ||  
2245 - (com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size()))) { 2266 + if(com != nullptr && (com->parent_->require_subcommand_max_ == 0 ||
  2267 + com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size())) {
2246 return false; 2268 return false;
2247 } 2269 }
2248 2270
@@ -2250,7 +2272,7 @@ class App { @@ -2250,7 +2272,7 @@ class App {
2250 throw CLI::ExtrasError(args); 2272 throw CLI::ExtrasError(args);
2251 } 2273 }
2252 /// If this is an option group don't deal with it 2274 /// If this is an option group don't deal with it
2253 - if((parent_ != nullptr) && (name_.empty())) { 2275 + if(parent_ != nullptr && name_.empty()) {
2254 return false; 2276 return false;
2255 } 2277 }
2256 /// We are out of other options this goes to missing 2278 /// We are out of other options this goes to missing
@@ -2270,7 +2292,7 @@ class App { @@ -2270,7 +2292,7 @@ class App {
2270 /// subcommands be ignored 2292 /// subcommands be ignored
2271 App *_find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept { 2293 App *_find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept {
2272 for(const App_p &com : subcommands_) { 2294 for(const App_p &com : subcommands_) {
2273 - if((com->disabled_) && (ignore_disabled)) 2295 + if(com->disabled_ && ignore_disabled)
2274 continue; 2296 continue;
2275 if(com->get_name().empty()) { 2297 if(com->get_name().empty()) {
2276 auto subc = com->_find_subcommand(subc_name, ignore_disabled, ignore_used); 2298 auto subc = com->_find_subcommand(subc_name, ignore_disabled, ignore_used);
@@ -2278,7 +2300,7 @@ class App { @@ -2278,7 +2300,7 @@ class App {
2278 return subc; 2300 return subc;
2279 } 2301 }
2280 } else if(com->check_name(subc_name)) { 2302 } else if(com->check_name(subc_name)) {
2281 - if((!*com) || (!ignore_used)) 2303 + if((!*com) || !ignore_used)
2282 return com.get(); 2304 return com.get();
2283 } 2305 }
2284 } 2306 }
@@ -2353,7 +2375,7 @@ class App { @@ -2353,7 +2375,7 @@ class App {
2353 // Option not found 2375 // Option not found
2354 if(op_ptr == std::end(options_)) { 2376 if(op_ptr == std::end(options_)) {
2355 for(auto &subc : subcommands_) { 2377 for(auto &subc : subcommands_) {
2356 - if((subc->name_.empty()) && (!(subc->disabled_))) { 2378 + if(subc->name_.empty() && !subc->disabled_) {
2357 if(subc->_parse_arg(args, current_type)) { 2379 if(subc->_parse_arg(args, current_type)) {
2358 if(!subc->pre_parse_called_) { 2380 if(!subc->pre_parse_called_) {
2359 subc->_trigger_pre_parse(args.size()); 2381 subc->_trigger_pre_parse(args.size());
@@ -2366,7 +2388,7 @@ class App { @@ -2366,7 +2388,7 @@ class App {
2366 if(parent_ != nullptr && fallthrough_) 2388 if(parent_ != nullptr && fallthrough_)
2367 return _get_fallthrough_parent()->_parse_arg(args, current_type); 2389 return _get_fallthrough_parent()->_parse_arg(args, current_type);
2368 // don't capture missing if this is a nameless subcommand 2390 // don't capture missing if this is a nameless subcommand
2369 - if((parent_ != nullptr) && (name_.empty())) { 2391 + if(parent_ != nullptr && name_.empty()) {
2370 return false; 2392 return false;
2371 } 2393 }
2372 // Otherwise, add to missing 2394 // Otherwise, add to missing
include/CLI/Option.hpp
@@ -667,7 +667,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -667,7 +667,7 @@ class Option : public OptionBase&lt;Option&gt; {
667 // Run the validators (can change the string) 667 // Run the validators (can change the string)
668 if(!validators_.empty()) { 668 if(!validators_.empty()) {
669 for(std::string &result : results_) 669 for(std::string &result : results_)
670 - for(const std::function<std::string(std::string &)> &vali : validators_) { 670 + for(const auto &vali : validators_) {
671 std::string err_msg; 671 std::string err_msg;
672 672
673 try { 673 try {
include/CLI/StringTools.hpp
@@ -171,12 +171,12 @@ inline std::ostream &amp;format_help(std::ostream &amp;out, std::string name, std::strin @@ -171,12 +171,12 @@ inline std::ostream &amp;format_help(std::ostream &amp;out, std::string name, std::strin
171 } 171 }
172 172
173 /// Verify the first character of an option 173 /// Verify the first character of an option
174 -template <typename T> bool valid_first_char(T c) { return std::isalnum(c, std::locale()) || c == '_'; } 174 +template <typename T> bool valid_first_char(T c) {
  175 + return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@';
  176 +}
175 177
176 /// Verify following characters of an option 178 /// Verify following characters of an option
177 -template <typename T> bool valid_later_char(T c) {  
178 - return std::isalnum(c, std::locale()) || c == '_' || c == '.' || c == '-';  
179 -} 179 +template <typename T> bool valid_later_char(T c) { return valid_first_char(c) || c == '.' || c == '-'; }
180 180
181 /// Verify an option name 181 /// Verify an option name
182 inline bool valid_name_string(const std::string &str) { 182 inline bool valid_name_string(const std::string &str) {
tests/CreationTest.cpp
@@ -472,6 +472,7 @@ TEST_F(TApp, SubcommandDefaults) { @@ -472,6 +472,7 @@ TEST_F(TApp, SubcommandDefaults) {
472 // Initial defaults 472 // Initial defaults
473 EXPECT_FALSE(app.get_allow_extras()); 473 EXPECT_FALSE(app.get_allow_extras());
474 EXPECT_FALSE(app.get_prefix_command()); 474 EXPECT_FALSE(app.get_prefix_command());
  475 + EXPECT_FALSE(app.get_immediate_callback());
475 EXPECT_FALSE(app.get_ignore_case()); 476 EXPECT_FALSE(app.get_ignore_case());
476 EXPECT_FALSE(app.get_ignore_underscore()); 477 EXPECT_FALSE(app.get_ignore_underscore());
477 #ifdef _WIN32 478 #ifdef _WIN32
@@ -487,6 +488,7 @@ TEST_F(TApp, SubcommandDefaults) { @@ -487,6 +488,7 @@ TEST_F(TApp, SubcommandDefaults) {
487 488
488 app.allow_extras(); 489 app.allow_extras();
489 app.prefix_command(); 490 app.prefix_command();
  491 + app.immediate_callback();
490 app.ignore_case(); 492 app.ignore_case();
491 app.ignore_underscore(); 493 app.ignore_underscore();
492 #ifdef _WIN32 494 #ifdef _WIN32
@@ -505,6 +507,7 @@ TEST_F(TApp, SubcommandDefaults) { @@ -505,6 +507,7 @@ TEST_F(TApp, SubcommandDefaults) {
505 // Initial defaults 507 // Initial defaults
506 EXPECT_TRUE(app2->get_allow_extras()); 508 EXPECT_TRUE(app2->get_allow_extras());
507 EXPECT_TRUE(app2->get_prefix_command()); 509 EXPECT_TRUE(app2->get_prefix_command());
  510 + EXPECT_TRUE(app2->get_immediate_callback());
508 EXPECT_TRUE(app2->get_ignore_case()); 511 EXPECT_TRUE(app2->get_ignore_case());
509 EXPECT_TRUE(app2->get_ignore_underscore()); 512 EXPECT_TRUE(app2->get_ignore_underscore());
510 #ifdef _WIN32 513 #ifdef _WIN32
tests/HelpTest.cpp
@@ -337,6 +337,17 @@ TEST(THelp, OnlyOneHelp) { @@ -337,6 +337,17 @@ TEST(THelp, OnlyOneHelp) {
337 EXPECT_THROW(app.parse(input), CLI::ExtrasError); 337 EXPECT_THROW(app.parse(input), CLI::ExtrasError);
338 } 338 }
339 339
  340 +TEST(THelp, MultiHelp) {
  341 + CLI::App app{"My prog"};
  342 +
  343 + // It is not supported to have more than one help flag, last one wins
  344 + app.set_help_flag("--help,-h,-?", "No short name allowed");
  345 + app.allow_windows_style_options();
  346 +
  347 + std::vector<std::string> input{"/?"};
  348 + EXPECT_THROW(app.parse(input), CLI::CallForHelp);
  349 +}
  350 +
340 TEST(THelp, OnlyOneAllHelp) { 351 TEST(THelp, OnlyOneAllHelp) {
341 CLI::App app{"My prog"}; 352 CLI::App app{"My prog"};
342 353
tests/HelpersTest.cpp
@@ -33,6 +33,10 @@ TEST(String, InvalidName) { @@ -33,6 +33,10 @@ TEST(String, InvalidName) {
33 EXPECT_FALSE(CLI::detail::valid_name_string("vali&d")); 33 EXPECT_FALSE(CLI::detail::valid_name_string("vali&d"));
34 EXPECT_TRUE(CLI::detail::valid_name_string("_valid")); 34 EXPECT_TRUE(CLI::detail::valid_name_string("_valid"));
35 EXPECT_FALSE(CLI::detail::valid_name_string("/valid")); 35 EXPECT_FALSE(CLI::detail::valid_name_string("/valid"));
  36 + EXPECT_TRUE(CLI::detail::valid_name_string("vali?d"));
  37 + EXPECT_TRUE(CLI::detail::valid_name_string("@@@@"));
  38 + EXPECT_TRUE(CLI::detail::valid_name_string("b@d2?"));
  39 + EXPECT_TRUE(CLI::detail::valid_name_string("2vali?d"));
36 } 40 }
37 41
38 TEST(StringTools, Modify) { 42 TEST(StringTools, Modify) {
tests/OptionGroupTest.cpp
@@ -550,6 +550,30 @@ TEST_F(ManyGroups, SameSubcommand) { @@ -550,6 +550,30 @@ TEST_F(ManyGroups, SameSubcommand) {
550 EXPECT_EQ(subs[1], sub2); 550 EXPECT_EQ(subs[1], sub2);
551 EXPECT_EQ(subs[2], sub3); 551 EXPECT_EQ(subs[2], sub3);
552 } 552 }
  553 +TEST_F(ManyGroups, CallbackOrder) {
  554 + // only 1 group can be used
  555 + remove_required();
  556 + std::vector<int> callback_order;
  557 + g1->callback([&callback_order]() { callback_order.push_back(1); });
  558 + g2->callback([&callback_order]() { callback_order.push_back(2); });
  559 + main->callback([&callback_order]() { callback_order.push_back(3); });
  560 +
  561 + args = {"--name2", "test"};
  562 + run();
  563 + EXPECT_EQ(callback_order, std::vector<int>({2, 3}));
  564 +
  565 + callback_order.clear();
  566 + args = {"--name1", "t2", "--name2", "test"};
  567 + g2->immediate_callback();
  568 + run();
  569 + EXPECT_EQ(callback_order, std::vector<int>({2, 1, 3}));
  570 + callback_order.clear();
  571 +
  572 + args = {"--name2", "test", "--name1", "t2"};
  573 + g2->immediate_callback(false);
  574 + run();
  575 + EXPECT_EQ(callback_order, std::vector<int>({1, 2, 3}));
  576 +}
553 577
554 struct ManyGroupsPreTrigger : public ManyGroups { 578 struct ManyGroupsPreTrigger : public ManyGroups {
555 size_t triggerMain, trigger1{87u}, trigger2{34u}, trigger3{27u}; 579 size_t triggerMain, trigger1{87u}, trigger2{34u}, trigger3{27u};
tests/SubcommandTest.cpp
@@ -908,6 +908,22 @@ TEST_F(SubcommandProgram, CallbackOrder) { @@ -908,6 +908,22 @@ TEST_F(SubcommandProgram, CallbackOrder) {
908 EXPECT_EQ(callback_order, std::vector<int>({2, 1})); 908 EXPECT_EQ(callback_order, std::vector<int>({2, 1}));
909 } 909 }
910 910
  911 +TEST_F(SubcommandProgram, CallbackOrderImmediate) {
  912 + std::vector<int> callback_order;
  913 + start->callback([&callback_order]() { callback_order.push_back(1); })->immediate_callback();
  914 + stop->callback([&callback_order]() { callback_order.push_back(2); });
  915 +
  916 + args = {"start", "stop", "start"};
  917 + run();
  918 + EXPECT_EQ(callback_order, std::vector<int>({1, 1, 2}));
  919 +
  920 + callback_order.clear();
  921 +
  922 + args = {"stop", "start", "stop", "start"};
  923 + run();
  924 + EXPECT_EQ(callback_order, std::vector<int>({1, 1, 2}));
  925 +}
  926 +
911 struct ManySubcommands : public TApp { 927 struct ManySubcommands : public TApp {
912 928
913 CLI::App *sub1; 929 CLI::App *sub1;