Commit b56ae237e5750c94182cf6b29cca9ccc491866de

Authored by Daniel Herrera Castro
Committed by Henry Schreiner
1 parent c57000e5

[precompiled] Initial proof-of-concept with App.cpp

- Add C11_COMPILE cmake option that creates a static lib instead of header-only
- Add C11_INLINE macro that depends on C11_COMPILE
- Split App.hpp into App.hpp and impl/App_inl.hpp
- Add App.cpp that compiles App_inl.hpp into an object file
- CMake modifications to handle impl headers differently for sinlge-header, headers-only, and compiled versions
CLI11.hpp.in
... ... @@ -62,6 +62,8 @@ namespace {namespace} {{
62 62  
63 63 {app_hpp}
64 64  
  65 +{app_inl_hpp}
  66 +
65 67 {config_hpp}
66 68  
67 69 {formatter_hpp}
... ...
CMakeLists.txt
... ... @@ -77,6 +77,7 @@ endif()
77 77  
78 78 option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")
79 79 option(CLI11_SINGLE_FILE "Generate a single header file")
  80 +option(CLI11_PRECOMPILED "Generate a precompiled static library instead of a header-only")
80 81 cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF
81 82 "NOT CMAKE_VERSION VERSION_LESS 3.11" OFF)
82 83  
... ... @@ -105,6 +106,11 @@ cmake_dependent_option(
105 106 CLI11_CUDA_TESTS "Build the tests with NVCC to check for warnings there - requires CMake 3.9+"
106 107 OFF "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
107 108  
  109 +if(CLI11_PRECOMPILED AND CLI11_SINGLE_FILE)
  110 + # Sanity check
  111 + message(FATAL_ERROR "CLI11_PRECOMPILE and CLI11_SINGLE_FILE are mutually exclusive")
  112 +endif()
  113 +
108 114 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND NOT DEFINED CMAKE_CXX_STANDARD)
109 115 set(CMAKE_CXX_STANDARD 11)
110 116 endif()
... ... @@ -160,13 +166,35 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.13)
160 166 target_link_options(CLI11_warnings INTERFACE $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>)
161 167 endif()
162 168  
  169 +# To see in IDE, headers must be listed for target
  170 +set(MAYBE_CONFIGURE_DEPENDS "")
  171 +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND NOT CMAKE_VERSION VERSION_LESS 3.12)
  172 + list(INSERT MAYBE_CONFIGURE_DEPENDS 0 CONFIGURE_DEPENDS)
  173 +endif()
  174 +
  175 +file(GLOB CLI11_headers ${MAYBE_CONFIGURE_DEPENDS} "${PROJECT_SOURCE_DIR}/include/CLI/*.hpp")
  176 +file(GLOB CLI11_impl_headers ${MAYBE_CONFIGURE_DEPENDS}
  177 + "${PROJECT_SOURCE_DIR}/include/CLI/impl/*.hpp")
  178 +
  179 +if(CLI11_PRECOMPILED)
  180 + # Create static lib
  181 + file(GLOB CLI11_precompile_sources "${PROJECT_SOURCE_DIR}/src/*.cpp")
  182 + add_library(CLI11 STATIC ${CLI11_headers} ${CLI11_impl_headers} ${CLI11_precompile_sources})
  183 + target_compile_definitions(CLI11 PUBLIC -DCLI11_COMPILE)
  184 +
  185 + set(PUBLIC_OR_INTERFACE PUBLIC)
  186 +else()
  187 + add_library(CLI11 INTERFACE)
  188 + set(PUBLIC_OR_INTERFACE INTERFACE)
  189 +endif()
  190 +
163 191 # Allow IDE's to group targets into folders
164   -add_library(CLI11 INTERFACE)
165 192 add_library(CLI11::CLI11 ALIAS CLI11) # for add_subdirectory calls
166 193  
167 194 # Duplicated because CMake adds the current source dir if you don't.
168   -target_include_directories(CLI11 INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
169   - $<INSTALL_INTERFACE:include>)
  195 +target_include_directories(
  196 + CLI11 ${PUBLIC_OR_INTERFACE} $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  197 + $<INSTALL_INTERFACE:include>)
170 198  
171 199 if(CMAKE_VERSION VERSION_LESS 3.8)
172 200 # This might not be a complete list
... ... @@ -184,14 +212,6 @@ else()
184 212 target_compile_features(CLI11 INTERFACE cxx_std_11)
185 213 endif()
186 214  
187   -# To see in IDE, headers must be listed for target
188   -set(header-patterns "${PROJECT_SOURCE_DIR}/include/CLI/*")
189   -if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND NOT CMAKE_VERSION VERSION_LESS 3.12)
190   - list(INSERT header-patterns 0 CONFIGURE_DEPENDS)
191   -endif()
192   -
193   -file(GLOB CLI11_headers ${header-patterns})
194   -
195 215 # Allow tests to be run on CUDA
196 216 if(CLI11_CUDA_TESTS)
197 217 enable_language(CUDA)
... ... @@ -202,7 +222,10 @@ endif()
202 222  
203 223 # This folder should be installed
204 224 if(CLI11_INSTALL)
205   - install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
  225 + install(FILES ${CLI11_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/CLI")
  226 + if(NOT CLI11_COMPILE)
  227 + install(FILES ${CLI11_impl_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/CLI/impl")
  228 + endif()
206 229  
207 230 # Make an export target
208 231 install(TARGETS CLI11 EXPORT CLI11Targets)
... ... @@ -257,9 +280,10 @@ if(CLI11_SINGLE_FILE)
257 280 OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
258 281 COMMAND
259 282 Python::Interpreter "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py"
260   - ${CLI11_headers} --main "${CMAKE_CURRENT_SOURCE_DIR}/CLI11.hpp.in" --output
261   - "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" --version "${CLI11_VERSION}"
262   - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" ${CLI11_headers})
  283 + ${CLI11_headers} ${CLI11_impl_headers} --main "${CMAKE_CURRENT_SOURCE_DIR}/CLI11.hpp.in"
  284 + --output "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" --version "${CLI11_VERSION}"
  285 + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" ${CLI11_headers}
  286 + ${CLI11_impl_headers})
263 287 add_custom_target(CLI11-generate-single-file ALL
264 288 DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp")
265 289 set_property(TARGET CLI11-generate-single-file PROPERTY FOLDER "Scripts")
... ...
include/CLI/App.hpp
... ... @@ -282,39 +282,7 @@ class App {
282 282 ///@}
283 283  
284 284 /// Special private constructor for subcommand
285   - App(std::string app_description, std::string app_name, App *parent)
286   - : name_(std::move(app_name)), description_(std::move(app_description)), parent_(parent) {
287   - // Inherit if not from a nullptr
288   - if(parent_ != nullptr) {
289   - if(parent_->help_ptr_ != nullptr)
290   - set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description());
291   - if(parent_->help_all_ptr_ != nullptr)
292   - set_help_all_flag(parent_->help_all_ptr_->get_name(false, true),
293   - parent_->help_all_ptr_->get_description());
294   -
295   - /// OptionDefaults
296   - option_defaults_ = parent_->option_defaults_;
297   -
298   - // INHERITABLE
299   - failure_message_ = parent_->failure_message_;
300   - allow_extras_ = parent_->allow_extras_;
301   - allow_config_extras_ = parent_->allow_config_extras_;
302   - prefix_command_ = parent_->prefix_command_;
303   - immediate_callback_ = parent_->immediate_callback_;
304   - ignore_case_ = parent_->ignore_case_;
305   - ignore_underscore_ = parent_->ignore_underscore_;
306   - fallthrough_ = parent_->fallthrough_;
307   - validate_positionals_ = parent_->validate_positionals_;
308   - validate_optional_arguments_ = parent_->validate_optional_arguments_;
309   - configurable_ = parent_->configurable_;
310   - allow_windows_style_options_ = parent_->allow_windows_style_options_;
311   - group_ = parent_->group_;
312   - footer_ = parent_->footer_;
313   - formatter_ = parent_->formatter_;
314   - config_formatter_ = parent_->config_formatter_;
315   - require_subcommand_max_ = parent_->require_subcommand_max_;
316   - }
317   - }
  285 + App(std::string app_description, std::string app_name, App *parent);
318 286  
319 287 public:
320 288 /// @name Basic
... ... @@ -369,41 +337,10 @@ class App {
369 337 }
370 338  
371 339 /// Set a name for the app (empty will use parser to set the name)
372   - App *name(std::string app_name = "") {
373   -
374   - if(parent_ != nullptr) {
375   - auto oname = name_;
376   - name_ = app_name;
377   - const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
378   - if(!res.empty()) {
379   - name_ = oname;
380   - throw(OptionAlreadyAdded(app_name + " conflicts with existing subcommand names"));
381   - }
382   - } else {
383   - name_ = app_name;
384   - }
385   - has_automatic_name_ = false;
386   - return this;
387   - }
  340 + App *name(std::string app_name = "");
388 341  
389 342 /// Set an alias for the app
390   - App *alias(std::string app_name) {
391   - if(app_name.empty() || !detail::valid_alias_name_string(app_name)) {
392   - throw IncorrectConstruction("Aliases may not be empty or contain newlines or null characters");
393   - }
394   - if(parent_ != nullptr) {
395   - aliases_.push_back(app_name);
396   - const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
397   - if(!res.empty()) {
398   - aliases_.pop_back();
399   - throw(OptionAlreadyAdded("alias already matches an existing subcommand: " + app_name));
400   - }
401   - } else {
402   - aliases_.push_back(app_name);
403   - }
404   -
405   - return this;
406   - }
  343 + App *alias(std::string app_name);
407 344  
408 345 /// Remove the error when extras are left over on the command line.
409 346 App *allow_extras(bool allow = true) {
... ... @@ -452,17 +389,7 @@ class App {
452 389 }
453 390  
454 391 /// Set the subcommand callback to be executed immediately on subcommand completion
455   - App *immediate_callback(bool immediate = true) {
456   - immediate_callback_ = immediate;
457   - if(immediate_callback_) {
458   - if(final_callback_ && !(parse_complete_callback_)) {
459   - std::swap(final_callback_, parse_complete_callback_);
460   - }
461   - } else if(!(final_callback_) && parse_complete_callback_) {
462   - std::swap(final_callback_, parse_complete_callback_);
463   - }
464   - return this;
465   - }
  392 + App *immediate_callback(bool immediate = true);
466 393  
467 394 /// Set the subcommand to validate positional arguments before assigning
468 395 App *validate_positionals(bool validate = true) {
... ... @@ -500,19 +427,7 @@ class App {
500 427 }
501 428  
502 429 /// Ignore case. Subcommands inherit value.
503   - App *ignore_case(bool value = true) {
504   - if(value && !ignore_case_) {
505   - ignore_case_ = true;
506   - auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
507   - const auto &match = _compare_subcommand_names(*this, *p);
508   - if(!match.empty()) {
509   - ignore_case_ = false; // we are throwing so need to be exception invariant
510   - throw OptionAlreadyAdded("ignore case would cause subcommand name conflicts: " + match);
511   - }
512   - }
513   - ignore_case_ = value;
514   - return this;
515   - }
  430 + App *ignore_case(bool value = true);
516 431  
517 432 /// Allow windows style options, such as `/opt`. First matching short or long name used. Subcommands inherit
518 433 /// value.
... ... @@ -534,19 +449,7 @@ class App {
534 449 }
535 450  
536 451 /// Ignore underscore. Subcommands inherit value.
537   - App *ignore_underscore(bool value = true) {
538   - if(value && !ignore_underscore_) {
539   - ignore_underscore_ = true;
540   - auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
541   - const auto &match = _compare_subcommand_names(*this, *p);
542   - if(!match.empty()) {
543   - ignore_underscore_ = false;
544   - throw OptionAlreadyAdded("ignore underscore would cause subcommand name conflicts: " + match);
545   - }
546   - }
547   - ignore_underscore_ = value;
548   - return this;
549   - }
  452 + App *ignore_underscore(bool value = true);
550 453  
551 454 /// Set the help formatter
552 455 App *formatter(std::shared_ptr<FormatterBase> fmt) {
... ... @@ -594,42 +497,7 @@ class App {
594 497 callback_t option_callback,
595 498 std::string option_description = "",
596 499 bool defaulted = false,
597   - std::function<std::string()> func = {}) {
598   - Option myopt{option_name, option_description, option_callback, this};
599   -
600   - if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
601   - return *v == myopt;
602   - }) == std::end(options_)) {
603   - options_.emplace_back();
604   - Option_p &option = options_.back();
605   - option.reset(new Option(option_name, option_description, option_callback, this));
606   -
607   - // Set the default string capture function
608   - option->default_function(func);
609   -
610   - // For compatibility with CLI11 1.7 and before, capture the default string here
611   - if(defaulted)
612   - option->capture_default_str();
613   -
614   - // Transfer defaults to the new option
615   - option_defaults_.copy_to(option.get());
616   -
617   - // Don't bother to capture if we already did
618   - if(!defaulted && option->get_always_capture_default())
619   - option->capture_default_str();
620   -
621   - return option.get();
622   - }
623   - // we know something matches now find what it is so we can produce more error information
624   - for(auto &opt : options_) {
625   - const auto &matchname = opt->matching_name(myopt);
626   - if(!matchname.empty()) {
627   - throw(OptionAlreadyAdded("added option matched existing option name: " + matchname));
628   - }
629   - }
630   - // this line should not be reached the above loop should trigger the throw
631   - throw(OptionAlreadyAdded("added option matched existing option name")); // LCOV_EXCL_LINE
632   - }
  500 + std::function<std::string()> func = {});
633 501  
634 502 /// Add option for assigning to a variable
635 503 template <typename AssignTo,
... ... @@ -711,103 +579,24 @@ class App {
711 579 }
712 580  
713 581 /// Set a help flag, replace the existing one if present
714   - Option *set_help_flag(std::string flag_name = "", const std::string &help_description = "") {
715   - // take flag_description by const reference otherwise add_flag tries to assign to help_description
716   - if(help_ptr_ != nullptr) {
717   - remove_option(help_ptr_);
718   - help_ptr_ = nullptr;
719   - }
720   -
721   - // Empty name will simply remove the help flag
722   - if(!flag_name.empty()) {
723   - help_ptr_ = add_flag(flag_name, help_description);
724   - help_ptr_->configurable(false);
725   - }
726   -
727   - return help_ptr_;
728   - }
  582 + Option *set_help_flag(std::string flag_name = "", const std::string &help_description = "");
729 583  
730 584 /// Set a help all flag, replaced the existing one if present
731   - Option *set_help_all_flag(std::string help_name = "", const std::string &help_description = "") {
732   - // take flag_description by const reference otherwise add_flag tries to assign to flag_description
733   - if(help_all_ptr_ != nullptr) {
734   - remove_option(help_all_ptr_);
735   - help_all_ptr_ = nullptr;
736   - }
737   -
738   - // Empty name will simply remove the help all flag
739   - if(!help_name.empty()) {
740   - help_all_ptr_ = add_flag(help_name, help_description);
741   - help_all_ptr_->configurable(false);
742   - }
743   -
744   - return help_all_ptr_;
745   - }
  585 + Option *set_help_all_flag(std::string help_name = "", const std::string &help_description = "");
746 586  
747 587 /// Set a version flag and version display string, replace the existing one if present
748 588 Option *set_version_flag(std::string flag_name = "",
749 589 const std::string &versionString = "",
750   - const std::string &version_help = "Display program version information and exit") {
751   - // take flag_description by const reference otherwise add_flag tries to assign to version_description
752   - if(version_ptr_ != nullptr) {
753   - remove_option(version_ptr_);
754   - version_ptr_ = nullptr;
755   - }
  590 + const std::string &version_help = "Display program version information and exit");
756 591  
757   - // Empty name will simply remove the version flag
758   - if(!flag_name.empty()) {
759   - version_ptr_ = add_flag_callback(
760   - flag_name, [versionString]() { throw(CLI::CallForVersion(versionString, 0)); }, version_help);
761   - version_ptr_->configurable(false);
762   - }
763   -
764   - return version_ptr_;
765   - }
766 592 /// Generate the version string through a callback function
767 593 Option *set_version_flag(std::string flag_name,
768 594 std::function<std::string()> vfunc,
769   - const std::string &version_help = "Display program version information and exit") {
770   - if(version_ptr_ != nullptr) {
771   - remove_option(version_ptr_);
772   - version_ptr_ = nullptr;
773   - }
774   -
775   - // Empty name will simply remove the version flag
776   - if(!flag_name.empty()) {
777   - version_ptr_ = add_flag_callback(
778   - flag_name, [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); }, version_help);
779   - version_ptr_->configurable(false);
780   - }
781   -
782   - return version_ptr_;
783   - }
  595 + const std::string &version_help = "Display program version information and exit");
784 596  
785 597 private:
786 598 /// Internal function for adding a flag
787   - Option *_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description) {
788   - Option *opt = nullptr;
789   - if(detail::has_default_flag_values(flag_name)) {
790   - // check for default values and if it has them
791   - auto flag_defaults = detail::get_default_flag_values(flag_name);
792   - detail::remove_default_flag_values(flag_name);
793   - opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
794   - for(const auto &fname : flag_defaults)
795   - opt->fnames_.push_back(fname.first);
796   - opt->default_flag_values_ = std::move(flag_defaults);
797   - } else {
798   - opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
799   - }
800   - // flags cannot have positional values
801   - if(opt->get_positional()) {
802   - auto pos_name = opt->get_name(true);
803   - remove_option(opt);
804   - throw IncorrectConstruction::PositionalFlag(pos_name);
805   - }
806   - opt->multi_option_policy(MultiOptionPolicy::TakeLast);
807   - opt->expected(0);
808   - opt->required(false);
809   - return opt;
810   - }
  599 + Option *_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description);
811 600  
812 601 public:
813 602 /// Add a flag with no description or variable assignment
... ... @@ -863,33 +652,12 @@ class App {
863 652 /// Add option for callback that is triggered with a true flag and takes no arguments
864 653 Option *add_flag_callback(std::string flag_name,
865 654 std::function<void(void)> function, ///< A function to call, void(void)
866   - std::string flag_description = "") {
867   -
868   - CLI::callback_t fun = [function](const CLI::results_t &res) {
869   - bool trigger{false};
870   - auto result = CLI::detail::lexical_cast(res[0], trigger);
871   - if(result && trigger) {
872   - function();
873   - }
874   - return result;
875   - };
876   - return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
877   - }
  655 + std::string flag_description = "");
878 656  
879 657 /// Add option for callback with an integer value
880 658 Option *add_flag_function(std::string flag_name,
881 659 std::function<void(std::int64_t)> function, ///< A function to call, void(int)
882   - std::string flag_description = "") {
883   -
884   - CLI::callback_t fun = [function](const CLI::results_t &res) {
885   - std::int64_t flag_count{0};
886   - CLI::detail::lexical_cast(res[0], flag_count);
887   - function(flag_count);
888   - return true;
889   - };
890   - return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
891   - ->multi_option_policy(MultiOptionPolicy::Sum);
892   - }
  660 + std::string flag_description = "");
893 661  
894 662 #ifdef CLI11_CPP14
895 663 /// Add option for callback (C++14 or better only)
... ... @@ -904,50 +672,10 @@ class App {
904 672 Option *set_config(std::string option_name = "",
905 673 std::string default_filename = "",
906 674 const std::string &help_message = "Read an ini file",
907   - bool config_required = false) {
908   -
909   - // Remove existing config if present
910   - if(config_ptr_ != nullptr) {
911   - remove_option(config_ptr_);
912   - config_ptr_ = nullptr; // need to remove the config_ptr completely
913   - }
914   -
915   - // Only add config if option passed
916   - if(!option_name.empty()) {
917   - config_ptr_ = add_option(option_name, help_message);
918   - if(config_required) {
919   - config_ptr_->required();
920   - }
921   - if(!default_filename.empty()) {
922   - config_ptr_->default_str(std::move(default_filename));
923   - }
924   - config_ptr_->configurable(false);
925   - }
926   -
927   - return config_ptr_;
928   - }
  675 + bool config_required = false);
929 676  
930 677 /// Removes an option from the App. Takes an option pointer. Returns true if found and removed.
931   - bool remove_option(Option *opt) {
932   - // Make sure no links exist
933   - for(Option_p &op : options_) {
934   - op->remove_needs(opt);
935   - op->remove_excludes(opt);
936   - }
937   -
938   - if(help_ptr_ == opt)
939   - help_ptr_ = nullptr;
940   - if(help_all_ptr_ == opt)
941   - help_all_ptr_ = nullptr;
942   -
943   - auto iterator =
944   - std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
945   - if(iterator != std::end(options_)) {
946   - options_.erase(iterator);
947   - return true;
948   - }
949   - return false;
950   - }
  678 + bool remove_option(Option *opt);
951 679  
952 680 /// creates an option group as part of the given app
953 681 template <typename T = Option_group>
... ... @@ -968,119 +696,35 @@ class App {
968 696 ///@{
969 697  
970 698 /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag
971   - App *add_subcommand(std::string subcommand_name = "", std::string subcommand_description = "") {
972   - if(!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {
973   - if(!detail::valid_first_char(subcommand_name[0])) {
974   - throw IncorrectConstruction(
975   - "Subcommand name starts with invalid character, '!' and '-' are not allowed");
976   - }
977   - for(auto c : subcommand_name) {
978   - if(!detail::valid_later_char(c)) {
979   - throw IncorrectConstruction(std::string("Subcommand name contains invalid character ('") + c +
980   - "'), all characters are allowed except"
981   - "'=',':','{','}', and ' '");
982   - }
983   - }
984   - }
985   - CLI::App_p subcom = std::shared_ptr<App>(new App(std::move(subcommand_description), subcommand_name, this));
986   - return add_subcommand(std::move(subcom));
987   - }
  699 + App *add_subcommand(std::string subcommand_name = "", std::string subcommand_description = "");
988 700  
989 701 /// Add a previously created app as a subcommand
990   - App *add_subcommand(CLI::App_p subcom) {
991   - if(!subcom)
992   - throw IncorrectConstruction("passed App is not valid");
993   - auto *ckapp = (name_.empty() && parent_ != nullptr) ? _get_fallthrough_parent() : this;
994   - const auto &mstrg = _compare_subcommand_names(*subcom, *ckapp);
995   - if(!mstrg.empty()) {
996   - throw(OptionAlreadyAdded("subcommand name or alias matches existing subcommand: " + mstrg));
997   - }
998   - subcom->parent_ = this;
999   - subcommands_.push_back(std::move(subcom));
1000   - return subcommands_.back().get();
1001   - }
  702 + App *add_subcommand(CLI::App_p subcom);
1002 703  
1003 704 /// Removes a subcommand from the App. Takes a subcommand pointer. Returns true if found and removed.
1004   - bool remove_subcommand(App *subcom) {
1005   - // Make sure no links exist
1006   - for(App_p &sub : subcommands_) {
1007   - sub->remove_excludes(subcom);
1008   - sub->remove_needs(subcom);
1009   - }
  705 + bool remove_subcommand(App *subcom);
1010 706  
1011   - auto iterator = std::find_if(
1012   - std::begin(subcommands_), std::end(subcommands_), [subcom](const App_p &v) { return v.get() == subcom; });
1013   - if(iterator != std::end(subcommands_)) {
1014   - subcommands_.erase(iterator);
1015   - return true;
1016   - }
1017   - return false;
1018   - }
1019 707 /// Check to see if a subcommand is part of this command (doesn't have to be in command line)
1020 708 /// returns the first subcommand if passed a nullptr
1021   - App *get_subcommand(const App *subcom) const {
1022   - if(subcom == nullptr)
1023   - throw OptionNotFound("nullptr passed");
1024   - for(const App_p &subcomptr : subcommands_)
1025   - if(subcomptr.get() == subcom)
1026   - return subcomptr.get();
1027   - throw OptionNotFound(subcom->get_name());
1028   - }
  709 + App *get_subcommand(const App *subcom) const;
1029 710  
1030 711 /// Check to see if a subcommand is part of this command (text version)
1031   - CLI11_NODISCARD App *get_subcommand(std::string subcom) const {
1032   - auto *subc = _find_subcommand(subcom, false, false);
1033   - if(subc == nullptr)
1034   - throw OptionNotFound(subcom);
1035   - return subc;
1036   - }
  712 + CLI11_NODISCARD App *get_subcommand(std::string subcom) const;
  713 +
1037 714 /// Get a pointer to subcommand by index
1038   - CLI11_NODISCARD App *get_subcommand(int index = 0) const {
1039   - if(index >= 0) {
1040   - auto uindex = static_cast<unsigned>(index);
1041   - if(uindex < subcommands_.size())
1042   - return subcommands_[uindex].get();
1043   - }
1044   - throw OptionNotFound(std::to_string(index));
1045   - }
  715 + CLI11_NODISCARD App *get_subcommand(int index = 0) const;
1046 716  
1047 717 /// Check to see if a subcommand is part of this command and get a shared_ptr to it
1048   - CLI::App_p get_subcommand_ptr(App *subcom) const {
1049   - if(subcom == nullptr)
1050   - throw OptionNotFound("nullptr passed");
1051   - for(const App_p &subcomptr : subcommands_)
1052   - if(subcomptr.get() == subcom)
1053   - return subcomptr;
1054   - throw OptionNotFound(subcom->get_name());
1055   - }
  718 + CLI::App_p get_subcommand_ptr(App *subcom) const;
1056 719  
1057 720 /// Check to see if a subcommand is part of this command (text version)
1058   - CLI11_NODISCARD CLI::App_p get_subcommand_ptr(std::string subcom) const {
1059   - for(const App_p &subcomptr : subcommands_)
1060   - if(subcomptr->check_name(subcom))
1061   - return subcomptr;
1062   - throw OptionNotFound(subcom);
1063   - }
  721 + CLI11_NODISCARD CLI::App_p get_subcommand_ptr(std::string subcom) const;
1064 722  
1065 723 /// Get an owning pointer to subcommand by index
1066   - CLI11_NODISCARD CLI::App_p get_subcommand_ptr(int index = 0) const {
1067   - if(index >= 0) {
1068   - auto uindex = static_cast<unsigned>(index);
1069   - if(uindex < subcommands_.size())
1070   - return subcommands_[uindex];
1071   - }
1072   - throw OptionNotFound(std::to_string(index));
1073   - }
  724 + CLI11_NODISCARD CLI::App_p get_subcommand_ptr(int index = 0) const;
1074 725  
1075 726 /// Check to see if an option group is part of this App
1076   - CLI11_NODISCARD App *get_option_group(std::string group_name) const {
1077   - for(const App_p &app : subcommands_) {
1078   - if(app->name_.empty() && app->group_ == group_name) {
1079   - return app.get();
1080   - }
1081   - }
1082   - throw OptionNotFound(group_name);
1083   - }
  727 + CLI11_NODISCARD App *get_option_group(std::string group_name) const;
1084 728  
1085 729 /// No argument version of count counts the number of times this subcommand was
1086 730 /// passed in. The main app will return 1. Unnamed subcommands will also return 1 unless
... ... @@ -1089,19 +733,7 @@ class App {
1089 733  
1090 734 /// Get a count of all the arguments processed in options and subcommands, this excludes arguments which were
1091 735 /// treated as extras.
1092   - CLI11_NODISCARD std::size_t count_all() const {
1093   - std::size_t cnt{0};
1094   - for(const auto &opt : options_) {
1095   - cnt += opt->count();
1096   - }
1097   - for(const auto &sub : subcommands_) {
1098   - cnt += sub->count_all();
1099   - }
1100   - if(!get_name().empty()) { // for named subcommands add the number of times the subcommand was called
1101   - cnt += parsed_;
1102   - }
1103   - return cnt;
1104   - }
  736 + CLI11_NODISCARD std::size_t count_all() const;
1105 737  
1106 738 /// Changes the group membership
1107 739 App *group(std::string group_name) {
... ... @@ -1192,153 +824,34 @@ class App {
1192 824 ///@{
1193 825 //
1194 826 /// Reset the parsed data
1195   - void clear() {
1196   -
1197   - parsed_ = 0;
1198   - pre_parse_called_ = false;
1199   -
1200   - missing_.clear();
1201   - parsed_subcommands_.clear();
1202   - for(const Option_p &opt : options_) {
1203   - opt->clear();
1204   - }
1205   - for(const App_p &subc : subcommands_) {
1206   - subc->clear();
1207   - }
1208   - }
  827 + void clear();
1209 828  
1210 829 /// Parses the command line - throws errors.
1211 830 /// This must be called after the options are in but before the rest of the program.
1212   - void parse(int argc, const char *const *argv) {
1213   - // If the name is not set, read from command line
1214   - if(name_.empty() || has_automatic_name_) {
1215   - has_automatic_name_ = true;
1216   - name_ = argv[0];
1217   - }
1218   -
1219   - std::vector<std::string> args;
1220   - args.reserve(static_cast<std::size_t>(argc) - 1U);
1221   - for(auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i)
1222   - args.emplace_back(argv[i]);
1223   - parse(std::move(args));
1224   - }
  831 + void parse(int argc, const char *const *argv);
1225 832  
1226 833 /// Parse a single string as if it contained command line arguments.
1227 834 /// This function splits the string into arguments then calls parse(std::vector<std::string> &)
1228 835 /// the function takes an optional boolean argument specifying if the programName is included in the string to
1229 836 /// process
1230   - void parse(std::string commandline, bool program_name_included = false) {
1231   -
1232   - if(program_name_included) {
1233   - auto nstr = detail::split_program_name(commandline);
1234   - if((name_.empty()) || (has_automatic_name_)) {
1235   - has_automatic_name_ = true;
1236   - name_ = nstr.first;
1237   - }
1238   - commandline = std::move(nstr.second);
1239   - } else {
1240   - detail::trim(commandline);
1241   - }
1242   - // the next section of code is to deal with quoted arguments after an '=' or ':' for windows like operations
1243   - if(!commandline.empty()) {
1244   - commandline = detail::find_and_modify(commandline, "=", detail::escape_detect);
1245   - if(allow_windows_style_options_)
1246   - commandline = detail::find_and_modify(commandline, ":", detail::escape_detect);
1247   - }
1248   -
1249   - auto args = detail::split_up(std::move(commandline));
1250   - // remove all empty strings
1251   - args.erase(std::remove(args.begin(), args.end(), std::string{}), args.end());
1252   - std::reverse(args.begin(), args.end());
1253   -
1254   - parse(std::move(args));
1255   - }
  837 + void parse(std::string commandline, bool program_name_included = false);
1256 838  
1257 839 /// The real work is done here. Expects a reversed vector.
1258 840 /// Changes the vector to the remaining options.
1259   - void parse(std::vector<std::string> &args) {
1260   - // Clear if parsed
1261   - if(parsed_ > 0)
1262   - clear();
1263   -
1264   - // parsed_ is incremented in commands/subcommands,
1265   - // but placed here to make sure this is cleared when
1266   - // running parse after an error is thrown, even by _validate or _configure.
1267   - parsed_ = 1;
1268   - _validate();
1269   - _configure();
1270   - // set the parent as nullptr as this object should be the top now
1271   - parent_ = nullptr;
1272   - parsed_ = 0;
1273   -
1274   - _parse(args);
1275   - run_callback();
1276   - }
  841 + void parse(std::vector<std::string> &args);
1277 842  
1278 843 /// The real work is done here. Expects a reversed vector.
1279   - void parse(std::vector<std::string> &&args) {
1280   - // Clear if parsed
1281   - if(parsed_ > 0)
1282   - clear();
1283   -
1284   - // parsed_ is incremented in commands/subcommands,
1285   - // but placed here to make sure this is cleared when
1286   - // running parse after an error is thrown, even by _validate or _configure.
1287   - parsed_ = 1;
1288   - _validate();
1289   - _configure();
1290   - // set the parent as nullptr as this object should be the top now
1291   - parent_ = nullptr;
1292   - parsed_ = 0;
1293   -
1294   - _parse(std::move(args));
1295   - run_callback();
1296   - }
1297   -
1298   - void parse_from_stream(std::istream &input) {
1299   - if(parsed_ == 0) {
1300   - _validate();
1301   - _configure();
1302   - // set the parent as nullptr as this object should be the top now
1303   - }
  844 + void parse(std::vector<std::string> &&args);
  845 +
  846 + void parse_from_stream(std::istream &input);
1304 847  
1305   - _parse_stream(input);
1306   - run_callback();
1307   - }
1308 848 /// Provide a function to print a help message. The function gets access to the App pointer and error.
1309 849 void failure_message(std::function<std::string(const App *, const Error &e)> function) {
1310 850 failure_message_ = function;
1311 851 }
1312 852  
1313 853 /// Print a nice error message and return the exit code
1314   - int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const {
1315   -
1316   - /// Avoid printing anything if this is a CLI::RuntimeError
1317   - if(e.get_name() == "RuntimeError")
1318   - return e.get_exit_code();
1319   -
1320   - if(e.get_name() == "CallForHelp") {
1321   - out << help();
1322   - return e.get_exit_code();
1323   - }
1324   -
1325   - if(e.get_name() == "CallForAllHelp") {
1326   - out << help("", AppFormatMode::All);
1327   - return e.get_exit_code();
1328   - }
1329   -
1330   - if(e.get_name() == "CallForVersion") {
1331   - out << e.what() << std::endl;
1332   - return e.get_exit_code();
1333   - }
1334   -
1335   - if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {
1336   - if(failure_message_)
1337   - err << failure_message_(this, e) << std::flush;
1338   - }
1339   -
1340   - return e.get_exit_code();
1341   - }
  854 + int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const;
1342 855  
1343 856 ///@}
1344 857 /// @name Post parsing
... ... @@ -1353,38 +866,11 @@ class App {
1353 866  
1354 867 /// Get a filtered subcommand pointer list from the original definition list. An empty function will provide all
1355 868 /// subcommands (const)
1356   - std::vector<const App *> get_subcommands(const std::function<bool(const App *)> &filter) const {
1357   - std::vector<const App *> subcomms(subcommands_.size());
1358   - std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {
1359   - return v.get();
1360   - });
1361   -
1362   - if(filter) {
1363   - subcomms.erase(std::remove_if(std::begin(subcomms),
1364   - std::end(subcomms),
1365   - [&filter](const App *app) { return !filter(app); }),
1366   - std::end(subcomms));
1367   - }
1368   -
1369   - return subcomms;
1370   - }
  869 + std::vector<const App *> get_subcommands(const std::function<bool(const App *)> &filter) const;
1371 870  
1372 871 /// Get a filtered subcommand pointer list from the original definition list. An empty function will provide all
1373 872 /// subcommands
1374   - std::vector<App *> get_subcommands(const std::function<bool(App *)> &filter) {
1375   - std::vector<App *> subcomms(subcommands_.size());
1376   - std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {
1377   - return v.get();
1378   - });
1379   -
1380   - if(filter) {
1381   - subcomms.erase(
1382   - std::remove_if(std::begin(subcomms), std::end(subcomms), [&filter](App *app) { return !filter(app); }),
1383   - std::end(subcomms));
1384   - }
1385   -
1386   - return subcomms;
1387   - }
  873 + std::vector<App *> get_subcommands(const std::function<bool(App *)> &filter);
1388 874  
1389 875 /// Check to see if given subcommand was selected
1390 876 bool got_subcommand(const App *subcom) const {
... ... @@ -1442,47 +928,16 @@ class App {
1442 928 }
1443 929  
1444 930 /// Removes an option from the excludes list of this subcommand
1445   - bool remove_excludes(Option *opt) {
1446   - auto iterator = std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);
1447   - if(iterator == std::end(exclude_options_)) {
1448   - return false;
1449   - }
1450   - exclude_options_.erase(iterator);
1451   - return true;
1452   - }
  931 + bool remove_excludes(Option *opt);
1453 932  
1454 933 /// Removes a subcommand from the excludes list of this subcommand
1455   - bool remove_excludes(App *app) {
1456   - auto iterator = std::find(std::begin(exclude_subcommands_), std::end(exclude_subcommands_), app);
1457   - if(iterator == std::end(exclude_subcommands_)) {
1458   - return false;
1459   - }
1460   - auto *other_app = *iterator;
1461   - exclude_subcommands_.erase(iterator);
1462   - other_app->remove_excludes(this);
1463   - return true;
1464   - }
  934 + bool remove_excludes(App *app);
1465 935  
1466 936 /// Removes an option from the needs list of this subcommand
1467   - bool remove_needs(Option *opt) {
1468   - auto iterator = std::find(std::begin(need_options_), std::end(need_options_), opt);
1469   - if(iterator == std::end(need_options_)) {
1470   - return false;
1471   - }
1472   - need_options_.erase(iterator);
1473   - return true;
1474   - }
  937 + bool remove_needs(Option *opt);
1475 938  
1476 939 /// Removes a subcommand from the needs list of this subcommand
1477   - bool remove_needs(App *app) {
1478   - auto iterator = std::find(std::begin(need_subcommands_), std::end(need_subcommands_), app);
1479   - if(iterator == std::end(need_subcommands_)) {
1480   - return false;
1481   - }
1482   - need_subcommands_.erase(iterator);
1483   - return true;
1484   - }
1485   -
  940 + bool remove_needs(App *app);
1486 941 ///@}
1487 942 /// @name Help
1488 943 ///@{
... ... @@ -1505,37 +960,10 @@ class App {
1505 960  
1506 961 /// Makes a help message, using the currently configured formatter
1507 962 /// Will only do one subcommand at a time
1508   - CLI11_NODISCARD std::string help(std::string prev = "", AppFormatMode mode = AppFormatMode::Normal) const {
1509   - if(prev.empty())
1510   - prev = get_name();
1511   - else
1512   - prev += " " + get_name();
1513   -
1514   - // Delegate to subcommand if needed
1515   - auto selected_subcommands = get_subcommands();
1516   - if(!selected_subcommands.empty()) {
1517   - return selected_subcommands.at(0)->help(prev, mode);
1518   - }
1519   - return formatter_->make_help(this, prev, mode);
1520   - }
  963 + CLI11_NODISCARD std::string help(std::string prev = "", AppFormatMode mode = AppFormatMode::Normal) const;
1521 964  
1522 965 /// Displays a version string
1523   - CLI11_NODISCARD std::string version() const {
1524   - std::string val;
1525   - if(version_ptr_ != nullptr) {
1526   - auto rv = version_ptr_->results();
1527   - version_ptr_->clear();
1528   - version_ptr_->add_result("true");
1529   - try {
1530   - version_ptr_->run_callback();
1531   - } catch(const CLI::CallForVersion &cfv) {
1532   - val = cfv.what();
1533   - }
1534   - version_ptr_->clear();
1535   - version_ptr_->add_result(rv);
1536   - }
1537   - return val;
1538   - }
  966 + CLI11_NODISCARD std::string version() const;
1539 967 ///@}
1540 968 /// @name Getters
1541 969 ///@{
... ... @@ -1566,75 +994,16 @@ class App {
1566 994 }
1567 995  
1568 996 /// Get the list of options (user facing function, so returns raw pointers), has optional filter function
1569   - std::vector<const Option *> get_options(const std::function<bool(const Option *)> filter = {}) const {
1570   - std::vector<const Option *> options(options_.size());
1571   - std::transform(std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) {
1572   - return val.get();
1573   - });
1574   -
1575   - if(filter) {
1576   - options.erase(std::remove_if(std::begin(options),
1577   - std::end(options),
1578   - [&filter](const Option *opt) { return !filter(opt); }),
1579   - std::end(options));
1580   - }
1581   -
1582   - return options;
1583   - }
  997 + std::vector<const Option *> get_options(const std::function<bool(const Option *)> filter = {}) const;
1584 998  
1585 999 /// Non-const version of the above
1586   - std::vector<Option *> get_options(const std::function<bool(Option *)> filter = {}) {
1587   - std::vector<Option *> options(options_.size());
1588   - std::transform(std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) {
1589   - return val.get();
1590   - });
1591   -
1592   - if(filter) {
1593   - options.erase(
1594   - std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }),
1595   - std::end(options));
1596   - }
1597   -
1598   - return options;
1599   - }
  1000 + std::vector<Option *> get_options(const std::function<bool(Option *)> filter = {});
1600 1001  
1601 1002 /// Get an option by name (noexcept non-const version)
1602   - Option *get_option_no_throw(std::string option_name) noexcept {
1603   - for(Option_p &opt : options_) {
1604   - if(opt->check_name(option_name)) {
1605   - return opt.get();
1606   - }
1607   - }
1608   - for(auto &subc : subcommands_) {
1609   - // also check down into nameless subcommands
1610   - if(subc->get_name().empty()) {
1611   - auto *opt = subc->get_option_no_throw(option_name);
1612   - if(opt != nullptr) {
1613   - return opt;
1614   - }
1615   - }
1616   - }
1617   - return nullptr;
1618   - }
  1003 + Option *get_option_no_throw(std::string option_name) noexcept;
1619 1004  
1620 1005 /// Get an option by name (noexcept const version)
1621   - CLI11_NODISCARD const Option *get_option_no_throw(std::string option_name) const noexcept {
1622   - for(const Option_p &opt : options_) {
1623   - if(opt->check_name(option_name)) {
1624   - return opt.get();
1625   - }
1626   - }
1627   - for(const auto &subc : subcommands_) {
1628   - // also check down into nameless subcommands
1629   - if(subc->get_name().empty()) {
1630   - auto *opt = subc->get_option_no_throw(option_name);
1631   - if(opt != nullptr) {
1632   - return opt;
1633   - }
1634   - }
1635   - }
1636   - return nullptr;
1637   - }
  1006 + CLI11_NODISCARD const Option *get_option_no_throw(std::string option_name) const noexcept;
1638 1007  
1639 1008 /// Get an option by name
1640 1009 CLI11_NODISCARD const Option *get_option(std::string option_name) const {
... ... @@ -1769,116 +1138,25 @@ class App {
1769 1138 }
1770 1139  
1771 1140 /// Get a display name for an app
1772   - CLI11_NODISCARD std::string get_display_name(bool with_aliases = false) const {
1773   - if(name_.empty()) {
1774   - return std::string("[Option Group: ") + get_group() + "]";
1775   - }
1776   - if(aliases_.empty() || !with_aliases) {
1777   - return name_;
1778   - }
1779   - std::string dispname = name_;
1780   - for(const auto &lalias : aliases_) {
1781   - dispname.push_back(',');
1782   - dispname.push_back(' ');
1783   - dispname.append(lalias);
1784   - }
1785   - return dispname;
1786   - }
  1141 + CLI11_NODISCARD std::string get_display_name(bool with_aliases = false) const;
1787 1142  
1788 1143 /// Check the name, case insensitive and underscore insensitive if set
1789   - CLI11_NODISCARD bool check_name(std::string name_to_check) const {
1790   - std::string local_name = name_;
1791   - if(ignore_underscore_) {
1792   - local_name = detail::remove_underscore(name_);
1793   - name_to_check = detail::remove_underscore(name_to_check);
1794   - }
1795   - if(ignore_case_) {
1796   - local_name = detail::to_lower(name_);
1797   - name_to_check = detail::to_lower(name_to_check);
1798   - }
1799   -
1800   - if(local_name == name_to_check) {
1801   - return true;
1802   - }
1803   - for(auto les : aliases_) { // NOLINT(performance-for-range-copy)
1804   - if(ignore_underscore_) {
1805   - les = detail::remove_underscore(les);
1806   - }
1807   - if(ignore_case_) {
1808   - les = detail::to_lower(les);
1809   - }
1810   - if(les == name_to_check) {
1811   - return true;
1812   - }
1813   - }
1814   - return false;
1815   - }
  1144 + CLI11_NODISCARD bool check_name(std::string name_to_check) const;
1816 1145  
1817 1146 /// Get the groups available directly from this option (in order)
1818   - CLI11_NODISCARD std::vector<std::string> get_groups() const {
1819   - std::vector<std::string> groups;
1820   -
1821   - for(const Option_p &opt : options_) {
1822   - // Add group if it is not already in there
1823   - if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) {
1824   - groups.push_back(opt->get_group());
1825   - }
1826   - }
1827   -
1828   - return groups;
1829   - }
  1147 + CLI11_NODISCARD std::vector<std::string> get_groups() const;
1830 1148  
1831 1149 /// This gets a vector of pointers with the original parse order
1832 1150 CLI11_NODISCARD const std::vector<Option *> &parse_order() const { return parse_order_; }
1833 1151  
1834 1152 /// This returns the missing options from the current subcommand
1835   - CLI11_NODISCARD std::vector<std::string> remaining(bool recurse = false) const {
1836   - std::vector<std::string> miss_list;
1837   - for(const std::pair<detail::Classifier, std::string> &miss : missing_) {
1838   - miss_list.push_back(std::get<1>(miss));
1839   - }
1840   - // Get from a subcommand that may allow extras
1841   - if(recurse) {
1842   - if(!allow_extras_) {
1843   - for(const auto &sub : subcommands_) {
1844   - if(sub->name_.empty() && !sub->missing_.empty()) {
1845   - for(const std::pair<detail::Classifier, std::string> &miss : sub->missing_) {
1846   - miss_list.push_back(std::get<1>(miss));
1847   - }
1848   - }
1849   - }
1850   - }
1851   - // Recurse into subcommands
1852   -
1853   - for(const App *sub : parsed_subcommands_) {
1854   - std::vector<std::string> output = sub->remaining(recurse);
1855   - std::copy(std::begin(output), std::end(output), std::back_inserter(miss_list));
1856   - }
1857   - }
1858   - return miss_list;
1859   - }
  1153 + CLI11_NODISCARD std::vector<std::string> remaining(bool recurse = false) const;
1860 1154  
1861 1155 /// This returns the missing options in a form ready for processing by another command line program
1862   - CLI11_NODISCARD std::vector<std::string> remaining_for_passthrough(bool recurse = false) const {
1863   - std::vector<std::string> miss_list = remaining(recurse);
1864   - std::reverse(std::begin(miss_list), std::end(miss_list));
1865   - return miss_list;
1866   - }
  1156 + CLI11_NODISCARD std::vector<std::string> remaining_for_passthrough(bool recurse = false) const;
1867 1157  
1868 1158 /// This returns the number of remaining options, minus the -- separator
1869   - CLI11_NODISCARD std::size_t remaining_size(bool recurse = false) const {
1870   - auto remaining_options = static_cast<std::size_t>(std::count_if(
1871   - std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifier, std::string> &val) {
1872   - return val.first != detail::Classifier::POSITIONAL_MARK;
1873   - }));
1874   -
1875   - if(recurse) {
1876   - for(const App_p &sub : subcommands_) {
1877   - remaining_options += sub->remaining_size(recurse);
1878   - }
1879   - }
1880   - return remaining_options;
1881   - }
  1159 + CLI11_NODISCARD std::size_t remaining_size(bool recurse = false) const;
1882 1160  
1883 1161 ///@}
1884 1162  
... ... @@ -1887,1147 +1165,118 @@ class App {
1887 1165 ///
1888 1166 /// Currently checks to see if multiple positionals exist with unlimited args and checks if the min and max options
1889 1167 /// are feasible
1890   - void _validate() const {
1891   - // count the number of positional only args
1892   - auto pcount = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1893   - return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional();
1894   - });
1895   - if(pcount > 1) {
1896   - auto pcount_req = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1897   - return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional() &&
1898   - opt->get_required();
1899   - });
1900   - if(pcount - pcount_req > 1) {
1901   - throw InvalidError(name_);
1902   - }
1903   - }
1904   -
1905   - std::size_t nameless_subs{0};
1906   - for(const App_p &app : subcommands_) {
1907   - app->_validate();
1908   - if(app->get_name().empty())
1909   - ++nameless_subs;
1910   - }
1911   -
1912   - if(require_option_min_ > 0) {
1913   - if(require_option_max_ > 0) {
1914   - if(require_option_max_ < require_option_min_) {
1915   - throw(InvalidError("Required min options greater than required max options",
1916   - ExitCodes::InvalidError));
1917   - }
1918   - }
1919   - if(require_option_min_ > (options_.size() + nameless_subs)) {
1920   - throw(InvalidError("Required min options greater than number of available options",
1921   - ExitCodes::InvalidError));
1922   - }
1923   - }
1924   - }
  1168 + void _validate() const;
1925 1169  
1926 1170 /// configure subcommands to enable parsing through the current object
1927 1171 /// set the correct fallthrough and prefix for nameless subcommands and manage the automatic enable or disable
1928 1172 /// makes sure parent is set correctly
1929   - void _configure() {
1930   - if(default_startup == startup_mode::enabled) {
1931   - disabled_ = false;
1932   - } else if(default_startup == startup_mode::disabled) {
1933   - disabled_ = true;
1934   - }
1935   - for(const App_p &app : subcommands_) {
1936   - if(app->has_automatic_name_) {
1937   - app->name_.clear();
1938   - }
1939   - if(app->name_.empty()) {
1940   - app->fallthrough_ = false; // make sure fallthrough_ is false to prevent infinite loop
1941   - app->prefix_command_ = false;
1942   - }
1943   - // make sure the parent is set to be this object in preparation for parse
1944   - app->parent_ = this;
1945   - app->_configure();
1946   - }
1947   - }
  1173 + void _configure();
1948 1174  
1949 1175 /// Internal function to run (App) callback, bottom up
1950   - void run_callback(bool final_mode = false, bool suppress_final_callback = false) {
1951   - pre_callback();
1952   - // in the main app if immediate_callback_ is set it runs the main callback before the used subcommands
1953   - if(!final_mode && parse_complete_callback_) {
1954   - parse_complete_callback_();
1955   - }
1956   - // run the callbacks for the received subcommands
1957   - for(App *subc : get_subcommands()) {
1958   - if(subc->parent_ == this) {
1959   - subc->run_callback(true, suppress_final_callback);
1960   - }
1961   - }
1962   - // now run callbacks for option_groups
1963   - for(auto &subc : subcommands_) {
1964   - if(subc->name_.empty() && subc->count_all() > 0) {
1965   - subc->run_callback(true, suppress_final_callback);
1966   - }
1967   - }
1968   -
1969   - // finally run the main callback
1970   - if(final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) {
1971   - if(!name_.empty() || count_all() > 0 || parent_ == nullptr) {
1972   - final_callback_();
1973   - }
1974   - }
1975   - }
  1176 + void run_callback(bool final_mode = false, bool suppress_final_callback = false);
1976 1177  
1977 1178 /// Check to see if a subcommand is valid. Give up immediately if subcommand max has been reached.
1978   - CLI11_NODISCARD bool _valid_subcommand(const std::string &current, bool ignore_used = true) const {
1979   - // Don't match if max has been reached - but still check parents
1980   - if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
1981   - return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
1982   - }
1983   - auto *com = _find_subcommand(current, true, ignore_used);
1984   - if(com != nullptr) {
1985   - return true;
1986   - }
1987   - // Check parent if exists, else return false
1988   - return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
1989   - }
  1179 + CLI11_NODISCARD bool _valid_subcommand(const std::string &current, bool ignore_used = true) const;
1990 1180  
1991 1181 /// Selects a Classifier enum based on the type of the current argument
1992 1182 CLI11_NODISCARD detail::Classifier _recognize(const std::string &current,
1993   - bool ignore_used_subcommands = true) const {
1994   - std::string dummy1, dummy2;
1995   -
1996   - if(current == "--")
1997   - return detail::Classifier::POSITIONAL_MARK;
1998   - if(_valid_subcommand(current, ignore_used_subcommands))
1999   - return detail::Classifier::SUBCOMMAND;
2000   - if(detail::split_long(current, dummy1, dummy2))
2001   - return detail::Classifier::LONG;
2002   - if(detail::split_short(current, dummy1, dummy2)) {
2003   - if(dummy1[0] >= '0' && dummy1[0] <= '9') {
2004   - if(get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {
2005   - return detail::Classifier::NONE;
2006   - }
2007   - }
2008   - return detail::Classifier::SHORT;
2009   - }
2010   - if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
2011   - return detail::Classifier::WINDOWS_STYLE;
2012   - if((current == "++") && !name_.empty() && parent_ != nullptr)
2013   - return detail::Classifier::SUBCOMMAND_TERMINATOR;
2014   - return detail::Classifier::NONE;
2015   - }
  1183 + bool ignore_used_subcommands = true) const;
2016 1184  
2017 1185 // The parse function is now broken into several parts, and part of process
2018 1186  
2019 1187 /// Read and process a configuration file (main app only)
2020   - void _process_config_file() {
2021   - if(config_ptr_ != nullptr) {
2022   - bool config_required = config_ptr_->get_required();
2023   - auto file_given = config_ptr_->count() > 0;
2024   - auto config_files = config_ptr_->as<std::vector<std::string>>();
2025   - if(config_files.empty() || config_files.front().empty()) {
2026   - if(config_required) {
2027   - throw FileError::Missing("no specified config file");
2028   - }
2029   - return;
2030   - }
2031   - for(auto rit = config_files.rbegin(); rit != config_files.rend(); ++rit) {
2032   - const auto &config_file = *rit;
2033   - auto path_result = detail::check_path(config_file.c_str());
2034   - if(path_result == detail::path_type::file) {
2035   - try {
2036   - std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
2037   - _parse_config(values);
2038   - if(!file_given) {
2039   - config_ptr_->add_result(config_file);
2040   - }
2041   - } catch(const FileError &) {
2042   - if(config_required || file_given)
2043   - throw;
2044   - }
2045   - } else if(config_required || file_given) {
2046   - throw FileError::Missing(config_file);
2047   - }
2048   - }
2049   - }
2050   - }
  1188 + void _process_config_file();
2051 1189  
2052 1190 /// Get envname options if not yet passed. Runs on *all* subcommands.
2053   - void _process_env() {
2054   - for(const Option_p &opt : options_) {
2055   - if(opt->count() == 0 && !opt->envname_.empty()) {
2056   - char *buffer = nullptr;
2057   - std::string ename_string;
2058   -
2059   -#ifdef _MSC_VER
2060   - // Windows version
2061   - std::size_t sz = 0;
2062   - if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) {
2063   - ename_string = std::string(buffer);
2064   - free(buffer);
2065   - }
2066   -#else
2067   - // This also works on Windows, but gives a warning
2068   - buffer = std::getenv(opt->envname_.c_str());
2069   - if(buffer != nullptr)
2070   - ename_string = std::string(buffer);
2071   -#endif
2072   -
2073   - if(!ename_string.empty()) {
2074   - opt->add_result(ename_string);
2075   - }
2076   - }
2077   - }
2078   -
2079   - for(App_p &sub : subcommands_) {
2080   - if(sub->get_name().empty() || !sub->parse_complete_callback_)
2081   - sub->_process_env();
2082   - }
2083   - }
  1191 + void _process_env();
2084 1192  
2085 1193 /// Process callbacks. Runs on *all* subcommands.
2086   - void _process_callbacks() {
2087   -
2088   - for(App_p &sub : subcommands_) {
2089   - // process the priority option_groups first
2090   - if(sub->get_name().empty() && sub->parse_complete_callback_) {
2091   - if(sub->count_all() > 0) {
2092   - sub->_process_callbacks();
2093   - sub->run_callback();
2094   - }
2095   - }
2096   - }
2097   -
2098   - for(const Option_p &opt : options_) {
2099   - if((*opt) && !opt->get_callback_run()) {
2100   - opt->run_callback();
2101   - }
2102   - }
2103   - for(App_p &sub : subcommands_) {
2104   - if(!sub->parse_complete_callback_) {
2105   - sub->_process_callbacks();
2106   - }
2107   - }
2108   - }
  1194 + void _process_callbacks();
2109 1195  
2110 1196 /// Run help flag processing if any are found.
2111 1197 ///
2112 1198 /// The flags allow recursive calls to remember if there was a help flag on a parent.
2113   - void _process_help_flags(bool trigger_help = false, bool trigger_all_help = false) const {
2114   - const Option *help_ptr = get_help_ptr();
2115   - const Option *help_all_ptr = get_help_all_ptr();
2116   -
2117   - if(help_ptr != nullptr && help_ptr->count() > 0)
2118   - trigger_help = true;
2119   - if(help_all_ptr != nullptr && help_all_ptr->count() > 0)
2120   - trigger_all_help = true;
2121   -
2122   - // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones.
2123   - if(!parsed_subcommands_.empty()) {
2124   - for(const App *sub : parsed_subcommands_)
2125   - sub->_process_help_flags(trigger_help, trigger_all_help);
2126   -
2127   - // Only the final subcommand should call for help. All help wins over help.
2128   - } else if(trigger_all_help) {
2129   - throw CallForAllHelp();
2130   - } else if(trigger_help) {
2131   - throw CallForHelp();
2132   - }
2133   - }
  1199 + void _process_help_flags(bool trigger_help = false, bool trigger_all_help = false) const;
2134 1200  
2135 1201 /// Verify required options and cross requirements. Subcommands too (only if selected).
2136   - void _process_requirements() {
2137   - // check excludes
2138   - bool excluded{false};
2139   - std::string excluder;
2140   - for(const auto &opt : exclude_options_) {
2141   - if(opt->count() > 0) {
2142   - excluded = true;
2143   - excluder = opt->get_name();
2144   - }
2145   - }
2146   - for(const auto &subc : exclude_subcommands_) {
2147   - if(subc->count_all() > 0) {
2148   - excluded = true;
2149   - excluder = subc->get_display_name();
2150   - }
2151   - }
2152   - if(excluded) {
2153   - if(count_all() > 0) {
2154   - throw ExcludesError(get_display_name(), excluder);
2155   - }
2156   - // if we are excluded but didn't receive anything, just return
2157   - return;
2158   - }
2159   -
2160   - // check excludes
2161   - bool missing_needed{false};
2162   - std::string missing_need;
2163   - for(const auto &opt : need_options_) {
2164   - if(opt->count() == 0) {
2165   - missing_needed = true;
2166   - missing_need = opt->get_name();
2167   - }
2168   - }
2169   - for(const auto &subc : need_subcommands_) {
2170   - if(subc->count_all() == 0) {
2171   - missing_needed = true;
2172   - missing_need = subc->get_display_name();
2173   - }
2174   - }
2175   - if(missing_needed) {
2176   - if(count_all() > 0) {
2177   - throw RequiresError(get_display_name(), missing_need);
2178   - }
2179   - // if we missing something but didn't have any options, just return
2180   - return;
2181   - }
2182   -
2183   - std::size_t used_options = 0;
2184   - for(const Option_p &opt : options_) {
2185   -
2186   - if(opt->count() != 0) {
2187   - ++used_options;
2188   - }
2189   - // Required but empty
2190   - if(opt->get_required() && opt->count() == 0) {
2191   - throw RequiredError(opt->get_name());
2192   - }
2193   - // Requires
2194   - for(const Option *opt_req : opt->needs_)
2195   - if(opt->count() > 0 && opt_req->count() == 0)
2196   - throw RequiresError(opt->get_name(), opt_req->get_name());
2197   - // Excludes
2198   - for(const Option *opt_ex : opt->excludes_)
2199   - if(opt->count() > 0 && opt_ex->count() != 0)
2200   - throw ExcludesError(opt->get_name(), opt_ex->get_name());
2201   - }
2202   - // check for the required number of subcommands
2203   - if(require_subcommand_min_ > 0) {
2204   - auto selected_subcommands = get_subcommands();
2205   - if(require_subcommand_min_ > selected_subcommands.size())
2206   - throw RequiredError::Subcommand(require_subcommand_min_);
2207   - }
2208   -
2209   - // Max error cannot occur, the extra subcommand will parse as an ExtrasError or a remaining item.
2210   -
2211   - // run this loop to check how many unnamed subcommands were actually used since they are considered options
2212   - // from the perspective of an App
2213   - for(App_p &sub : subcommands_) {
2214   - if(sub->disabled_)
2215   - continue;
2216   - if(sub->name_.empty() && sub->count_all() > 0) {
2217   - ++used_options;
2218   - }
2219   - }
2220   -
2221   - if(require_option_min_ > used_options || (require_option_max_ > 0 && require_option_max_ < used_options)) {
2222   - auto option_list = detail::join(options_, [this](const Option_p &ptr) {
2223   - if(ptr.get() == help_ptr_ || ptr.get() == help_all_ptr_) {
2224   - return std::string{};
2225   - }
2226   - return ptr->get_name(false, true);
2227   - });
2228   -
2229   - auto subc_list = get_subcommands([](App *app) { return ((app->get_name().empty()) && (!app->disabled_)); });
2230   - if(!subc_list.empty()) {
2231   - option_list += "," + detail::join(subc_list, [](const App *app) { return app->get_display_name(); });
2232   - }
2233   - throw RequiredError::Option(require_option_min_, require_option_max_, used_options, option_list);
2234   - }
2235   -
2236   - // now process the requirements for subcommands if needed
2237   - for(App_p &sub : subcommands_) {
2238   - if(sub->disabled_)
2239   - continue;
2240   - if(sub->name_.empty() && sub->required_ == false) {
2241   - if(sub->count_all() == 0) {
2242   - if(require_option_min_ > 0 && require_option_min_ <= used_options) {
2243   - continue;
2244   - // if we have met the requirement and there is nothing in this option group skip checking
2245   - // requirements
2246   - }
2247   - if(require_option_max_ > 0 && used_options >= require_option_min_) {
2248   - continue;
2249   - // if we have met the requirement and there is nothing in this option group skip checking
2250   - // requirements
2251   - }
2252   - }
2253   - }
2254   - if(sub->count() > 0 || sub->name_.empty()) {
2255   - sub->_process_requirements();
2256   - }
2257   -
2258   - if(sub->required_ && sub->count_all() == 0) {
2259   - throw(CLI::RequiredError(sub->get_display_name()));
2260   - }
2261   - }
2262   - }
  1202 + void _process_requirements();
2263 1203  
2264 1204 /// Process callbacks and such.
2265   - void _process() {
2266   - try {
2267   - // the config file might generate a FileError but that should not be processed until later in the process
2268   - // to allow for help, version and other errors to generate first.
2269   - _process_config_file();
2270   -
2271   - // process env shouldn't throw but no reason to process it if config generated an error
2272   - _process_env();
2273   - } catch(const CLI::FileError &) {
2274   - // callbacks and help_flags can generate exceptions which should take priority
2275   - // over the config file error if one exists.
2276   - _process_callbacks();
2277   - _process_help_flags();
2278   - throw;
2279   - }
2280   -
2281   - _process_callbacks();
2282   - _process_help_flags();
2283   -
2284   - _process_requirements();
2285   - }
  1205 + void _process();
2286 1206  
2287 1207 /// Throw an error if anything is left over and should not be.
2288   - void _process_extras() {
2289   - if(!(allow_extras_ || prefix_command_)) {
2290   - std::size_t num_left_over = remaining_size();
2291   - if(num_left_over > 0) {
2292   - throw ExtrasError(name_, remaining(false));
2293   - }
2294   - }
2295   -
2296   - for(App_p &sub : subcommands_) {
2297   - if(sub->count() > 0)
2298   - sub->_process_extras();
2299   - }
2300   - }
  1208 + void _process_extras();
2301 1209  
2302 1210 /// Throw an error if anything is left over and should not be.
2303 1211 /// Modifies the args to fill in the missing items before throwing.
2304   - void _process_extras(std::vector<std::string> &args) {
2305   - if(!(allow_extras_ || prefix_command_)) {
2306   - std::size_t num_left_over = remaining_size();
2307   - if(num_left_over > 0) {
2308   - args = remaining(false);
2309   - throw ExtrasError(name_, args);
2310   - }
2311   - }
2312   -
2313   - for(App_p &sub : subcommands_) {
2314   - if(sub->count() > 0)
2315   - sub->_process_extras(args);
2316   - }
2317   - }
  1212 + void _process_extras(std::vector<std::string> &args);
2318 1213  
2319 1214 /// Internal function to recursively increment the parsed counter on the current app as well unnamed subcommands
2320   - void increment_parsed() {
2321   - ++parsed_;
2322   - for(App_p &sub : subcommands_) {
2323   - if(sub->get_name().empty())
2324   - sub->increment_parsed();
2325   - }
2326   - }
2327   - /// Internal parse function
2328   - void _parse(std::vector<std::string> &args) {
2329   - increment_parsed();
2330   - _trigger_pre_parse(args.size());
2331   - bool positional_only = false;
2332   -
2333   - while(!args.empty()) {
2334   - if(!_parse_single(args, positional_only)) {
2335   - break;
2336   - }
2337   - }
2338   -
2339   - if(parent_ == nullptr) {
2340   - _process();
2341   -
2342   - // Throw error if any items are left over (depending on settings)
2343   - _process_extras(args);
2344   -
2345   - // Convert missing (pairs) to extras (string only) ready for processing in another app
2346   - args = remaining_for_passthrough(false);
2347   - } else if(parse_complete_callback_) {
2348   - _process_env();
2349   - _process_callbacks();
2350   - _process_help_flags();
2351   - _process_requirements();
2352   - run_callback(false, true);
2353   - }
2354   - }
  1215 + void increment_parsed();
2355 1216  
2356 1217 /// Internal parse function
2357   - void _parse(std::vector<std::string> &&args) {
2358   - // this can only be called by the top level in which case parent == nullptr by definition
2359   - // operation is simplified
2360   - increment_parsed();
2361   - _trigger_pre_parse(args.size());
2362   - bool positional_only = false;
2363   -
2364   - while(!args.empty()) {
2365   - _parse_single(args, positional_only);
2366   - }
2367   - _process();
  1218 + void _parse(std::vector<std::string> &args);
2368 1219  
2369   - // Throw error if any items are left over (depending on settings)
2370   - _process_extras();
2371   - }
  1220 + /// Internal parse function
  1221 + void _parse(std::vector<std::string> &&args);
2372 1222  
2373 1223 /// Internal function to parse a stream
2374   - void _parse_stream(std::istream &input) {
2375   - auto values = config_formatter_->from_config(input);
2376   - _parse_config(values);
2377   - increment_parsed();
2378   - _trigger_pre_parse(values.size());
2379   - _process();
2380   -
2381   - // Throw error if any items are left over (depending on settings)
2382   - _process_extras();
2383   - }
  1224 + void _parse_stream(std::istream &input);
2384 1225  
2385 1226 /// Parse one config param, return false if not found in any subcommand, remove if it is
2386 1227 ///
2387 1228 /// If this has more than one dot.separated.name, go into the subcommand matching it
2388 1229 /// Returns true if it managed to find the option, if false you'll need to remove the arg manually.
2389   - void _parse_config(const std::vector<ConfigItem> &args) {
2390   - for(const ConfigItem &item : args) {
2391   - if(!_parse_single_config(item) && allow_config_extras_ == config_extras_mode::error)
2392   - throw ConfigError::Extras(item.fullname());
2393   - }
2394   - }
  1230 + void _parse_config(const std::vector<ConfigItem> &args);
2395 1231  
2396 1232 /// Fill in a single config option
2397   - bool _parse_single_config(const ConfigItem &item, std::size_t level = 0) {
2398   - if(level < item.parents.size()) {
2399   - try {
2400   - auto *subcom = get_subcommand(item.parents.at(level));
2401   - auto result = subcom->_parse_single_config(item, level + 1);
2402   -
2403   - return result;
2404   - } catch(const OptionNotFound &) {
2405   - return false;
2406   - }
2407   - }
2408   - // check for section open
2409   - if(item.name == "++") {
2410   - if(configurable_) {
2411   - increment_parsed();
2412   - _trigger_pre_parse(2);
2413   - if(parent_ != nullptr) {
2414   - parent_->parsed_subcommands_.push_back(this);
2415   - }
2416   - }
2417   - return true;
2418   - }
2419   - // check for section close
2420   - if(item.name == "--") {
2421   - if(configurable_ && parse_complete_callback_) {
2422   - _process_callbacks();
2423   - _process_requirements();
2424   - run_callback();
2425   - }
2426   - return true;
2427   - }
2428   - Option *op = get_option_no_throw("--" + item.name);
2429   - if(op == nullptr) {
2430   - if(item.name.size() == 1) {
2431   - op = get_option_no_throw("-" + item.name);
2432   - }
2433   - }
2434   - if(op == nullptr) {
2435   - op = get_option_no_throw(item.name);
2436   - }
2437   - if(op == nullptr) {
2438   - // If the option was not present
2439   - if(get_allow_config_extras() == config_extras_mode::capture)
2440   - // Should we worry about classifying the extras properly?
2441   - missing_.emplace_back(detail::Classifier::NONE, item.fullname());
2442   - return false;
2443   - }
2444   -
2445   - if(!op->get_configurable()) {
2446   - if(get_allow_config_extras() == config_extras_mode::ignore_all) {
2447   - return false;
2448   - }
2449   - throw ConfigError::NotConfigurable(item.fullname());
2450   - }
2451   -
2452   - if(op->empty()) {
2453   -
2454   - if(op->get_expected_min() == 0) {
2455   - // Flag parsing
2456   - auto res = config_formatter_->to_flag(item);
2457   - res = op->get_flag_value(item.name, res);
2458   -
2459   - op->add_result(res);
2460   -
2461   - } else {
2462   - op->add_result(item.inputs);
2463   - op->run_callback();
2464   - }
2465   - }
2466   -
2467   - return true;
2468   - }
  1233 + bool _parse_single_config(const ConfigItem &item, std::size_t level = 0);
2469 1234  
2470 1235 /// Parse "one" argument (some may eat more than one), delegate to parent if fails, add to missing if missing
2471 1236 /// from main return false if the parse has failed and needs to return to parent
2472   - bool _parse_single(std::vector<std::string> &args, bool &positional_only) {
2473   - bool retval = true;
2474   - detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back());
2475   - switch(classifier) {
2476   - case detail::Classifier::POSITIONAL_MARK:
2477   - args.pop_back();
2478   - positional_only = true;
2479   - if((!_has_remaining_positionals()) && (parent_ != nullptr)) {
2480   - retval = false;
2481   - } else {
2482   - _move_to_missing(classifier, "--");
2483   - }
2484   - break;
2485   - case detail::Classifier::SUBCOMMAND_TERMINATOR:
2486   - // treat this like a positional mark if in the parent app
2487   - args.pop_back();
2488   - retval = false;
2489   - break;
2490   - case detail::Classifier::SUBCOMMAND:
2491   - retval = _parse_subcommand(args);
2492   - break;
2493   - case detail::Classifier::LONG:
2494   - case detail::Classifier::SHORT:
2495   - case detail::Classifier::WINDOWS_STYLE:
2496   - // If already parsed a subcommand, don't accept options_
2497   - _parse_arg(args, classifier);
2498   - break;
2499   - case detail::Classifier::NONE:
2500   - // Probably a positional or something for a parent (sub)command
2501   - retval = _parse_positional(args, false);
2502   - if(retval && positionals_at_end_) {
2503   - positional_only = true;
2504   - }
2505   - break;
2506   - // LCOV_EXCL_START
2507   - default:
2508   - throw HorribleError("unrecognized classifier (you should not see this!)");
2509   - // LCOV_EXCL_STOP
2510   - }
2511   - return retval;
2512   - }
  1237 + bool _parse_single(std::vector<std::string> &args, bool &positional_only);
2513 1238  
2514 1239 /// Count the required remaining positional arguments
2515   - CLI11_NODISCARD std::size_t _count_remaining_positionals(bool required_only = false) const {
2516   - std::size_t retval = 0;
2517   - for(const Option_p &opt : options_) {
2518   - if(opt->get_positional() && (!required_only || opt->get_required())) {
2519   - if(opt->get_items_expected_min() > 0 &&
2520   - static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
2521   - retval += static_cast<std::size_t>(opt->get_items_expected_min()) - opt->count();
2522   - }
2523   - }
2524   - }
2525   - return retval;
2526   - }
  1240 + CLI11_NODISCARD std::size_t _count_remaining_positionals(bool required_only = false) const;
2527 1241  
2528 1242 /// Count the required remaining positional arguments
2529   - CLI11_NODISCARD bool _has_remaining_positionals() const {
2530   - for(const Option_p &opt : options_) {
2531   - if(opt->get_positional() && ((static_cast<int>(opt->count()) < opt->get_items_expected_min()))) {
2532   - return true;
2533   - }
2534   - }
2535   -
2536   - return false;
2537   - }
  1243 + CLI11_NODISCARD bool _has_remaining_positionals() const;
2538 1244  
2539 1245 /// Parse a positional, go up the tree to check
2540 1246 /// @param haltOnSubcommand if set to true the operation will not process subcommands merely return false
2541 1247 /// Return true if the positional was used false otherwise
2542   - bool _parse_positional(std::vector<std::string> &args, bool haltOnSubcommand) {
2543   -
2544   - const std::string &positional = args.back();
2545   -
2546   - if(positionals_at_end_) {
2547   - // deal with the case of required arguments at the end which should take precedence over other arguments
2548   - auto arg_rem = args.size();
2549   - auto remreq = _count_remaining_positionals(true);
2550   - if(arg_rem <= remreq) {
2551   - for(const Option_p &opt : options_) {
2552   - if(opt->get_positional() && opt->required_) {
2553   - if(static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
2554   - if(validate_positionals_) {
2555   - std::string pos = positional;
2556   - pos = opt->_validate(pos, 0);
2557   - if(!pos.empty()) {
2558   - continue;
2559   - }
2560   - }
2561   -
2562   - parse_order_.push_back(opt.get());
2563   - /// if we require a separator add it here
2564   - if(opt->get_inject_separator()) {
2565   - if(!opt->results().empty() && !opt->results().back().empty()) {
2566   - opt->add_result(std::string{});
2567   - }
2568   - }
2569   - if(opt->get_trigger_on_parse() &&
2570   - opt->current_option_state_ == Option::option_state::callback_run) {
2571   - opt->clear();
2572   - }
2573   - opt->add_result(positional);
2574   - if(opt->get_trigger_on_parse()) {
2575   - opt->run_callback();
2576   - }
2577   - args.pop_back();
2578   - return true;
2579   - }
2580   - }
2581   - }
2582   - }
2583   - }
2584   - for(const Option_p &opt : options_) {
2585   - // Eat options, one by one, until done
2586   - if(opt->get_positional() &&
2587   - (static_cast<int>(opt->count()) < opt->get_items_expected_min() || opt->get_allow_extra_args())) {
2588   - if(validate_positionals_) {
2589   - std::string pos = positional;
2590   - pos = opt->_validate(pos, 0);
2591   - if(!pos.empty()) {
2592   - continue;
2593   - }
2594   - }
2595   - if(opt->get_inject_separator()) {
2596   - if(!opt->results().empty() && !opt->results().back().empty()) {
2597   - opt->add_result(std::string{});
2598   - }
2599   - }
2600   - if(opt->get_trigger_on_parse() && opt->current_option_state_ == Option::option_state::callback_run) {
2601   - opt->clear();
2602   - }
2603   - opt->add_result(positional);
2604   - if(opt->get_trigger_on_parse()) {
2605   - opt->run_callback();
2606   - }
2607   - parse_order_.push_back(opt.get());
2608   - args.pop_back();
2609   - return true;
2610   - }
2611   - }
2612   -
2613   - for(auto &subc : subcommands_) {
2614   - if((subc->name_.empty()) && (!subc->disabled_)) {
2615   - if(subc->_parse_positional(args, false)) {
2616   - if(!subc->pre_parse_called_) {
2617   - subc->_trigger_pre_parse(args.size());
2618   - }
2619   - return true;
2620   - }
2621   - }
2622   - }
2623   - // let the parent deal with it if possible
2624   - if(parent_ != nullptr && fallthrough_)
2625   - return _get_fallthrough_parent()->_parse_positional(args, static_cast<bool>(parse_complete_callback_));
2626   -
2627   - /// Try to find a local subcommand that is repeated
2628   - auto *com = _find_subcommand(args.back(), true, false);
2629   - if(com != nullptr && (require_subcommand_max_ == 0 || require_subcommand_max_ > parsed_subcommands_.size())) {
2630   - if(haltOnSubcommand) {
2631   - return false;
2632   - }
2633   - args.pop_back();
2634   - com->_parse(args);
2635   - return true;
2636   - }
2637   - /// now try one last gasp at subcommands that have been executed before, go to root app and try to find a
2638   - /// subcommand in a broader way, if one exists let the parent deal with it
2639   - auto *parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
2640   - com = parent_app->_find_subcommand(args.back(), true, false);
2641   - if(com != nullptr && (com->parent_->require_subcommand_max_ == 0 ||
2642   - com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size())) {
2643   - return false;
2644   - }
2645   -
2646   - if(positionals_at_end_) {
2647   - throw CLI::ExtrasError(name_, args);
2648   - }
2649   - /// If this is an option group don't deal with it
2650   - if(parent_ != nullptr && name_.empty()) {
2651   - return false;
2652   - }
2653   - /// We are out of other options this goes to missing
2654   - _move_to_missing(detail::Classifier::NONE, positional);
2655   - args.pop_back();
2656   - if(prefix_command_) {
2657   - while(!args.empty()) {
2658   - _move_to_missing(detail::Classifier::NONE, args.back());
2659   - args.pop_back();
2660   - }
2661   - }
2662   -
2663   - return true;
2664   - }
  1248 + bool _parse_positional(std::vector<std::string> &args, bool haltOnSubcommand);
2665 1249  
2666 1250 /// Locate a subcommand by name with two conditions, should disabled subcommands be ignored, and should used
2667 1251 /// subcommands be ignored
2668 1252 CLI11_NODISCARD App *
2669   - _find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept {
2670   - for(const App_p &com : subcommands_) {
2671   - if(com->disabled_ && ignore_disabled)
2672   - continue;
2673   - if(com->get_name().empty()) {
2674   - auto *subc = com->_find_subcommand(subc_name, ignore_disabled, ignore_used);
2675   - if(subc != nullptr) {
2676   - return subc;
2677   - }
2678   - }
2679   - if(com->check_name(subc_name)) {
2680   - if((!*com) || !ignore_used)
2681   - return com.get();
2682   - }
2683   - }
2684   - return nullptr;
2685   - }
  1253 + _find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept;
2686 1254  
2687 1255 /// Parse a subcommand, modify args and continue
2688 1256 ///
2689 1257 /// Unlike the others, this one will always allow fallthrough
2690 1258 /// return true if the subcommand was processed false otherwise
2691   - bool _parse_subcommand(std::vector<std::string> &args) {
2692   - if(_count_remaining_positionals(/* required */ true) > 0) {
2693   - _parse_positional(args, false);
2694   - return true;
2695   - }
2696   - auto *com = _find_subcommand(args.back(), true, true);
2697   - if(com != nullptr) {
2698   - args.pop_back();
2699   - if(!com->silent_) {
2700   - parsed_subcommands_.push_back(com);
2701   - }
2702   - com->_parse(args);
2703   - auto *parent_app = com->parent_;
2704   - while(parent_app != this) {
2705   - parent_app->_trigger_pre_parse(args.size());
2706   - if(!com->silent_) {
2707   - parent_app->parsed_subcommands_.push_back(com);
2708   - }
2709   - parent_app = parent_app->parent_;
2710   - }
2711   - return true;
2712   - }
2713   -
2714   - if(parent_ == nullptr)
2715   - throw HorribleError("Subcommand " + args.back() + " missing");
2716   - return false;
2717   - }
  1259 + bool _parse_subcommand(std::vector<std::string> &args);
2718 1260  
2719 1261 /// Parse a short (false) or long (true) argument, must be at the top of the list
2720 1262 /// return true if the argument was processed or false if nothing was done
2721   - bool _parse_arg(std::vector<std::string> &args, detail::Classifier current_type) {
2722   -
2723   - std::string current = args.back();
2724   -
2725   - std::string arg_name;
2726   - std::string value;
2727   - std::string rest;
2728   -
2729   - switch(current_type) {
2730   - case detail::Classifier::LONG:
2731   - if(!detail::split_long(current, arg_name, value))
2732   - throw HorribleError("Long parsed but missing (you should not see this):" + args.back());
2733   - break;
2734   - case detail::Classifier::SHORT:
2735   - if(!detail::split_short(current, arg_name, rest))
2736   - throw HorribleError("Short parsed but missing! You should not see this");
2737   - break;
2738   - case detail::Classifier::WINDOWS_STYLE:
2739   - if(!detail::split_windows_style(current, arg_name, value))
2740   - throw HorribleError("windows option parsed but missing! You should not see this");
2741   - break;
2742   - case detail::Classifier::SUBCOMMAND:
2743   - case detail::Classifier::SUBCOMMAND_TERMINATOR:
2744   - case detail::Classifier::POSITIONAL_MARK:
2745   - case detail::Classifier::NONE:
2746   - default:
2747   - throw HorribleError("parsing got called with invalid option! You should not see this");
2748   - }
2749   -
2750   - auto op_ptr =
2751   - std::find_if(std::begin(options_), std::end(options_), [arg_name, current_type](const Option_p &opt) {
2752   - if(current_type == detail::Classifier::LONG)
2753   - return opt->check_lname(arg_name);
2754   - if(current_type == detail::Classifier::SHORT)
2755   - return opt->check_sname(arg_name);
2756   - // this will only get called for detail::Classifier::WINDOWS_STYLE
2757   - return opt->check_lname(arg_name) || opt->check_sname(arg_name);
2758   - });
2759   -
2760   - // Option not found
2761   - if(op_ptr == std::end(options_)) {
2762   - for(auto &subc : subcommands_) {
2763   - if(subc->name_.empty() && !subc->disabled_) {
2764   - if(subc->_parse_arg(args, current_type)) {
2765   - if(!subc->pre_parse_called_) {
2766   - subc->_trigger_pre_parse(args.size());
2767   - }
2768   - return true;
2769   - }
2770   - }
2771   - }
2772   -
2773   - // don't capture missing if this is a nameless subcommand and nameless subcommands can't fallthrough
2774   - if(parent_ != nullptr && name_.empty()) {
2775   - return false;
2776   - }
2777   -
2778   - // If a subcommand, try the main command
2779   - if(parent_ != nullptr && fallthrough_)
2780   - return _get_fallthrough_parent()->_parse_arg(args, current_type);
2781   -
2782   - // Otherwise, add to missing
2783   - args.pop_back();
2784   - _move_to_missing(current_type, current);
2785   - return true;
2786   - }
2787   -
2788   - args.pop_back();
2789   -
2790   - // Get a reference to the pointer to make syntax bearable
2791   - Option_p &op = *op_ptr;
2792   - /// if we require a separator add it here
2793   - if(op->get_inject_separator()) {
2794   - if(!op->results().empty() && !op->results().back().empty()) {
2795   - op->add_result(std::string{});
2796   - }
2797   - }
2798   - if(op->get_trigger_on_parse() && op->current_option_state_ == Option::option_state::callback_run) {
2799   - op->clear();
2800   - }
2801   - int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min());
2802   - int max_num = op->get_items_expected_max();
2803   - // check container like options to limit the argument size to a single type if the allow_extra_flags argument is
2804   - // set. 16 is somewhat arbitrary (needs to be at least 4)
2805   - if(max_num >= detail::expected_max_vector_size / 16 && !op->get_allow_extra_args()) {
2806   - auto tmax = op->get_type_size_max();
2807   - max_num = detail::checked_multiply(tmax, op->get_expected_min()) ? tmax : detail::expected_max_vector_size;
2808   - }
2809   - // Make sure we always eat the minimum for unlimited vectors
2810   - int collected = 0; // total number of arguments collected
2811   - int result_count = 0; // local variable for number of results in a single arg string
2812   - // deal with purely flag like things
2813   - if(max_num == 0) {
2814   - auto res = op->get_flag_value(arg_name, value);
2815   - op->add_result(res);
2816   - parse_order_.push_back(op.get());
2817   - } else if(!value.empty()) { // --this=value
2818   - op->add_result(value, result_count);
2819   - parse_order_.push_back(op.get());
2820   - collected += result_count;
2821   - // -Trest
2822   - } else if(!rest.empty()) {
2823   - op->add_result(rest, result_count);
2824   - parse_order_.push_back(op.get());
2825   - rest = "";
2826   - collected += result_count;
2827   - }
2828   -
2829   - // gather the minimum number of arguments
2830   - while(min_num > collected && !args.empty()) {
2831   - std::string current_ = args.back();
2832   - args.pop_back();
2833   - op->add_result(current_, result_count);
2834   - parse_order_.push_back(op.get());
2835   - collected += result_count;
2836   - }
2837   -
2838   - if(min_num > collected) { // if we have run out of arguments and the minimum was not met
2839   - throw ArgumentMismatch::TypedAtLeast(op->get_name(), min_num, op->get_type_name());
2840   - }
2841   -
2842   - // now check for optional arguments
2843   - if(max_num > collected || op->get_allow_extra_args()) { // we allow optional arguments
2844   - auto remreqpos = _count_remaining_positionals(true);
2845   - // we have met the minimum now optionally check up to the maximum
2846   - while((collected < max_num || op->get_allow_extra_args()) && !args.empty() &&
2847   - _recognize(args.back(), false) == detail::Classifier::NONE) {
2848   - // If any required positionals remain, don't keep eating
2849   - if(remreqpos >= args.size()) {
2850   - break;
2851   - }
2852   - if(validate_optional_arguments_) {
2853   - std::string arg = args.back();
2854   - arg = op->_validate(arg, 0);
2855   - if(!arg.empty()) {
2856   - break;
2857   - }
2858   - }
2859   - op->add_result(args.back(), result_count);
2860   - parse_order_.push_back(op.get());
2861   - args.pop_back();
2862   - collected += result_count;
2863   - }
2864   -
2865   - // Allow -- to end an unlimited list and "eat" it
2866   - if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)
2867   - args.pop_back();
2868   - // optional flag that didn't receive anything now get the default value
2869   - if(min_num == 0 && max_num > 0 && collected == 0) {
2870   - auto res = op->get_flag_value(arg_name, std::string{});
2871   - op->add_result(res);
2872   - parse_order_.push_back(op.get());
2873   - }
2874   - }
2875   - // if we only partially completed a type then add an empty string if allowed for later processing
2876   - if(min_num > 0 && (collected % op->get_type_size_max()) != 0) {
2877   - if(op->get_type_size_max() != op->get_type_size_min()) {
2878   - op->add_result(std::string{});
2879   - } else {
2880   - throw ArgumentMismatch::PartialType(op->get_name(), op->get_type_size_min(), op->get_type_name());
2881   - }
2882   - }
2883   - if(op->get_trigger_on_parse()) {
2884   - op->run_callback();
2885   - }
2886   - if(!rest.empty()) {
2887   - rest = "-" + rest;
2888   - args.push_back(rest);
2889   - }
2890   - return true;
2891   - }
  1263 + bool _parse_arg(std::vector<std::string> &args, detail::Classifier current_type);
2892 1264  
2893 1265 /// Trigger the pre_parse callback if needed
2894   - void _trigger_pre_parse(std::size_t remaining_args) {
2895   - if(!pre_parse_called_) {
2896   - pre_parse_called_ = true;
2897   - if(pre_parse_callback_) {
2898   - pre_parse_callback_(remaining_args);
2899   - }
2900   - } else if(immediate_callback_) {
2901   - if(!name_.empty()) {
2902   - auto pcnt = parsed_;
2903   - auto extras = std::move(missing_);
2904   - clear();
2905   - parsed_ = pcnt;
2906   - pre_parse_called_ = true;
2907   - missing_ = std::move(extras);
2908   - }
2909   - }
2910   - }
  1266 + void _trigger_pre_parse(std::size_t remaining_args);
2911 1267  
2912 1268 /// Get the appropriate parent to fallthrough to which is the first one that has a name or the main app
2913   - App *_get_fallthrough_parent() {
2914   - if(parent_ == nullptr) {
2915   - throw(HorribleError("No Valid parent"));
2916   - }
2917   - auto *fallthrough_parent = parent_;
2918   - while((fallthrough_parent->parent_ != nullptr) && (fallthrough_parent->get_name().empty())) {
2919   - fallthrough_parent = fallthrough_parent->parent_;
2920   - }
2921   - return fallthrough_parent;
2922   - }
  1269 + App *_get_fallthrough_parent();
2923 1270  
2924 1271 /// Helper function to run through all possible comparisons of subcommand names to check there is no overlap
2925   - CLI11_NODISCARD const std::string &_compare_subcommand_names(const App &subcom, const App &base) const {
2926   - static const std::string estring;
2927   - if(subcom.disabled_) {
2928   - return estring;
2929   - }
2930   - for(const auto &subc : base.subcommands_) {
2931   - if(subc.get() != &subcom) {
2932   - if(subc->disabled_) {
2933   - continue;
2934   - }
2935   - if(!subcom.get_name().empty()) {
2936   - if(subc->check_name(subcom.get_name())) {
2937   - return subcom.get_name();
2938   - }
2939   - }
2940   - if(!subc->get_name().empty()) {
2941   - if(subcom.check_name(subc->get_name())) {
2942   - return subc->get_name();
2943   - }
2944   - }
2945   - for(const auto &les : subcom.aliases_) {
2946   - if(subc->check_name(les)) {
2947   - return les;
2948   - }
2949   - }
2950   - // this loop is needed in case of ignore_underscore or ignore_case on one but not the other
2951   - for(const auto &les : subc->aliases_) {
2952   - if(subcom.check_name(les)) {
2953   - return les;
2954   - }
2955   - }
2956   - // if the subcommand is an option group we need to check deeper
2957   - if(subc->get_name().empty()) {
2958   - const auto &cmpres = _compare_subcommand_names(subcom, *subc);
2959   - if(!cmpres.empty()) {
2960   - return cmpres;
2961   - }
2962   - }
2963   - // if the test subcommand is an option group we need to check deeper
2964   - if(subcom.get_name().empty()) {
2965   - const auto &cmpres = _compare_subcommand_names(*subc, subcom);
2966   - if(!cmpres.empty()) {
2967   - return cmpres;
2968   - }
2969   - }
2970   - }
2971   - }
2972   - return estring;
2973   - }
  1272 + CLI11_NODISCARD const std::string &_compare_subcommand_names(const App &subcom, const App &base) const;
  1273 +
2974 1274 /// Helper function to place extra values in the most appropriate position
2975   - void _move_to_missing(detail::Classifier val_type, const std::string &val) {
2976   - if(allow_extras_ || subcommands_.empty()) {
2977   - missing_.emplace_back(val_type, val);
2978   - return;
2979   - }
2980   - // allow extra arguments to be places in an option group if it is allowed there
2981   - for(auto &subc : subcommands_) {
2982   - if(subc->name_.empty() && subc->allow_extras_) {
2983   - subc->missing_.emplace_back(val_type, val);
2984   - return;
2985   - }
2986   - }
2987   - // if we haven't found any place to put them yet put them in missing
2988   - missing_.emplace_back(val_type, val);
2989   - }
  1275 + void _move_to_missing(detail::Classifier val_type, const std::string &val);
2990 1276  
2991 1277 public:
2992 1278 /// function that could be used by subclasses of App to shift options around into subcommands
2993   - void _move_option(Option *opt, App *app) {
2994   - if(opt == nullptr) {
2995   - throw OptionNotFound("the option is NULL");
2996   - }
2997   - // verify that the give app is actually a subcommand
2998   - bool found = false;
2999   - for(auto &subc : subcommands_) {
3000   - if(app == subc.get()) {
3001   - found = true;
3002   - }
3003   - }
3004   - if(!found) {
3005   - throw OptionNotFound("The Given app is not a subcommand");
3006   - }
3007   -
3008   - if((help_ptr_ == opt) || (help_all_ptr_ == opt))
3009   - throw OptionAlreadyAdded("cannot move help options");
3010   -
3011   - if(config_ptr_ == opt)
3012   - throw OptionAlreadyAdded("cannot move config file options");
3013   -
3014   - auto iterator =
3015   - std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
3016   - if(iterator != std::end(options_)) {
3017   - const auto &opt_p = *iterator;
3018   - if(std::find_if(std::begin(app->options_), std::end(app->options_), [&opt_p](const Option_p &v) {
3019   - return (*v == *opt_p);
3020   - }) == std::end(app->options_)) {
3021   - // only erase after the insertion was successful
3022   - app->options_.push_back(std::move(*iterator));
3023   - options_.erase(iterator);
3024   - } else {
3025   - throw OptionAlreadyAdded("option was not located: " + opt->get_name());
3026   - }
3027   - } else {
3028   - throw OptionNotFound("could not locate the given Option");
3029   - }
3030   - }
  1279 + void _move_option(Option *opt, App *app);
3031 1280 }; // namespace CLI
3032 1281  
3033 1282 /// Extension of App to better manage groups of options
... ... @@ -3063,62 +1312,21 @@ class Option_group : public App {
3063 1312 return subcom;
3064 1313 }
3065 1314 };
3066   -/// Helper function to enable one option group/subcommand when another is used
3067   -inline void TriggerOn(App *trigger_app, App *app_to_enable) {
3068   - app_to_enable->enabled_by_default(false);
3069   - app_to_enable->disabled_by_default();
3070   - trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(false); });
3071   -}
3072 1315  
3073 1316 /// Helper function to enable one option group/subcommand when another is used
3074   -inline void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
3075   - for(auto &app : apps_to_enable) {
3076   - app->enabled_by_default(false);
3077   - app->disabled_by_default();
3078   - }
  1317 +CLI11_INLINE void TriggerOn(App *trigger_app, App *app_to_enable);
3079 1318  
3080   - trigger_app->preparse_callback([apps_to_enable](std::size_t) {
3081   - for(const auto &app : apps_to_enable) {
3082   - app->disabled(false);
3083   - }
3084   - });
3085   -}
  1319 +/// Helper function to enable one option group/subcommand when another is used
  1320 +CLI11_INLINE void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable);
3086 1321  
3087 1322 /// Helper function to disable one option group/subcommand when another is used
3088   -inline void TriggerOff(App *trigger_app, App *app_to_enable) {
3089   - app_to_enable->disabled_by_default(false);
3090   - app_to_enable->enabled_by_default();
3091   - trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(); });
3092   -}
  1323 +CLI11_INLINE void TriggerOff(App *trigger_app, App *app_to_enable);
3093 1324  
3094 1325 /// Helper function to disable one option group/subcommand when another is used
3095   -inline void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable) {
3096   - for(auto &app : apps_to_enable) {
3097   - app->disabled_by_default(false);
3098   - app->enabled_by_default();
3099   - }
3100   -
3101   - trigger_app->preparse_callback([apps_to_enable](std::size_t) {
3102   - for(const auto &app : apps_to_enable) {
3103   - app->disabled();
3104   - }
3105   - });
3106   -}
  1326 +CLI11_INLINE void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable);
3107 1327  
3108 1328 /// Helper function to mark an option as deprecated
3109   -inline void deprecate_option(Option *opt, const std::string &replacement = "") {
3110   - Validator deprecate_warning{[opt, replacement](std::string &) {
3111   - std::cout << opt->get_name() << " is deprecated please use '" << replacement
3112   - << "' instead\n";
3113   - return std::string();
3114   - },
3115   - "DEPRECATED"};
3116   - deprecate_warning.application_index(0);
3117   - opt->check(deprecate_warning);
3118   - if(!replacement.empty()) {
3119   - opt->description(opt->get_description() + " DEPRECATED: please use '" + replacement + "' instead");
3120   - }
3121   -}
  1329 +CLI11_INLINE void deprecate_option(Option *opt, const std::string &replacement = "");
3122 1330  
3123 1331 /// Helper function to mark an option as deprecated
3124 1332 inline void deprecate_option(App *app, const std::string &option_name, const std::string &replacement = "") {
... ... @@ -3133,84 +1341,24 @@ inline void deprecate_option(App &amp;app, const std::string &amp;option_name, const std
3133 1341 }
3134 1342  
3135 1343 /// Helper function to mark an option as retired
3136   -inline void retire_option(App *app, Option *opt) {
3137   - App temp;
3138   - auto *option_copy = temp.add_option(opt->get_name(false, true))
3139   - ->type_size(opt->get_type_size_min(), opt->get_type_size_max())
3140   - ->expected(opt->get_expected_min(), opt->get_expected_max())
3141   - ->allow_extra_args(opt->get_allow_extra_args());
3142   -
3143   - app->remove_option(opt);
3144   - auto *opt2 = app->add_option(option_copy->get_name(false, true), "option has been retired and has no effect")
3145   - ->type_name("RETIRED")
3146   - ->default_str("RETIRED")
3147   - ->type_size(option_copy->get_type_size_min(), option_copy->get_type_size_max())
3148   - ->expected(option_copy->get_expected_min(), option_copy->get_expected_max())
3149   - ->allow_extra_args(option_copy->get_allow_extra_args());
3150   -
3151   - Validator retired_warning{[opt2](std::string &) {
3152   - std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
3153   - return std::string();
3154   - },
3155   - ""};
3156   - retired_warning.application_index(0);
3157   - opt2->check(retired_warning);
3158   -}
  1344 +CLI11_INLINE void retire_option(App *app, Option *opt);
3159 1345  
3160 1346 /// Helper function to mark an option as retired
3161   -inline void retire_option(App &app, Option *opt) { retire_option(&app, opt); }
  1347 +CLI11_INLINE void retire_option(App &app, Option *opt);
3162 1348  
3163 1349 /// Helper function to mark an option as retired
3164   -inline void retire_option(App *app, const std::string &option_name) {
3165   -
3166   - auto *opt = app->get_option_no_throw(option_name);
3167   - if(opt != nullptr) {
3168   - retire_option(app, opt);
3169   - return;
3170   - }
3171   - auto *opt2 = app->add_option(option_name, "option has been retired and has no effect")
3172   - ->type_name("RETIRED")
3173   - ->expected(0, 1)
3174   - ->default_str("RETIRED");
3175   - Validator retired_warning{[opt2](std::string &) {
3176   - std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
3177   - return std::string();
3178   - },
3179   - ""};
3180   - retired_warning.application_index(0);
3181   - opt2->check(retired_warning);
3182   -}
  1350 +CLI11_INLINE void retire_option(App *app, const std::string &option_name);
3183 1351  
3184 1352 /// Helper function to mark an option as retired
3185   -inline void retire_option(App &app, const std::string &option_name) { retire_option(&app, option_name); }
  1353 +CLI11_INLINE void retire_option(App &app, const std::string &option_name);
3186 1354  
3187 1355 namespace FailureMessage {
3188 1356  
3189 1357 /// Printout a clean, simple message on error (the default in CLI11 1.5+)
3190   -inline std::string simple(const App *app, const Error &e) {
3191   - std::string header = std::string(e.what()) + "\n";
3192   - std::vector<std::string> names;
3193   -
3194   - // Collect names
3195   - if(app->get_help_ptr() != nullptr)
3196   - names.push_back(app->get_help_ptr()->get_name());
3197   -
3198   - if(app->get_help_all_ptr() != nullptr)
3199   - names.push_back(app->get_help_all_ptr()->get_name());
3200   -
3201   - // If any names found, suggest those
3202   - if(!names.empty())
3203   - header += "Run with " + detail::join(names, " or ") + " for more information.\n";
3204   -
3205   - return header;
3206   -}
  1358 +CLI11_INLINE std::string simple(const App *app, const Error &e);
3207 1359  
3208 1360 /// Printout the full help string on error (if this fn is set, the old default for CLI11)
3209   -inline std::string help(const App *app, const Error &e) {
3210   - std::string header = std::string("ERROR: ") + e.get_name() + ": " + e.what() + "\n";
3211   - header += app->help();
3212   - return header;
3213   -}
  1361 +CLI11_INLINE std::string help(const App *app, const Error &e);
3214 1362  
3215 1363 } // namespace FailureMessage
3216 1364  
... ... @@ -3250,3 +1398,7 @@ struct AppFriend {
3250 1398  
3251 1399 // [CLI11:app_hpp:end]
3252 1400 } // namespace CLI
  1401 +
  1402 +#ifndef CLI11_COMPILE
  1403 +#include "impl/App_inl.hpp"
  1404 +#endif
... ...
include/CLI/Macros.hpp
... ... @@ -65,4 +65,11 @@
65 65 #define CLI11_USE_STATIC_RTTI 1
66 66 #endif
67 67 #endif
  68 +
  69 +/** Inline macro **/
  70 +#ifdef CLI11_COMPILE
  71 +#define CLI11_INLINE
  72 +#else
  73 +#define CLI11_INLINE inline
  74 +#endif
68 75 // [CLI11:macros_hpp:end]
... ...
include/CLI/impl/App_inl.hpp 0 → 100644
  1 +// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
  2 +// under NSF AWARD 1414736 and by the respective contributors.
  3 +// All rights reserved.
  4 +//
  5 +// SPDX-License-Identifier: BSD-3-Clause
  6 +
  7 +#pragma once
  8 +
  9 +// This include is only needed for IDEs to discover symbols
  10 +#include <CLI/App.hpp>
  11 +
  12 +// [CLI11:public_includes:set]
  13 +#include <algorithm>
  14 +#include <memory>
  15 +#include <string>
  16 +#include <utility>
  17 +#include <vector>
  18 +// [CLI11:public_includes:end]
  19 +
  20 +namespace CLI {
  21 +// [CLI11:app_inl_hpp:verbatim]
  22 +
  23 +CLI11_INLINE App::App(std::string app_description, std::string app_name, App *parent)
  24 + : name_(std::move(app_name)), description_(std::move(app_description)), parent_(parent) {
  25 + // Inherit if not from a nullptr
  26 + if(parent_ != nullptr) {
  27 + if(parent_->help_ptr_ != nullptr)
  28 + set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description());
  29 + if(parent_->help_all_ptr_ != nullptr)
  30 + set_help_all_flag(parent_->help_all_ptr_->get_name(false, true), parent_->help_all_ptr_->get_description());
  31 +
  32 + /// OptionDefaults
  33 + option_defaults_ = parent_->option_defaults_;
  34 +
  35 + // INHERITABLE
  36 + failure_message_ = parent_->failure_message_;
  37 + allow_extras_ = parent_->allow_extras_;
  38 + allow_config_extras_ = parent_->allow_config_extras_;
  39 + prefix_command_ = parent_->prefix_command_;
  40 + immediate_callback_ = parent_->immediate_callback_;
  41 + ignore_case_ = parent_->ignore_case_;
  42 + ignore_underscore_ = parent_->ignore_underscore_;
  43 + fallthrough_ = parent_->fallthrough_;
  44 + validate_positionals_ = parent_->validate_positionals_;
  45 + validate_optional_arguments_ = parent_->validate_optional_arguments_;
  46 + configurable_ = parent_->configurable_;
  47 + allow_windows_style_options_ = parent_->allow_windows_style_options_;
  48 + group_ = parent_->group_;
  49 + footer_ = parent_->footer_;
  50 + formatter_ = parent_->formatter_;
  51 + config_formatter_ = parent_->config_formatter_;
  52 + require_subcommand_max_ = parent_->require_subcommand_max_;
  53 + }
  54 +}
  55 +
  56 +CLI11_INLINE App *App::name(std::string app_name) {
  57 +
  58 + if(parent_ != nullptr) {
  59 + auto oname = name_;
  60 + name_ = app_name;
  61 + const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
  62 + if(!res.empty()) {
  63 + name_ = oname;
  64 + throw(OptionAlreadyAdded(app_name + " conflicts with existing subcommand names"));
  65 + }
  66 + } else {
  67 + name_ = app_name;
  68 + }
  69 + has_automatic_name_ = false;
  70 + return this;
  71 +}
  72 +
  73 +CLI11_INLINE App *App::alias(std::string app_name) {
  74 + if(app_name.empty() || !detail::valid_alias_name_string(app_name)) {
  75 + throw IncorrectConstruction("Aliases may not be empty or contain newlines or null characters");
  76 + }
  77 + if(parent_ != nullptr) {
  78 + aliases_.push_back(app_name);
  79 + const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
  80 + if(!res.empty()) {
  81 + aliases_.pop_back();
  82 + throw(OptionAlreadyAdded("alias already matches an existing subcommand: " + app_name));
  83 + }
  84 + } else {
  85 + aliases_.push_back(app_name);
  86 + }
  87 +
  88 + return this;
  89 +}
  90 +
  91 +CLI11_INLINE App *App::immediate_callback(bool immediate) {
  92 + immediate_callback_ = immediate;
  93 + if(immediate_callback_) {
  94 + if(final_callback_ && !(parse_complete_callback_)) {
  95 + std::swap(final_callback_, parse_complete_callback_);
  96 + }
  97 + } else if(!(final_callback_) && parse_complete_callback_) {
  98 + std::swap(final_callback_, parse_complete_callback_);
  99 + }
  100 + return this;
  101 +}
  102 +
  103 +CLI11_INLINE App *App::ignore_case(bool value) {
  104 + if(value && !ignore_case_) {
  105 + ignore_case_ = true;
  106 + auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
  107 + const auto &match = _compare_subcommand_names(*this, *p);
  108 + if(!match.empty()) {
  109 + ignore_case_ = false; // we are throwing so need to be exception invariant
  110 + throw OptionAlreadyAdded("ignore case would cause subcommand name conflicts: " + match);
  111 + }
  112 + }
  113 + ignore_case_ = value;
  114 + return this;
  115 +}
  116 +
  117 +CLI11_INLINE App *App::ignore_underscore(bool value) {
  118 + if(value && !ignore_underscore_) {
  119 + ignore_underscore_ = true;
  120 + auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
  121 + const auto &match = _compare_subcommand_names(*this, *p);
  122 + if(!match.empty()) {
  123 + ignore_underscore_ = false;
  124 + throw OptionAlreadyAdded("ignore underscore would cause subcommand name conflicts: " + match);
  125 + }
  126 + }
  127 + ignore_underscore_ = value;
  128 + return this;
  129 +}
  130 +
  131 +CLI11_INLINE Option *App::add_option(std::string option_name,
  132 + callback_t option_callback,
  133 + std::string option_description,
  134 + bool defaulted,
  135 + std::function<std::string()> func) {
  136 + Option myopt{option_name, option_description, option_callback, this};
  137 +
  138 + if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) { return *v == myopt; }) ==
  139 + std::end(options_)) {
  140 + options_.emplace_back();
  141 + Option_p &option = options_.back();
  142 + option.reset(new Option(option_name, option_description, option_callback, this));
  143 +
  144 + // Set the default string capture function
  145 + option->default_function(func);
  146 +
  147 + // For compatibility with CLI11 1.7 and before, capture the default string here
  148 + if(defaulted)
  149 + option->capture_default_str();
  150 +
  151 + // Transfer defaults to the new option
  152 + option_defaults_.copy_to(option.get());
  153 +
  154 + // Don't bother to capture if we already did
  155 + if(!defaulted && option->get_always_capture_default())
  156 + option->capture_default_str();
  157 +
  158 + return option.get();
  159 + }
  160 + // we know something matches now find what it is so we can produce more error information
  161 + for(auto &opt : options_) {
  162 + const auto &matchname = opt->matching_name(myopt);
  163 + if(!matchname.empty()) {
  164 + throw(OptionAlreadyAdded("added option matched existing option name: " + matchname));
  165 + }
  166 + }
  167 + // this line should not be reached the above loop should trigger the throw
  168 + throw(OptionAlreadyAdded("added option matched existing option name")); // LCOV_EXCL_LINE
  169 +}
  170 +
  171 +CLI11_INLINE Option *App::set_help_flag(std::string flag_name, const std::string &help_description) {
  172 + // take flag_description by const reference otherwise add_flag tries to assign to help_description
  173 + if(help_ptr_ != nullptr) {
  174 + remove_option(help_ptr_);
  175 + help_ptr_ = nullptr;
  176 + }
  177 +
  178 + // Empty name will simply remove the help flag
  179 + if(!flag_name.empty()) {
  180 + help_ptr_ = add_flag(flag_name, help_description);
  181 + help_ptr_->configurable(false);
  182 + }
  183 +
  184 + return help_ptr_;
  185 +}
  186 +
  187 +CLI11_INLINE Option *App::set_help_all_flag(std::string help_name, const std::string &help_description) {
  188 + // take flag_description by const reference otherwise add_flag tries to assign to flag_description
  189 + if(help_all_ptr_ != nullptr) {
  190 + remove_option(help_all_ptr_);
  191 + help_all_ptr_ = nullptr;
  192 + }
  193 +
  194 + // Empty name will simply remove the help all flag
  195 + if(!help_name.empty()) {
  196 + help_all_ptr_ = add_flag(help_name, help_description);
  197 + help_all_ptr_->configurable(false);
  198 + }
  199 +
  200 + return help_all_ptr_;
  201 +}
  202 +
  203 +CLI11_INLINE Option *
  204 +App::set_version_flag(std::string flag_name, const std::string &versionString, const std::string &version_help) {
  205 + // take flag_description by const reference otherwise add_flag tries to assign to version_description
  206 + if(version_ptr_ != nullptr) {
  207 + remove_option(version_ptr_);
  208 + version_ptr_ = nullptr;
  209 + }
  210 +
  211 + // Empty name will simply remove the version flag
  212 + if(!flag_name.empty()) {
  213 + version_ptr_ = add_flag_callback(
  214 + flag_name, [versionString]() { throw(CLI::CallForVersion(versionString, 0)); }, version_help);
  215 + version_ptr_->configurable(false);
  216 + }
  217 +
  218 + return version_ptr_;
  219 +}
  220 +
  221 +CLI11_INLINE Option *
  222 +App::set_version_flag(std::string flag_name, std::function<std::string()> vfunc, const std::string &version_help) {
  223 + if(version_ptr_ != nullptr) {
  224 + remove_option(version_ptr_);
  225 + version_ptr_ = nullptr;
  226 + }
  227 +
  228 + // Empty name will simply remove the version flag
  229 + if(!flag_name.empty()) {
  230 + version_ptr_ = add_flag_callback(
  231 + flag_name, [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); }, version_help);
  232 + version_ptr_->configurable(false);
  233 + }
  234 +
  235 + return version_ptr_;
  236 +}
  237 +
  238 +CLI11_INLINE Option *App::_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description) {
  239 + Option *opt = nullptr;
  240 + if(detail::has_default_flag_values(flag_name)) {
  241 + // check for default values and if it has them
  242 + auto flag_defaults = detail::get_default_flag_values(flag_name);
  243 + detail::remove_default_flag_values(flag_name);
  244 + opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
  245 + for(const auto &fname : flag_defaults)
  246 + opt->fnames_.push_back(fname.first);
  247 + opt->default_flag_values_ = std::move(flag_defaults);
  248 + } else {
  249 + opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
  250 + }
  251 + // flags cannot have positional values
  252 + if(opt->get_positional()) {
  253 + auto pos_name = opt->get_name(true);
  254 + remove_option(opt);
  255 + throw IncorrectConstruction::PositionalFlag(pos_name);
  256 + }
  257 + opt->multi_option_policy(MultiOptionPolicy::TakeLast);
  258 + opt->expected(0);
  259 + opt->required(false);
  260 + return opt;
  261 +}
  262 +
  263 +CLI11_INLINE Option *App::add_flag_callback(std::string flag_name,
  264 + std::function<void(void)> function, ///< A function to call, void(void)
  265 + std::string flag_description) {
  266 +
  267 + CLI::callback_t fun = [function](const CLI::results_t &res) {
  268 + bool trigger{false};
  269 + auto result = CLI::detail::lexical_cast(res[0], trigger);
  270 + if(result && trigger) {
  271 + function();
  272 + }
  273 + return result;
  274 + };
  275 + return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
  276 +}
  277 +
  278 +CLI11_INLINE Option *
  279 +App::add_flag_function(std::string flag_name,
  280 + std::function<void(std::int64_t)> function, ///< A function to call, void(int)
  281 + std::string flag_description) {
  282 +
  283 + CLI::callback_t fun = [function](const CLI::results_t &res) {
  284 + std::int64_t flag_count{0};
  285 + CLI::detail::lexical_cast(res[0], flag_count);
  286 + function(flag_count);
  287 + return true;
  288 + };
  289 + return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
  290 + ->multi_option_policy(MultiOptionPolicy::Sum);
  291 +}
  292 +
  293 +CLI11_INLINE Option *App::set_config(std::string option_name,
  294 + std::string default_filename,
  295 + const std::string &help_message,
  296 + bool config_required) {
  297 +
  298 + // Remove existing config if present
  299 + if(config_ptr_ != nullptr) {
  300 + remove_option(config_ptr_);
  301 + config_ptr_ = nullptr; // need to remove the config_ptr completely
  302 + }
  303 +
  304 + // Only add config if option passed
  305 + if(!option_name.empty()) {
  306 + config_ptr_ = add_option(option_name, help_message);
  307 + if(config_required) {
  308 + config_ptr_->required();
  309 + }
  310 + if(!default_filename.empty()) {
  311 + config_ptr_->default_str(std::move(default_filename));
  312 + }
  313 + config_ptr_->configurable(false);
  314 + }
  315 +
  316 + return config_ptr_;
  317 +}
  318 +
  319 +CLI11_INLINE bool App::remove_option(Option *opt) {
  320 + // Make sure no links exist
  321 + for(Option_p &op : options_) {
  322 + op->remove_needs(opt);
  323 + op->remove_excludes(opt);
  324 + }
  325 +
  326 + if(help_ptr_ == opt)
  327 + help_ptr_ = nullptr;
  328 + if(help_all_ptr_ == opt)
  329 + help_all_ptr_ = nullptr;
  330 +
  331 + auto iterator =
  332 + std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
  333 + if(iterator != std::end(options_)) {
  334 + options_.erase(iterator);
  335 + return true;
  336 + }
  337 + return false;
  338 +}
  339 +
  340 +CLI11_INLINE App *App::add_subcommand(std::string subcommand_name, std::string subcommand_description) {
  341 + if(!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {
  342 + if(!detail::valid_first_char(subcommand_name[0])) {
  343 + throw IncorrectConstruction("Subcommand name starts with invalid character, '!' and '-' are not allowed");
  344 + }
  345 + for(auto c : subcommand_name) {
  346 + if(!detail::valid_later_char(c)) {
  347 + throw IncorrectConstruction(std::string("Subcommand name contains invalid character ('") + c +
  348 + "'), all characters are allowed except"
  349 + "'=',':','{','}', and ' '");
  350 + }
  351 + }
  352 + }
  353 + CLI::App_p subcom = std::shared_ptr<App>(new App(std::move(subcommand_description), subcommand_name, this));
  354 + return add_subcommand(std::move(subcom));
  355 +}
  356 +
  357 +CLI11_INLINE App *App::add_subcommand(CLI::App_p subcom) {
  358 + if(!subcom)
  359 + throw IncorrectConstruction("passed App is not valid");
  360 + auto *ckapp = (name_.empty() && parent_ != nullptr) ? _get_fallthrough_parent() : this;
  361 + const auto &mstrg = _compare_subcommand_names(*subcom, *ckapp);
  362 + if(!mstrg.empty()) {
  363 + throw(OptionAlreadyAdded("subcommand name or alias matches existing subcommand: " + mstrg));
  364 + }
  365 + subcom->parent_ = this;
  366 + subcommands_.push_back(std::move(subcom));
  367 + return subcommands_.back().get();
  368 +}
  369 +
  370 +CLI11_INLINE bool App::remove_subcommand(App *subcom) {
  371 + // Make sure no links exist
  372 + for(App_p &sub : subcommands_) {
  373 + sub->remove_excludes(subcom);
  374 + sub->remove_needs(subcom);
  375 + }
  376 +
  377 + auto iterator = std::find_if(
  378 + std::begin(subcommands_), std::end(subcommands_), [subcom](const App_p &v) { return v.get() == subcom; });
  379 + if(iterator != std::end(subcommands_)) {
  380 + subcommands_.erase(iterator);
  381 + return true;
  382 + }
  383 + return false;
  384 +}
  385 +
  386 +CLI11_INLINE App *App::get_subcommand(const App *subcom) const {
  387 + if(subcom == nullptr)
  388 + throw OptionNotFound("nullptr passed");
  389 + for(const App_p &subcomptr : subcommands_)
  390 + if(subcomptr.get() == subcom)
  391 + return subcomptr.get();
  392 + throw OptionNotFound(subcom->get_name());
  393 +}
  394 +
  395 +CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(std::string subcom) const {
  396 + auto *subc = _find_subcommand(subcom, false, false);
  397 + if(subc == nullptr)
  398 + throw OptionNotFound(subcom);
  399 + return subc;
  400 +}
  401 +
  402 +CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(int index) const {
  403 + if(index >= 0) {
  404 + auto uindex = static_cast<unsigned>(index);
  405 + if(uindex < subcommands_.size())
  406 + return subcommands_[uindex].get();
  407 + }
  408 + throw OptionNotFound(std::to_string(index));
  409 +}
  410 +
  411 +CLI11_INLINE CLI::App_p App::get_subcommand_ptr(App *subcom) const {
  412 + if(subcom == nullptr)
  413 + throw OptionNotFound("nullptr passed");
  414 + for(const App_p &subcomptr : subcommands_)
  415 + if(subcomptr.get() == subcom)
  416 + return subcomptr;
  417 + throw OptionNotFound(subcom->get_name());
  418 +}
  419 +
  420 +CLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(std::string subcom) const {
  421 + for(const App_p &subcomptr : subcommands_)
  422 + if(subcomptr->check_name(subcom))
  423 + return subcomptr;
  424 + throw OptionNotFound(subcom);
  425 +}
  426 +
  427 +CLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(int index) const {
  428 + if(index >= 0) {
  429 + auto uindex = static_cast<unsigned>(index);
  430 + if(uindex < subcommands_.size())
  431 + return subcommands_[uindex];
  432 + }
  433 + throw OptionNotFound(std::to_string(index));
  434 +}
  435 +
  436 +CLI11_NODISCARD CLI11_INLINE std::size_t App::count_all() const {
  437 + std::size_t cnt{0};
  438 + for(const auto &opt : options_) {
  439 + cnt += opt->count();
  440 + }
  441 + for(const auto &sub : subcommands_) {
  442 + cnt += sub->count_all();
  443 + }
  444 + if(!get_name().empty()) { // for named subcommands add the number of times the subcommand was called
  445 + cnt += parsed_;
  446 + }
  447 + return cnt;
  448 +}
  449 +
  450 +CLI11_INLINE void App::clear() {
  451 +
  452 + parsed_ = 0;
  453 + pre_parse_called_ = false;
  454 +
  455 + missing_.clear();
  456 + parsed_subcommands_.clear();
  457 + for(const Option_p &opt : options_) {
  458 + opt->clear();
  459 + }
  460 + for(const App_p &subc : subcommands_) {
  461 + subc->clear();
  462 + }
  463 +}
  464 +
  465 +CLI11_INLINE void App::parse(int argc, const char *const *argv) {
  466 + // If the name is not set, read from command line
  467 + if(name_.empty() || has_automatic_name_) {
  468 + has_automatic_name_ = true;
  469 + name_ = argv[0];
  470 + }
  471 +
  472 + std::vector<std::string> args;
  473 + args.reserve(static_cast<std::size_t>(argc) - 1U);
  474 + for(auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i)
  475 + args.emplace_back(argv[i]);
  476 + parse(std::move(args));
  477 +}
  478 +
  479 +CLI11_INLINE void App::parse(std::string commandline, bool program_name_included) {
  480 +
  481 + if(program_name_included) {
  482 + auto nstr = detail::split_program_name(commandline);
  483 + if((name_.empty()) || (has_automatic_name_)) {
  484 + has_automatic_name_ = true;
  485 + name_ = nstr.first;
  486 + }
  487 + commandline = std::move(nstr.second);
  488 + } else {
  489 + detail::trim(commandline);
  490 + }
  491 + // the next section of code is to deal with quoted arguments after an '=' or ':' for windows like operations
  492 + if(!commandline.empty()) {
  493 + commandline = detail::find_and_modify(commandline, "=", detail::escape_detect);
  494 + if(allow_windows_style_options_)
  495 + commandline = detail::find_and_modify(commandline, ":", detail::escape_detect);
  496 + }
  497 +
  498 + auto args = detail::split_up(std::move(commandline));
  499 + // remove all empty strings
  500 + args.erase(std::remove(args.begin(), args.end(), std::string{}), args.end());
  501 + std::reverse(args.begin(), args.end());
  502 +
  503 + parse(std::move(args));
  504 +}
  505 +
  506 +CLI11_INLINE void App::parse(std::vector<std::string> &args) {
  507 + // Clear if parsed
  508 + if(parsed_ > 0)
  509 + clear();
  510 +
  511 + // parsed_ is incremented in commands/subcommands,
  512 + // but placed here to make sure this is cleared when
  513 + // running parse after an error is thrown, even by _validate or _configure.
  514 + parsed_ = 1;
  515 + _validate();
  516 + _configure();
  517 + // set the parent as nullptr as this object should be the top now
  518 + parent_ = nullptr;
  519 + parsed_ = 0;
  520 +
  521 + _parse(args);
  522 + run_callback();
  523 +}
  524 +
  525 +CLI11_INLINE void App::parse(std::vector<std::string> &&args) {
  526 + // Clear if parsed
  527 + if(parsed_ > 0)
  528 + clear();
  529 +
  530 + // parsed_ is incremented in commands/subcommands,
  531 + // but placed here to make sure this is cleared when
  532 + // running parse after an error is thrown, even by _validate or _configure.
  533 + parsed_ = 1;
  534 + _validate();
  535 + _configure();
  536 + // set the parent as nullptr as this object should be the top now
  537 + parent_ = nullptr;
  538 + parsed_ = 0;
  539 +
  540 + _parse(std::move(args));
  541 + run_callback();
  542 +}
  543 +
  544 +CLI11_INLINE void App::parse_from_stream(std::istream &input) {
  545 + if(parsed_ == 0) {
  546 + _validate();
  547 + _configure();
  548 + // set the parent as nullptr as this object should be the top now
  549 + }
  550 +
  551 + _parse_stream(input);
  552 + run_callback();
  553 +}
  554 +
  555 +CLI11_INLINE int App::exit(const Error &e, std::ostream &out, std::ostream &err) const {
  556 +
  557 + /// Avoid printing anything if this is a CLI::RuntimeError
  558 + if(e.get_name() == "RuntimeError")
  559 + return e.get_exit_code();
  560 +
  561 + if(e.get_name() == "CallForHelp") {
  562 + out << help();
  563 + return e.get_exit_code();
  564 + }
  565 +
  566 + if(e.get_name() == "CallForAllHelp") {
  567 + out << help("", AppFormatMode::All);
  568 + return e.get_exit_code();
  569 + }
  570 +
  571 + if(e.get_name() == "CallForVersion") {
  572 + out << e.what() << std::endl;
  573 + return e.get_exit_code();
  574 + }
  575 +
  576 + if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {
  577 + if(failure_message_)
  578 + err << failure_message_(this, e) << std::flush;
  579 + }
  580 +
  581 + return e.get_exit_code();
  582 +}
  583 +
  584 +CLI11_INLINE std::vector<const App *> App::get_subcommands(const std::function<bool(const App *)> &filter) const {
  585 + std::vector<const App *> subcomms(subcommands_.size());
  586 + std::transform(
  587 + std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { return v.get(); });
  588 +
  589 + if(filter) {
  590 + subcomms.erase(std::remove_if(std::begin(subcomms),
  591 + std::end(subcomms),
  592 + [&filter](const App *app) { return !filter(app); }),
  593 + std::end(subcomms));
  594 + }
  595 +
  596 + return subcomms;
  597 +}
  598 +
  599 +CLI11_INLINE std::vector<App *> App::get_subcommands(const std::function<bool(App *)> &filter) {
  600 + std::vector<App *> subcomms(subcommands_.size());
  601 + std::transform(
  602 + std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { return v.get(); });
  603 +
  604 + if(filter) {
  605 + subcomms.erase(
  606 + std::remove_if(std::begin(subcomms), std::end(subcomms), [&filter](App *app) { return !filter(app); }),
  607 + std::end(subcomms));
  608 + }
  609 +
  610 + return subcomms;
  611 +}
  612 +
  613 +CLI11_INLINE bool App::remove_excludes(Option *opt) {
  614 + auto iterator = std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);
  615 + if(iterator == std::end(exclude_options_)) {
  616 + return false;
  617 + }
  618 + exclude_options_.erase(iterator);
  619 + return true;
  620 +}
  621 +
  622 +CLI11_INLINE bool App::remove_excludes(App *app) {
  623 + auto iterator = std::find(std::begin(exclude_subcommands_), std::end(exclude_subcommands_), app);
  624 + if(iterator == std::end(exclude_subcommands_)) {
  625 + return false;
  626 + }
  627 + auto *other_app = *iterator;
  628 + exclude_subcommands_.erase(iterator);
  629 + other_app->remove_excludes(this);
  630 + return true;
  631 +}
  632 +
  633 +CLI11_INLINE bool App::remove_needs(Option *opt) {
  634 + auto iterator = std::find(std::begin(need_options_), std::end(need_options_), opt);
  635 + if(iterator == std::end(need_options_)) {
  636 + return false;
  637 + }
  638 + need_options_.erase(iterator);
  639 + return true;
  640 +}
  641 +
  642 +CLI11_INLINE bool App::remove_needs(App *app) {
  643 + auto iterator = std::find(std::begin(need_subcommands_), std::end(need_subcommands_), app);
  644 + if(iterator == std::end(need_subcommands_)) {
  645 + return false;
  646 + }
  647 + need_subcommands_.erase(iterator);
  648 + return true;
  649 +}
  650 +
  651 +CLI11_NODISCARD CLI11_INLINE std::string App::help(std::string prev, AppFormatMode mode) const {
  652 + if(prev.empty())
  653 + prev = get_name();
  654 + else
  655 + prev += " " + get_name();
  656 +
  657 + // Delegate to subcommand if needed
  658 + auto selected_subcommands = get_subcommands();
  659 + if(!selected_subcommands.empty()) {
  660 + return selected_subcommands.at(0)->help(prev, mode);
  661 + }
  662 + return formatter_->make_help(this, prev, mode);
  663 +}
  664 +
  665 +CLI11_NODISCARD CLI11_INLINE std::string App::version() const {
  666 + std::string val;
  667 + if(version_ptr_ != nullptr) {
  668 + auto rv = version_ptr_->results();
  669 + version_ptr_->clear();
  670 + version_ptr_->add_result("true");
  671 + try {
  672 + version_ptr_->run_callback();
  673 + } catch(const CLI::CallForVersion &cfv) {
  674 + val = cfv.what();
  675 + }
  676 + version_ptr_->clear();
  677 + version_ptr_->add_result(rv);
  678 + }
  679 + return val;
  680 +}
  681 +
  682 +CLI11_INLINE std::vector<const Option *> App::get_options(const std::function<bool(const Option *)> filter) const {
  683 + std::vector<const Option *> options(options_.size());
  684 + std::transform(
  685 + std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) { return val.get(); });
  686 +
  687 + if(filter) {
  688 + options.erase(std::remove_if(std::begin(options),
  689 + std::end(options),
  690 + [&filter](const Option *opt) { return !filter(opt); }),
  691 + std::end(options));
  692 + }
  693 +
  694 + return options;
  695 +}
  696 +
  697 +CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Option *)> filter) {
  698 + std::vector<Option *> options(options_.size());
  699 + std::transform(
  700 + std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) { return val.get(); });
  701 +
  702 + if(filter) {
  703 + options.erase(
  704 + std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }),
  705 + std::end(options));
  706 + }
  707 +
  708 + return options;
  709 +}
  710 +
  711 +CLI11_INLINE Option *App::get_option_no_throw(std::string option_name) noexcept {
  712 + for(Option_p &opt : options_) {
  713 + if(opt->check_name(option_name)) {
  714 + return opt.get();
  715 + }
  716 + }
  717 + for(auto &subc : subcommands_) {
  718 + // also check down into nameless subcommands
  719 + if(subc->get_name().empty()) {
  720 + auto *opt = subc->get_option_no_throw(option_name);
  721 + if(opt != nullptr) {
  722 + return opt;
  723 + }
  724 + }
  725 + }
  726 + return nullptr;
  727 +}
  728 +
  729 +CLI11_NODISCARD CLI11_INLINE const Option *App::get_option_no_throw(std::string option_name) const noexcept {
  730 + for(const Option_p &opt : options_) {
  731 + if(opt->check_name(option_name)) {
  732 + return opt.get();
  733 + }
  734 + }
  735 + for(const auto &subc : subcommands_) {
  736 + // also check down into nameless subcommands
  737 + if(subc->get_name().empty()) {
  738 + auto *opt = subc->get_option_no_throw(option_name);
  739 + if(opt != nullptr) {
  740 + return opt;
  741 + }
  742 + }
  743 + }
  744 + return nullptr;
  745 +}
  746 +
  747 +CLI11_NODISCARD CLI11_INLINE std::string App::get_display_name(bool with_aliases) const {
  748 + if(name_.empty()) {
  749 + return std::string("[Option Group: ") + get_group() + "]";
  750 + }
  751 + if(aliases_.empty() || !with_aliases) {
  752 + return name_;
  753 + }
  754 + std::string dispname = name_;
  755 + for(const auto &lalias : aliases_) {
  756 + dispname.push_back(',');
  757 + dispname.push_back(' ');
  758 + dispname.append(lalias);
  759 + }
  760 + return dispname;
  761 +}
  762 +
  763 +CLI11_NODISCARD CLI11_INLINE bool App::check_name(std::string name_to_check) const {
  764 + std::string local_name = name_;
  765 + if(ignore_underscore_) {
  766 + local_name = detail::remove_underscore(name_);
  767 + name_to_check = detail::remove_underscore(name_to_check);
  768 + }
  769 + if(ignore_case_) {
  770 + local_name = detail::to_lower(name_);
  771 + name_to_check = detail::to_lower(name_to_check);
  772 + }
  773 +
  774 + if(local_name == name_to_check) {
  775 + return true;
  776 + }
  777 + for(auto les : aliases_) { // NOLINT(performance-for-range-copy)
  778 + if(ignore_underscore_) {
  779 + les = detail::remove_underscore(les);
  780 + }
  781 + if(ignore_case_) {
  782 + les = detail::to_lower(les);
  783 + }
  784 + if(les == name_to_check) {
  785 + return true;
  786 + }
  787 + }
  788 + return false;
  789 +}
  790 +
  791 +CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::get_groups() const {
  792 + std::vector<std::string> groups;
  793 +
  794 + for(const Option_p &opt : options_) {
  795 + // Add group if it is not already in there
  796 + if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) {
  797 + groups.push_back(opt->get_group());
  798 + }
  799 + }
  800 +
  801 + return groups;
  802 +}
  803 +
  804 +CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining(bool recurse) const {
  805 + std::vector<std::string> miss_list;
  806 + for(const std::pair<detail::Classifier, std::string> &miss : missing_) {
  807 + miss_list.push_back(std::get<1>(miss));
  808 + }
  809 + // Get from a subcommand that may allow extras
  810 + if(recurse) {
  811 + if(!allow_extras_) {
  812 + for(const auto &sub : subcommands_) {
  813 + if(sub->name_.empty() && !sub->missing_.empty()) {
  814 + for(const std::pair<detail::Classifier, std::string> &miss : sub->missing_) {
  815 + miss_list.push_back(std::get<1>(miss));
  816 + }
  817 + }
  818 + }
  819 + }
  820 + // Recurse into subcommands
  821 +
  822 + for(const App *sub : parsed_subcommands_) {
  823 + std::vector<std::string> output = sub->remaining(recurse);
  824 + std::copy(std::begin(output), std::end(output), std::back_inserter(miss_list));
  825 + }
  826 + }
  827 + return miss_list;
  828 +}
  829 +
  830 +CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining_for_passthrough(bool recurse) const {
  831 + std::vector<std::string> miss_list = remaining(recurse);
  832 + std::reverse(std::begin(miss_list), std::end(miss_list));
  833 + return miss_list;
  834 +}
  835 +
  836 +CLI11_NODISCARD CLI11_INLINE std::size_t App::remaining_size(bool recurse) const {
  837 + auto remaining_options = static_cast<std::size_t>(std::count_if(
  838 + std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifier, std::string> &val) {
  839 + return val.first != detail::Classifier::POSITIONAL_MARK;
  840 + }));
  841 +
  842 + if(recurse) {
  843 + for(const App_p &sub : subcommands_) {
  844 + remaining_options += sub->remaining_size(recurse);
  845 + }
  846 + }
  847 + return remaining_options;
  848 +}
  849 +
  850 +CLI11_INLINE void App::_validate() const {
  851 + // count the number of positional only args
  852 + auto pcount = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
  853 + return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional();
  854 + });
  855 + if(pcount > 1) {
  856 + auto pcount_req = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
  857 + return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional() &&
  858 + opt->get_required();
  859 + });
  860 + if(pcount - pcount_req > 1) {
  861 + throw InvalidError(name_);
  862 + }
  863 + }
  864 +
  865 + std::size_t nameless_subs{0};
  866 + for(const App_p &app : subcommands_) {
  867 + app->_validate();
  868 + if(app->get_name().empty())
  869 + ++nameless_subs;
  870 + }
  871 +
  872 + if(require_option_min_ > 0) {
  873 + if(require_option_max_ > 0) {
  874 + if(require_option_max_ < require_option_min_) {
  875 + throw(InvalidError("Required min options greater than required max options", ExitCodes::InvalidError));
  876 + }
  877 + }
  878 + if(require_option_min_ > (options_.size() + nameless_subs)) {
  879 + throw(
  880 + InvalidError("Required min options greater than number of available options", ExitCodes::InvalidError));
  881 + }
  882 + }
  883 +}
  884 +
  885 +CLI11_INLINE void App::_configure() {
  886 + if(default_startup == startup_mode::enabled) {
  887 + disabled_ = false;
  888 + } else if(default_startup == startup_mode::disabled) {
  889 + disabled_ = true;
  890 + }
  891 + for(const App_p &app : subcommands_) {
  892 + if(app->has_automatic_name_) {
  893 + app->name_.clear();
  894 + }
  895 + if(app->name_.empty()) {
  896 + app->fallthrough_ = false; // make sure fallthrough_ is false to prevent infinite loop
  897 + app->prefix_command_ = false;
  898 + }
  899 + // make sure the parent is set to be this object in preparation for parse
  900 + app->parent_ = this;
  901 + app->_configure();
  902 + }
  903 +}
  904 +
  905 +CLI11_INLINE void App::run_callback(bool final_mode, bool suppress_final_callback) {
  906 + pre_callback();
  907 + // in the main app if immediate_callback_ is set it runs the main callback before the used subcommands
  908 + if(!final_mode && parse_complete_callback_) {
  909 + parse_complete_callback_();
  910 + }
  911 + // run the callbacks for the received subcommands
  912 + for(App *subc : get_subcommands()) {
  913 + if(subc->parent_ == this) {
  914 + subc->run_callback(true, suppress_final_callback);
  915 + }
  916 + }
  917 + // now run callbacks for option_groups
  918 + for(auto &subc : subcommands_) {
  919 + if(subc->name_.empty() && subc->count_all() > 0) {
  920 + subc->run_callback(true, suppress_final_callback);
  921 + }
  922 + }
  923 +
  924 + // finally run the main callback
  925 + if(final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) {
  926 + if(!name_.empty() || count_all() > 0 || parent_ == nullptr) {
  927 + final_callback_();
  928 + }
  929 + }
  930 +}
  931 +
  932 +CLI11_NODISCARD CLI11_INLINE bool App::_valid_subcommand(const std::string &current, bool ignore_used) const {
  933 + // Don't match if max has been reached - but still check parents
  934 + if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
  935 + return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
  936 + }
  937 + auto *com = _find_subcommand(current, true, ignore_used);
  938 + if(com != nullptr) {
  939 + return true;
  940 + }
  941 + // Check parent if exists, else return false
  942 + return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
  943 +}
  944 +
  945 +CLI11_NODISCARD CLI11_INLINE detail::Classifier App::_recognize(const std::string &current,
  946 + bool ignore_used_subcommands) const {
  947 + std::string dummy1, dummy2;
  948 +
  949 + if(current == "--")
  950 + return detail::Classifier::POSITIONAL_MARK;
  951 + if(_valid_subcommand(current, ignore_used_subcommands))
  952 + return detail::Classifier::SUBCOMMAND;
  953 + if(detail::split_long(current, dummy1, dummy2))
  954 + return detail::Classifier::LONG;
  955 + if(detail::split_short(current, dummy1, dummy2)) {
  956 + if(dummy1[0] >= '0' && dummy1[0] <= '9') {
  957 + if(get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {
  958 + return detail::Classifier::NONE;
  959 + }
  960 + }
  961 + return detail::Classifier::SHORT;
  962 + }
  963 + if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
  964 + return detail::Classifier::WINDOWS_STYLE;
  965 + if((current == "++") && !name_.empty() && parent_ != nullptr)
  966 + return detail::Classifier::SUBCOMMAND_TERMINATOR;
  967 + return detail::Classifier::NONE;
  968 +}
  969 +
  970 +CLI11_INLINE void App::_process_config_file() {
  971 + if(config_ptr_ != nullptr) {
  972 + bool config_required = config_ptr_->get_required();
  973 + auto file_given = config_ptr_->count() > 0;
  974 + auto config_files = config_ptr_->as<std::vector<std::string>>();
  975 + if(config_files.empty() || config_files.front().empty()) {
  976 + if(config_required) {
  977 + throw FileError::Missing("no specified config file");
  978 + }
  979 + return;
  980 + }
  981 + for(auto rit = config_files.rbegin(); rit != config_files.rend(); ++rit) {
  982 + const auto &config_file = *rit;
  983 + auto path_result = detail::check_path(config_file.c_str());
  984 + if(path_result == detail::path_type::file) {
  985 + try {
  986 + std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
  987 + _parse_config(values);
  988 + if(!file_given) {
  989 + config_ptr_->add_result(config_file);
  990 + }
  991 + } catch(const FileError &) {
  992 + if(config_required || file_given)
  993 + throw;
  994 + }
  995 + } else if(config_required || file_given) {
  996 + throw FileError::Missing(config_file);
  997 + }
  998 + }
  999 + }
  1000 +}
  1001 +
  1002 +CLI11_INLINE void App::_process_env() {
  1003 + for(const Option_p &opt : options_) {
  1004 + if(opt->count() == 0 && !opt->envname_.empty()) {
  1005 + char *buffer = nullptr;
  1006 + std::string ename_string;
  1007 +
  1008 +#ifdef _MSC_VER
  1009 + // Windows version
  1010 + std::size_t sz = 0;
  1011 + if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) {
  1012 + ename_string = std::string(buffer);
  1013 + free(buffer);
  1014 + }
  1015 +#else
  1016 + // This also works on Windows, but gives a warning
  1017 + buffer = std::getenv(opt->envname_.c_str());
  1018 + if(buffer != nullptr)
  1019 + ename_string = std::string(buffer);
  1020 +#endif
  1021 +
  1022 + if(!ename_string.empty()) {
  1023 + opt->add_result(ename_string);
  1024 + }
  1025 + }
  1026 + }
  1027 +
  1028 + for(App_p &sub : subcommands_) {
  1029 + if(sub->get_name().empty() || !sub->parse_complete_callback_)
  1030 + sub->_process_env();
  1031 + }
  1032 +}
  1033 +
  1034 +CLI11_INLINE void App::_process_callbacks() {
  1035 +
  1036 + for(App_p &sub : subcommands_) {
  1037 + // process the priority option_groups first
  1038 + if(sub->get_name().empty() && sub->parse_complete_callback_) {
  1039 + if(sub->count_all() > 0) {
  1040 + sub->_process_callbacks();
  1041 + sub->run_callback();
  1042 + }
  1043 + }
  1044 + }
  1045 +
  1046 + for(const Option_p &opt : options_) {
  1047 + if((*opt) && !opt->get_callback_run()) {
  1048 + opt->run_callback();
  1049 + }
  1050 + }
  1051 + for(App_p &sub : subcommands_) {
  1052 + if(!sub->parse_complete_callback_) {
  1053 + sub->_process_callbacks();
  1054 + }
  1055 + }
  1056 +}
  1057 +
  1058 +CLI11_INLINE void App::_process_help_flags(bool trigger_help, bool trigger_all_help) const {
  1059 + const Option *help_ptr = get_help_ptr();
  1060 + const Option *help_all_ptr = get_help_all_ptr();
  1061 +
  1062 + if(help_ptr != nullptr && help_ptr->count() > 0)
  1063 + trigger_help = true;
  1064 + if(help_all_ptr != nullptr && help_all_ptr->count() > 0)
  1065 + trigger_all_help = true;
  1066 +
  1067 + // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones.
  1068 + if(!parsed_subcommands_.empty()) {
  1069 + for(const App *sub : parsed_subcommands_)
  1070 + sub->_process_help_flags(trigger_help, trigger_all_help);
  1071 +
  1072 + // Only the final subcommand should call for help. All help wins over help.
  1073 + } else if(trigger_all_help) {
  1074 + throw CallForAllHelp();
  1075 + } else if(trigger_help) {
  1076 + throw CallForHelp();
  1077 + }
  1078 +}
  1079 +
  1080 +CLI11_INLINE void App::_process_requirements() {
  1081 + // check excludes
  1082 + bool excluded{false};
  1083 + std::string excluder;
  1084 + for(const auto &opt : exclude_options_) {
  1085 + if(opt->count() > 0) {
  1086 + excluded = true;
  1087 + excluder = opt->get_name();
  1088 + }
  1089 + }
  1090 + for(const auto &subc : exclude_subcommands_) {
  1091 + if(subc->count_all() > 0) {
  1092 + excluded = true;
  1093 + excluder = subc->get_display_name();
  1094 + }
  1095 + }
  1096 + if(excluded) {
  1097 + if(count_all() > 0) {
  1098 + throw ExcludesError(get_display_name(), excluder);
  1099 + }
  1100 + // if we are excluded but didn't receive anything, just return
  1101 + return;
  1102 + }
  1103 +
  1104 + // check excludes
  1105 + bool missing_needed{false};
  1106 + std::string missing_need;
  1107 + for(const auto &opt : need_options_) {
  1108 + if(opt->count() == 0) {
  1109 + missing_needed = true;
  1110 + missing_need = opt->get_name();
  1111 + }
  1112 + }
  1113 + for(const auto &subc : need_subcommands_) {
  1114 + if(subc->count_all() == 0) {
  1115 + missing_needed = true;
  1116 + missing_need = subc->get_display_name();
  1117 + }
  1118 + }
  1119 + if(missing_needed) {
  1120 + if(count_all() > 0) {
  1121 + throw RequiresError(get_display_name(), missing_need);
  1122 + }
  1123 + // if we missing something but didn't have any options, just return
  1124 + return;
  1125 + }
  1126 +
  1127 + std::size_t used_options = 0;
  1128 + for(const Option_p &opt : options_) {
  1129 +
  1130 + if(opt->count() != 0) {
  1131 + ++used_options;
  1132 + }
  1133 + // Required but empty
  1134 + if(opt->get_required() && opt->count() == 0) {
  1135 + throw RequiredError(opt->get_name());
  1136 + }
  1137 + // Requires
  1138 + for(const Option *opt_req : opt->needs_)
  1139 + if(opt->count() > 0 && opt_req->count() == 0)
  1140 + throw RequiresError(opt->get_name(), opt_req->get_name());
  1141 + // Excludes
  1142 + for(const Option *opt_ex : opt->excludes_)
  1143 + if(opt->count() > 0 && opt_ex->count() != 0)
  1144 + throw ExcludesError(opt->get_name(), opt_ex->get_name());
  1145 + }
  1146 + // check for the required number of subcommands
  1147 + if(require_subcommand_min_ > 0) {
  1148 + auto selected_subcommands = get_subcommands();
  1149 + if(require_subcommand_min_ > selected_subcommands.size())
  1150 + throw RequiredError::Subcommand(require_subcommand_min_);
  1151 + }
  1152 +
  1153 + // Max error cannot occur, the extra subcommand will parse as an ExtrasError or a remaining item.
  1154 +
  1155 + // run this loop to check how many unnamed subcommands were actually used since they are considered options
  1156 + // from the perspective of an App
  1157 + for(App_p &sub : subcommands_) {
  1158 + if(sub->disabled_)
  1159 + continue;
  1160 + if(sub->name_.empty() && sub->count_all() > 0) {
  1161 + ++used_options;
  1162 + }
  1163 + }
  1164 +
  1165 + if(require_option_min_ > used_options || (require_option_max_ > 0 && require_option_max_ < used_options)) {
  1166 + auto option_list = detail::join(options_, [this](const Option_p &ptr) {
  1167 + if(ptr.get() == help_ptr_ || ptr.get() == help_all_ptr_) {
  1168 + return std::string{};
  1169 + }
  1170 + return ptr->get_name(false, true);
  1171 + });
  1172 +
  1173 + auto subc_list = get_subcommands([](App *app) { return ((app->get_name().empty()) && (!app->disabled_)); });
  1174 + if(!subc_list.empty()) {
  1175 + option_list += "," + detail::join(subc_list, [](const App *app) { return app->get_display_name(); });
  1176 + }
  1177 + throw RequiredError::Option(require_option_min_, require_option_max_, used_options, option_list);
  1178 + }
  1179 +
  1180 + // now process the requirements for subcommands if needed
  1181 + for(App_p &sub : subcommands_) {
  1182 + if(sub->disabled_)
  1183 + continue;
  1184 + if(sub->name_.empty() && sub->required_ == false) {
  1185 + if(sub->count_all() == 0) {
  1186 + if(require_option_min_ > 0 && require_option_min_ <= used_options) {
  1187 + continue;
  1188 + // if we have met the requirement and there is nothing in this option group skip checking
  1189 + // requirements
  1190 + }
  1191 + if(require_option_max_ > 0 && used_options >= require_option_min_) {
  1192 + continue;
  1193 + // if we have met the requirement and there is nothing in this option group skip checking
  1194 + // requirements
  1195 + }
  1196 + }
  1197 + }
  1198 + if(sub->count() > 0 || sub->name_.empty()) {
  1199 + sub->_process_requirements();
  1200 + }
  1201 +
  1202 + if(sub->required_ && sub->count_all() == 0) {
  1203 + throw(CLI::RequiredError(sub->get_display_name()));
  1204 + }
  1205 + }
  1206 +}
  1207 +
  1208 +CLI11_INLINE void App::_process() {
  1209 + try {
  1210 + // the config file might generate a FileError but that should not be processed until later in the process
  1211 + // to allow for help, version and other errors to generate first.
  1212 + _process_config_file();
  1213 +
  1214 + // process env shouldn't throw but no reason to process it if config generated an error
  1215 + _process_env();
  1216 + } catch(const CLI::FileError &) {
  1217 + // callbacks and help_flags can generate exceptions which should take priority
  1218 + // over the config file error if one exists.
  1219 + _process_callbacks();
  1220 + _process_help_flags();
  1221 + throw;
  1222 + }
  1223 +
  1224 + _process_callbacks();
  1225 + _process_help_flags();
  1226 +
  1227 + _process_requirements();
  1228 +}
  1229 +
  1230 +CLI11_INLINE void App::_process_extras() {
  1231 + if(!(allow_extras_ || prefix_command_)) {
  1232 + std::size_t num_left_over = remaining_size();
  1233 + if(num_left_over > 0) {
  1234 + throw ExtrasError(name_, remaining(false));
  1235 + }
  1236 + }
  1237 +
  1238 + for(App_p &sub : subcommands_) {
  1239 + if(sub->count() > 0)
  1240 + sub->_process_extras();
  1241 + }
  1242 +}
  1243 +
  1244 +CLI11_INLINE void App::_process_extras(std::vector<std::string> &args) {
  1245 + if(!(allow_extras_ || prefix_command_)) {
  1246 + std::size_t num_left_over = remaining_size();
  1247 + if(num_left_over > 0) {
  1248 + args = remaining(false);
  1249 + throw ExtrasError(name_, args);
  1250 + }
  1251 + }
  1252 +
  1253 + for(App_p &sub : subcommands_) {
  1254 + if(sub->count() > 0)
  1255 + sub->_process_extras(args);
  1256 + }
  1257 +}
  1258 +
  1259 +CLI11_INLINE void App::increment_parsed() {
  1260 + ++parsed_;
  1261 + for(App_p &sub : subcommands_) {
  1262 + if(sub->get_name().empty())
  1263 + sub->increment_parsed();
  1264 + }
  1265 +}
  1266 +
  1267 +CLI11_INLINE void App::_parse(std::vector<std::string> &args) {
  1268 + increment_parsed();
  1269 + _trigger_pre_parse(args.size());
  1270 + bool positional_only = false;
  1271 +
  1272 + while(!args.empty()) {
  1273 + if(!_parse_single(args, positional_only)) {
  1274 + break;
  1275 + }
  1276 + }
  1277 +
  1278 + if(parent_ == nullptr) {
  1279 + _process();
  1280 +
  1281 + // Throw error if any items are left over (depending on settings)
  1282 + _process_extras(args);
  1283 +
  1284 + // Convert missing (pairs) to extras (string only) ready for processing in another app
  1285 + args = remaining_for_passthrough(false);
  1286 + } else if(parse_complete_callback_) {
  1287 + _process_env();
  1288 + _process_callbacks();
  1289 + _process_help_flags();
  1290 + _process_requirements();
  1291 + run_callback(false, true);
  1292 + }
  1293 +}
  1294 +
  1295 +CLI11_INLINE void App::_parse(std::vector<std::string> &&args) {
  1296 + // this can only be called by the top level in which case parent == nullptr by definition
  1297 + // operation is simplified
  1298 + increment_parsed();
  1299 + _trigger_pre_parse(args.size());
  1300 + bool positional_only = false;
  1301 +
  1302 + while(!args.empty()) {
  1303 + _parse_single(args, positional_only);
  1304 + }
  1305 + _process();
  1306 +
  1307 + // Throw error if any items are left over (depending on settings)
  1308 + _process_extras();
  1309 +}
  1310 +
  1311 +CLI11_INLINE void App::_parse_stream(std::istream &input) {
  1312 + auto values = config_formatter_->from_config(input);
  1313 + _parse_config(values);
  1314 + increment_parsed();
  1315 + _trigger_pre_parse(values.size());
  1316 + _process();
  1317 +
  1318 + // Throw error if any items are left over (depending on settings)
  1319 + _process_extras();
  1320 +}
  1321 +
  1322 +CLI11_INLINE void App::_parse_config(const std::vector<ConfigItem> &args) {
  1323 + for(const ConfigItem &item : args) {
  1324 + if(!_parse_single_config(item) && allow_config_extras_ == config_extras_mode::error)
  1325 + throw ConfigError::Extras(item.fullname());
  1326 + }
  1327 +}
  1328 +
  1329 +CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t level) {
  1330 + if(level < item.parents.size()) {
  1331 + try {
  1332 + auto *subcom = get_subcommand(item.parents.at(level));
  1333 + auto result = subcom->_parse_single_config(item, level + 1);
  1334 +
  1335 + return result;
  1336 + } catch(const OptionNotFound &) {
  1337 + return false;
  1338 + }
  1339 + }
  1340 + // check for section open
  1341 + if(item.name == "++") {
  1342 + if(configurable_) {
  1343 + increment_parsed();
  1344 + _trigger_pre_parse(2);
  1345 + if(parent_ != nullptr) {
  1346 + parent_->parsed_subcommands_.push_back(this);
  1347 + }
  1348 + }
  1349 + return true;
  1350 + }
  1351 + // check for section close
  1352 + if(item.name == "--") {
  1353 + if(configurable_ && parse_complete_callback_) {
  1354 + _process_callbacks();
  1355 + _process_requirements();
  1356 + run_callback();
  1357 + }
  1358 + return true;
  1359 + }
  1360 + Option *op = get_option_no_throw("--" + item.name);
  1361 + if(op == nullptr) {
  1362 + if(item.name.size() == 1) {
  1363 + op = get_option_no_throw("-" + item.name);
  1364 + }
  1365 + }
  1366 + if(op == nullptr) {
  1367 + op = get_option_no_throw(item.name);
  1368 + }
  1369 + if(op == nullptr) {
  1370 + // If the option was not present
  1371 + if(get_allow_config_extras() == config_extras_mode::capture)
  1372 + // Should we worry about classifying the extras properly?
  1373 + missing_.emplace_back(detail::Classifier::NONE, item.fullname());
  1374 + return false;
  1375 + }
  1376 +
  1377 + if(!op->get_configurable()) {
  1378 + if(get_allow_config_extras() == config_extras_mode::ignore_all) {
  1379 + return false;
  1380 + }
  1381 + throw ConfigError::NotConfigurable(item.fullname());
  1382 + }
  1383 +
  1384 + if(op->empty()) {
  1385 +
  1386 + if(op->get_expected_min() == 0) {
  1387 + // Flag parsing
  1388 + auto res = config_formatter_->to_flag(item);
  1389 + res = op->get_flag_value(item.name, res);
  1390 +
  1391 + op->add_result(res);
  1392 +
  1393 + } else {
  1394 + op->add_result(item.inputs);
  1395 + op->run_callback();
  1396 + }
  1397 + }
  1398 +
  1399 + return true;
  1400 +}
  1401 +
  1402 +CLI11_INLINE bool App::_parse_single(std::vector<std::string> &args, bool &positional_only) {
  1403 + bool retval = true;
  1404 + detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back());
  1405 + switch(classifier) {
  1406 + case detail::Classifier::POSITIONAL_MARK:
  1407 + args.pop_back();
  1408 + positional_only = true;
  1409 + if((!_has_remaining_positionals()) && (parent_ != nullptr)) {
  1410 + retval = false;
  1411 + } else {
  1412 + _move_to_missing(classifier, "--");
  1413 + }
  1414 + break;
  1415 + case detail::Classifier::SUBCOMMAND_TERMINATOR:
  1416 + // treat this like a positional mark if in the parent app
  1417 + args.pop_back();
  1418 + retval = false;
  1419 + break;
  1420 + case detail::Classifier::SUBCOMMAND:
  1421 + retval = _parse_subcommand(args);
  1422 + break;
  1423 + case detail::Classifier::LONG:
  1424 + case detail::Classifier::SHORT:
  1425 + case detail::Classifier::WINDOWS_STYLE:
  1426 + // If already parsed a subcommand, don't accept options_
  1427 + _parse_arg(args, classifier);
  1428 + break;
  1429 + case detail::Classifier::NONE:
  1430 + // Probably a positional or something for a parent (sub)command
  1431 + retval = _parse_positional(args, false);
  1432 + if(retval && positionals_at_end_) {
  1433 + positional_only = true;
  1434 + }
  1435 + break;
  1436 + // LCOV_EXCL_START
  1437 + default:
  1438 + throw HorribleError("unrecognized classifier (you should not see this!)");
  1439 + // LCOV_EXCL_STOP
  1440 + }
  1441 + return retval;
  1442 +}
  1443 +
  1444 +CLI11_NODISCARD CLI11_INLINE std::size_t App::_count_remaining_positionals(bool required_only) const {
  1445 + std::size_t retval = 0;
  1446 + for(const Option_p &opt : options_) {
  1447 + if(opt->get_positional() && (!required_only || opt->get_required())) {
  1448 + if(opt->get_items_expected_min() > 0 && static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
  1449 + retval += static_cast<std::size_t>(opt->get_items_expected_min()) - opt->count();
  1450 + }
  1451 + }
  1452 + }
  1453 + return retval;
  1454 +}
  1455 +
  1456 +CLI11_NODISCARD CLI11_INLINE bool App::_has_remaining_positionals() const {
  1457 + for(const Option_p &opt : options_) {
  1458 + if(opt->get_positional() && ((static_cast<int>(opt->count()) < opt->get_items_expected_min()))) {
  1459 + return true;
  1460 + }
  1461 + }
  1462 +
  1463 + return false;
  1464 +}
  1465 +
  1466 +CLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args, bool haltOnSubcommand) {
  1467 +
  1468 + const std::string &positional = args.back();
  1469 +
  1470 + if(positionals_at_end_) {
  1471 + // deal with the case of required arguments at the end which should take precedence over other arguments
  1472 + auto arg_rem = args.size();
  1473 + auto remreq = _count_remaining_positionals(true);
  1474 + if(arg_rem <= remreq) {
  1475 + for(const Option_p &opt : options_) {
  1476 + if(opt->get_positional() && opt->required_) {
  1477 + if(static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
  1478 + if(validate_positionals_) {
  1479 + std::string pos = positional;
  1480 + pos = opt->_validate(pos, 0);
  1481 + if(!pos.empty()) {
  1482 + continue;
  1483 + }
  1484 + }
  1485 +
  1486 + parse_order_.push_back(opt.get());
  1487 + /// if we require a separator add it here
  1488 + if(opt->get_inject_separator()) {
  1489 + if(!opt->results().empty() && !opt->results().back().empty()) {
  1490 + opt->add_result(std::string{});
  1491 + }
  1492 + }
  1493 + if(opt->get_trigger_on_parse() &&
  1494 + opt->current_option_state_ == Option::option_state::callback_run) {
  1495 + opt->clear();
  1496 + }
  1497 + opt->add_result(positional);
  1498 + if(opt->get_trigger_on_parse()) {
  1499 + opt->run_callback();
  1500 + }
  1501 + args.pop_back();
  1502 + return true;
  1503 + }
  1504 + }
  1505 + }
  1506 + }
  1507 + }
  1508 + for(const Option_p &opt : options_) {
  1509 + // Eat options, one by one, until done
  1510 + if(opt->get_positional() &&
  1511 + (static_cast<int>(opt->count()) < opt->get_items_expected_min() || opt->get_allow_extra_args())) {
  1512 + if(validate_positionals_) {
  1513 + std::string pos = positional;
  1514 + pos = opt->_validate(pos, 0);
  1515 + if(!pos.empty()) {
  1516 + continue;
  1517 + }
  1518 + }
  1519 + if(opt->get_inject_separator()) {
  1520 + if(!opt->results().empty() && !opt->results().back().empty()) {
  1521 + opt->add_result(std::string{});
  1522 + }
  1523 + }
  1524 + if(opt->get_trigger_on_parse() && opt->current_option_state_ == Option::option_state::callback_run) {
  1525 + opt->clear();
  1526 + }
  1527 + opt->add_result(positional);
  1528 + if(opt->get_trigger_on_parse()) {
  1529 + opt->run_callback();
  1530 + }
  1531 + parse_order_.push_back(opt.get());
  1532 + args.pop_back();
  1533 + return true;
  1534 + }
  1535 + }
  1536 +
  1537 + for(auto &subc : subcommands_) {
  1538 + if((subc->name_.empty()) && (!subc->disabled_)) {
  1539 + if(subc->_parse_positional(args, false)) {
  1540 + if(!subc->pre_parse_called_) {
  1541 + subc->_trigger_pre_parse(args.size());
  1542 + }
  1543 + return true;
  1544 + }
  1545 + }
  1546 + }
  1547 + // let the parent deal with it if possible
  1548 + if(parent_ != nullptr && fallthrough_)
  1549 + return _get_fallthrough_parent()->_parse_positional(args, static_cast<bool>(parse_complete_callback_));
  1550 +
  1551 + /// Try to find a local subcommand that is repeated
  1552 + auto *com = _find_subcommand(args.back(), true, false);
  1553 + if(com != nullptr && (require_subcommand_max_ == 0 || require_subcommand_max_ > parsed_subcommands_.size())) {
  1554 + if(haltOnSubcommand) {
  1555 + return false;
  1556 + }
  1557 + args.pop_back();
  1558 + com->_parse(args);
  1559 + return true;
  1560 + }
  1561 + /// now try one last gasp at subcommands that have been executed before, go to root app and try to find a
  1562 + /// subcommand in a broader way, if one exists let the parent deal with it
  1563 + auto *parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
  1564 + com = parent_app->_find_subcommand(args.back(), true, false);
  1565 + if(com != nullptr && (com->parent_->require_subcommand_max_ == 0 ||
  1566 + com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size())) {
  1567 + return false;
  1568 + }
  1569 +
  1570 + if(positionals_at_end_) {
  1571 + throw CLI::ExtrasError(name_, args);
  1572 + }
  1573 + /// If this is an option group don't deal with it
  1574 + if(parent_ != nullptr && name_.empty()) {
  1575 + return false;
  1576 + }
  1577 + /// We are out of other options this goes to missing
  1578 + _move_to_missing(detail::Classifier::NONE, positional);
  1579 + args.pop_back();
  1580 + if(prefix_command_) {
  1581 + while(!args.empty()) {
  1582 + _move_to_missing(detail::Classifier::NONE, args.back());
  1583 + args.pop_back();
  1584 + }
  1585 + }
  1586 +
  1587 + return true;
  1588 +}
  1589 +
  1590 +CLI11_NODISCARD CLI11_INLINE App *
  1591 +App::_find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept {
  1592 + for(const App_p &com : subcommands_) {
  1593 + if(com->disabled_ && ignore_disabled)
  1594 + continue;
  1595 + if(com->get_name().empty()) {
  1596 + auto *subc = com->_find_subcommand(subc_name, ignore_disabled, ignore_used);
  1597 + if(subc != nullptr) {
  1598 + return subc;
  1599 + }
  1600 + }
  1601 + if(com->check_name(subc_name)) {
  1602 + if((!*com) || !ignore_used)
  1603 + return com.get();
  1604 + }
  1605 + }
  1606 + return nullptr;
  1607 +}
  1608 +
  1609 +CLI11_INLINE bool App::_parse_subcommand(std::vector<std::string> &args) {
  1610 + if(_count_remaining_positionals(/* required */ true) > 0) {
  1611 + _parse_positional(args, false);
  1612 + return true;
  1613 + }
  1614 + auto *com = _find_subcommand(args.back(), true, true);
  1615 + if(com != nullptr) {
  1616 + args.pop_back();
  1617 + if(!com->silent_) {
  1618 + parsed_subcommands_.push_back(com);
  1619 + }
  1620 + com->_parse(args);
  1621 + auto *parent_app = com->parent_;
  1622 + while(parent_app != this) {
  1623 + parent_app->_trigger_pre_parse(args.size());
  1624 + if(!com->silent_) {
  1625 + parent_app->parsed_subcommands_.push_back(com);
  1626 + }
  1627 + parent_app = parent_app->parent_;
  1628 + }
  1629 + return true;
  1630 + }
  1631 +
  1632 + if(parent_ == nullptr)
  1633 + throw HorribleError("Subcommand " + args.back() + " missing");
  1634 + return false;
  1635 +}
  1636 +
  1637 +CLI11_INLINE bool App::_parse_arg(std::vector<std::string> &args, detail::Classifier current_type) {
  1638 +
  1639 + std::string current = args.back();
  1640 +
  1641 + std::string arg_name;
  1642 + std::string value;
  1643 + std::string rest;
  1644 +
  1645 + switch(current_type) {
  1646 + case detail::Classifier::LONG:
  1647 + if(!detail::split_long(current, arg_name, value))
  1648 + throw HorribleError("Long parsed but missing (you should not see this):" + args.back());
  1649 + break;
  1650 + case detail::Classifier::SHORT:
  1651 + if(!detail::split_short(current, arg_name, rest))
  1652 + throw HorribleError("Short parsed but missing! You should not see this");
  1653 + break;
  1654 + case detail::Classifier::WINDOWS_STYLE:
  1655 + if(!detail::split_windows_style(current, arg_name, value))
  1656 + throw HorribleError("windows option parsed but missing! You should not see this");
  1657 + break;
  1658 + case detail::Classifier::SUBCOMMAND:
  1659 + case detail::Classifier::SUBCOMMAND_TERMINATOR:
  1660 + case detail::Classifier::POSITIONAL_MARK:
  1661 + case detail::Classifier::NONE:
  1662 + default:
  1663 + throw HorribleError("parsing got called with invalid option! You should not see this");
  1664 + }
  1665 +
  1666 + auto op_ptr = std::find_if(std::begin(options_), std::end(options_), [arg_name, current_type](const Option_p &opt) {
  1667 + if(current_type == detail::Classifier::LONG)
  1668 + return opt->check_lname(arg_name);
  1669 + if(current_type == detail::Classifier::SHORT)
  1670 + return opt->check_sname(arg_name);
  1671 + // this will only get called for detail::Classifier::WINDOWS_STYLE
  1672 + return opt->check_lname(arg_name) || opt->check_sname(arg_name);
  1673 + });
  1674 +
  1675 + // Option not found
  1676 + if(op_ptr == std::end(options_)) {
  1677 + for(auto &subc : subcommands_) {
  1678 + if(subc->name_.empty() && !subc->disabled_) {
  1679 + if(subc->_parse_arg(args, current_type)) {
  1680 + if(!subc->pre_parse_called_) {
  1681 + subc->_trigger_pre_parse(args.size());
  1682 + }
  1683 + return true;
  1684 + }
  1685 + }
  1686 + }
  1687 +
  1688 + // don't capture missing if this is a nameless subcommand and nameless subcommands can't fallthrough
  1689 + if(parent_ != nullptr && name_.empty()) {
  1690 + return false;
  1691 + }
  1692 +
  1693 + // If a subcommand, try the main command
  1694 + if(parent_ != nullptr && fallthrough_)
  1695 + return _get_fallthrough_parent()->_parse_arg(args, current_type);
  1696 +
  1697 + // Otherwise, add to missing
  1698 + args.pop_back();
  1699 + _move_to_missing(current_type, current);
  1700 + return true;
  1701 + }
  1702 +
  1703 + args.pop_back();
  1704 +
  1705 + // Get a reference to the pointer to make syntax bearable
  1706 + Option_p &op = *op_ptr;
  1707 + /// if we require a separator add it here
  1708 + if(op->get_inject_separator()) {
  1709 + if(!op->results().empty() && !op->results().back().empty()) {
  1710 + op->add_result(std::string{});
  1711 + }
  1712 + }
  1713 + if(op->get_trigger_on_parse() && op->current_option_state_ == Option::option_state::callback_run) {
  1714 + op->clear();
  1715 + }
  1716 + int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min());
  1717 + int max_num = op->get_items_expected_max();
  1718 + // check container like options to limit the argument size to a single type if the allow_extra_flags argument is
  1719 + // set. 16 is somewhat arbitrary (needs to be at least 4)
  1720 + if(max_num >= detail::expected_max_vector_size / 16 && !op->get_allow_extra_args()) {
  1721 + auto tmax = op->get_type_size_max();
  1722 + max_num = detail::checked_multiply(tmax, op->get_expected_min()) ? tmax : detail::expected_max_vector_size;
  1723 + }
  1724 + // Make sure we always eat the minimum for unlimited vectors
  1725 + int collected = 0; // total number of arguments collected
  1726 + int result_count = 0; // local variable for number of results in a single arg string
  1727 + // deal with purely flag like things
  1728 + if(max_num == 0) {
  1729 + auto res = op->get_flag_value(arg_name, value);
  1730 + op->add_result(res);
  1731 + parse_order_.push_back(op.get());
  1732 + } else if(!value.empty()) { // --this=value
  1733 + op->add_result(value, result_count);
  1734 + parse_order_.push_back(op.get());
  1735 + collected += result_count;
  1736 + // -Trest
  1737 + } else if(!rest.empty()) {
  1738 + op->add_result(rest, result_count);
  1739 + parse_order_.push_back(op.get());
  1740 + rest = "";
  1741 + collected += result_count;
  1742 + }
  1743 +
  1744 + // gather the minimum number of arguments
  1745 + while(min_num > collected && !args.empty()) {
  1746 + std::string current_ = args.back();
  1747 + args.pop_back();
  1748 + op->add_result(current_, result_count);
  1749 + parse_order_.push_back(op.get());
  1750 + collected += result_count;
  1751 + }
  1752 +
  1753 + if(min_num > collected) { // if we have run out of arguments and the minimum was not met
  1754 + throw ArgumentMismatch::TypedAtLeast(op->get_name(), min_num, op->get_type_name());
  1755 + }
  1756 +
  1757 + // now check for optional arguments
  1758 + if(max_num > collected || op->get_allow_extra_args()) { // we allow optional arguments
  1759 + auto remreqpos = _count_remaining_positionals(true);
  1760 + // we have met the minimum now optionally check up to the maximum
  1761 + while((collected < max_num || op->get_allow_extra_args()) && !args.empty() &&
  1762 + _recognize(args.back(), false) == detail::Classifier::NONE) {
  1763 + // If any required positionals remain, don't keep eating
  1764 + if(remreqpos >= args.size()) {
  1765 + break;
  1766 + }
  1767 + if(validate_optional_arguments_) {
  1768 + std::string arg = args.back();
  1769 + arg = op->_validate(arg, 0);
  1770 + if(!arg.empty()) {
  1771 + break;
  1772 + }
  1773 + }
  1774 + op->add_result(args.back(), result_count);
  1775 + parse_order_.push_back(op.get());
  1776 + args.pop_back();
  1777 + collected += result_count;
  1778 + }
  1779 +
  1780 + // Allow -- to end an unlimited list and "eat" it
  1781 + if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)
  1782 + args.pop_back();
  1783 + // optional flag that didn't receive anything now get the default value
  1784 + if(min_num == 0 && max_num > 0 && collected == 0) {
  1785 + auto res = op->get_flag_value(arg_name, std::string{});
  1786 + op->add_result(res);
  1787 + parse_order_.push_back(op.get());
  1788 + }
  1789 + }
  1790 + // if we only partially completed a type then add an empty string if allowed for later processing
  1791 + if(min_num > 0 && (collected % op->get_type_size_max()) != 0) {
  1792 + if(op->get_type_size_max() != op->get_type_size_min()) {
  1793 + op->add_result(std::string{});
  1794 + } else {
  1795 + throw ArgumentMismatch::PartialType(op->get_name(), op->get_type_size_min(), op->get_type_name());
  1796 + }
  1797 + }
  1798 + if(op->get_trigger_on_parse()) {
  1799 + op->run_callback();
  1800 + }
  1801 + if(!rest.empty()) {
  1802 + rest = "-" + rest;
  1803 + args.push_back(rest);
  1804 + }
  1805 + return true;
  1806 +}
  1807 +
  1808 +CLI11_INLINE void App::_trigger_pre_parse(std::size_t remaining_args) {
  1809 + if(!pre_parse_called_) {
  1810 + pre_parse_called_ = true;
  1811 + if(pre_parse_callback_) {
  1812 + pre_parse_callback_(remaining_args);
  1813 + }
  1814 + } else if(immediate_callback_) {
  1815 + if(!name_.empty()) {
  1816 + auto pcnt = parsed_;
  1817 + auto extras = std::move(missing_);
  1818 + clear();
  1819 + parsed_ = pcnt;
  1820 + pre_parse_called_ = true;
  1821 + missing_ = std::move(extras);
  1822 + }
  1823 + }
  1824 +}
  1825 +
  1826 +CLI11_INLINE App *App::_get_fallthrough_parent() {
  1827 + if(parent_ == nullptr) {
  1828 + throw(HorribleError("No Valid parent"));
  1829 + }
  1830 + auto *fallthrough_parent = parent_;
  1831 + while((fallthrough_parent->parent_ != nullptr) && (fallthrough_parent->get_name().empty())) {
  1832 + fallthrough_parent = fallthrough_parent->parent_;
  1833 + }
  1834 + return fallthrough_parent;
  1835 +}
  1836 +
  1837 +CLI11_NODISCARD CLI11_INLINE const std::string &App::_compare_subcommand_names(const App &subcom,
  1838 + const App &base) const {
  1839 + static const std::string estring;
  1840 + if(subcom.disabled_) {
  1841 + return estring;
  1842 + }
  1843 + for(const auto &subc : base.subcommands_) {
  1844 + if(subc.get() != &subcom) {
  1845 + if(subc->disabled_) {
  1846 + continue;
  1847 + }
  1848 + if(!subcom.get_name().empty()) {
  1849 + if(subc->check_name(subcom.get_name())) {
  1850 + return subcom.get_name();
  1851 + }
  1852 + }
  1853 + if(!subc->get_name().empty()) {
  1854 + if(subcom.check_name(subc->get_name())) {
  1855 + return subc->get_name();
  1856 + }
  1857 + }
  1858 + for(const auto &les : subcom.aliases_) {
  1859 + if(subc->check_name(les)) {
  1860 + return les;
  1861 + }
  1862 + }
  1863 + // this loop is needed in case of ignore_underscore or ignore_case on one but not the other
  1864 + for(const auto &les : subc->aliases_) {
  1865 + if(subcom.check_name(les)) {
  1866 + return les;
  1867 + }
  1868 + }
  1869 + // if the subcommand is an option group we need to check deeper
  1870 + if(subc->get_name().empty()) {
  1871 + const auto &cmpres = _compare_subcommand_names(subcom, *subc);
  1872 + if(!cmpres.empty()) {
  1873 + return cmpres;
  1874 + }
  1875 + }
  1876 + // if the test subcommand is an option group we need to check deeper
  1877 + if(subcom.get_name().empty()) {
  1878 + const auto &cmpres = _compare_subcommand_names(*subc, subcom);
  1879 + if(!cmpres.empty()) {
  1880 + return cmpres;
  1881 + }
  1882 + }
  1883 + }
  1884 + }
  1885 + return estring;
  1886 +}
  1887 +
  1888 +CLI11_INLINE void App::_move_to_missing(detail::Classifier val_type, const std::string &val) {
  1889 + if(allow_extras_ || subcommands_.empty()) {
  1890 + missing_.emplace_back(val_type, val);
  1891 + return;
  1892 + }
  1893 + // allow extra arguments to be places in an option group if it is allowed there
  1894 + for(auto &subc : subcommands_) {
  1895 + if(subc->name_.empty() && subc->allow_extras_) {
  1896 + subc->missing_.emplace_back(val_type, val);
  1897 + return;
  1898 + }
  1899 + }
  1900 + // if we haven't found any place to put them yet put them in missing
  1901 + missing_.emplace_back(val_type, val);
  1902 +}
  1903 +
  1904 +CLI11_INLINE void App::_move_option(Option *opt, App *app) {
  1905 + if(opt == nullptr) {
  1906 + throw OptionNotFound("the option is NULL");
  1907 + }
  1908 + // verify that the give app is actually a subcommand
  1909 + bool found = false;
  1910 + for(auto &subc : subcommands_) {
  1911 + if(app == subc.get()) {
  1912 + found = true;
  1913 + }
  1914 + }
  1915 + if(!found) {
  1916 + throw OptionNotFound("The Given app is not a subcommand");
  1917 + }
  1918 +
  1919 + if((help_ptr_ == opt) || (help_all_ptr_ == opt))
  1920 + throw OptionAlreadyAdded("cannot move help options");
  1921 +
  1922 + if(config_ptr_ == opt)
  1923 + throw OptionAlreadyAdded("cannot move config file options");
  1924 +
  1925 + auto iterator =
  1926 + std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
  1927 + if(iterator != std::end(options_)) {
  1928 + const auto &opt_p = *iterator;
  1929 + if(std::find_if(std::begin(app->options_), std::end(app->options_), [&opt_p](const Option_p &v) {
  1930 + return (*v == *opt_p);
  1931 + }) == std::end(app->options_)) {
  1932 + // only erase after the insertion was successful
  1933 + app->options_.push_back(std::move(*iterator));
  1934 + options_.erase(iterator);
  1935 + } else {
  1936 + throw OptionAlreadyAdded("option was not located: " + opt->get_name());
  1937 + }
  1938 + } else {
  1939 + throw OptionNotFound("could not locate the given Option");
  1940 + }
  1941 +}
  1942 +
  1943 +CLI11_INLINE void TriggerOn(App *trigger_app, App *app_to_enable) {
  1944 + app_to_enable->enabled_by_default(false);
  1945 + app_to_enable->disabled_by_default();
  1946 + trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(false); });
  1947 +}
  1948 +
  1949 +CLI11_INLINE void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
  1950 + for(auto &app : apps_to_enable) {
  1951 + app->enabled_by_default(false);
  1952 + app->disabled_by_default();
  1953 + }
  1954 +
  1955 + trigger_app->preparse_callback([apps_to_enable](std::size_t) {
  1956 + for(const auto &app : apps_to_enable) {
  1957 + app->disabled(false);
  1958 + }
  1959 + });
  1960 +}
  1961 +
  1962 +CLI11_INLINE void TriggerOff(App *trigger_app, App *app_to_enable) {
  1963 + app_to_enable->disabled_by_default(false);
  1964 + app_to_enable->enabled_by_default();
  1965 + trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(); });
  1966 +}
  1967 +
  1968 +CLI11_INLINE void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable) {
  1969 + for(auto &app : apps_to_enable) {
  1970 + app->disabled_by_default(false);
  1971 + app->enabled_by_default();
  1972 + }
  1973 +
  1974 + trigger_app->preparse_callback([apps_to_enable](std::size_t) {
  1975 + for(const auto &app : apps_to_enable) {
  1976 + app->disabled();
  1977 + }
  1978 + });
  1979 +}
  1980 +
  1981 +CLI11_INLINE void deprecate_option(Option *opt, const std::string &replacement) {
  1982 + Validator deprecate_warning{[opt, replacement](std::string &) {
  1983 + std::cout << opt->get_name() << " is deprecated please use '" << replacement
  1984 + << "' instead\n";
  1985 + return std::string();
  1986 + },
  1987 + "DEPRECATED"};
  1988 + deprecate_warning.application_index(0);
  1989 + opt->check(deprecate_warning);
  1990 + if(!replacement.empty()) {
  1991 + opt->description(opt->get_description() + " DEPRECATED: please use '" + replacement + "' instead");
  1992 + }
  1993 +}
  1994 +
  1995 +CLI11_INLINE void retire_option(App *app, Option *opt) {
  1996 + App temp;
  1997 + auto *option_copy = temp.add_option(opt->get_name(false, true))
  1998 + ->type_size(opt->get_type_size_min(), opt->get_type_size_max())
  1999 + ->expected(opt->get_expected_min(), opt->get_expected_max())
  2000 + ->allow_extra_args(opt->get_allow_extra_args());
  2001 +
  2002 + app->remove_option(opt);
  2003 + auto *opt2 = app->add_option(option_copy->get_name(false, true), "option has been retired and has no effect")
  2004 + ->type_name("RETIRED")
  2005 + ->default_str("RETIRED")
  2006 + ->type_size(option_copy->get_type_size_min(), option_copy->get_type_size_max())
  2007 + ->expected(option_copy->get_expected_min(), option_copy->get_expected_max())
  2008 + ->allow_extra_args(option_copy->get_allow_extra_args());
  2009 +
  2010 + Validator retired_warning{[opt2](std::string &) {
  2011 + std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
  2012 + return std::string();
  2013 + },
  2014 + ""};
  2015 + retired_warning.application_index(0);
  2016 + opt2->check(retired_warning);
  2017 +}
  2018 +
  2019 +CLI11_INLINE void retire_option(App &app, Option *opt) { retire_option(&app, opt); }
  2020 +
  2021 +CLI11_INLINE void retire_option(App *app, const std::string &option_name) {
  2022 +
  2023 + auto *opt = app->get_option_no_throw(option_name);
  2024 + if(opt != nullptr) {
  2025 + retire_option(app, opt);
  2026 + return;
  2027 + }
  2028 + auto *opt2 = app->add_option(option_name, "option has been retired and has no effect")
  2029 + ->type_name("RETIRED")
  2030 + ->expected(0, 1)
  2031 + ->default_str("RETIRED");
  2032 + Validator retired_warning{[opt2](std::string &) {
  2033 + std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
  2034 + return std::string();
  2035 + },
  2036 + ""};
  2037 + retired_warning.application_index(0);
  2038 + opt2->check(retired_warning);
  2039 +}
  2040 +
  2041 +CLI11_INLINE void retire_option(App &app, const std::string &option_name) { retire_option(&app, option_name); }
  2042 +
  2043 +namespace FailureMessage {
  2044 +
  2045 +CLI11_INLINE std::string simple(const App *app, const Error &e) {
  2046 + std::string header = std::string(e.what()) + "\n";
  2047 + std::vector<std::string> names;
  2048 +
  2049 + // Collect names
  2050 + if(app->get_help_ptr() != nullptr)
  2051 + names.push_back(app->get_help_ptr()->get_name());
  2052 +
  2053 + if(app->get_help_all_ptr() != nullptr)
  2054 + names.push_back(app->get_help_all_ptr()->get_name());
  2055 +
  2056 + // If any names found, suggest those
  2057 + if(!names.empty())
  2058 + header += "Run with " + detail::join(names, " or ") + " for more information.\n";
  2059 +
  2060 + return header;
  2061 +}
  2062 +
  2063 +CLI11_INLINE std::string help(const App *app, const Error &e) {
  2064 + std::string header = std::string("ERROR: ") + e.get_name() + ": " + e.what() + "\n";
  2065 + header += app->help();
  2066 + return header;
  2067 +}
  2068 +
  2069 +} // namespace FailureMessage
  2070 +
  2071 +// [CLI11:app_inl_hpp:end]
  2072 +} // namespace CLI
... ...
src/App.cpp 0 → 100644
  1 +// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
  2 +// under NSF AWARD 1414736 and by the respective contributors.
  3 +// All rights reserved.
  4 +//
  5 +// SPDX-License-Identifier: BSD-3-Clause
  6 +
  7 +#include <CLI/impl/App_inl.hpp>
  8 +
  9 +#include <CLI/Config.hpp>
  10 +#include <CLI/Formatter.hpp>
... ...