Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-20 09:07:12

0001 // Copyright (c) 2017-2025, University of Cincinnati, developed by Henry Schreiner
0002 // under NSF AWARD 1414736 and by the respective contributors.
0003 // All rights reserved.
0004 //
0005 // SPDX-License-Identifier: BSD-3-Clause
0006 
0007 #pragma once
0008 
0009 // IWYU pragma: private, include "CLI/CLI.hpp"
0010 
0011 // This include is only needed for IDEs to discover symbols
0012 #include "../Option.hpp"
0013 
0014 // [CLI11:public_includes:set]
0015 #include <algorithm>
0016 #include <memory>
0017 #include <string>
0018 #include <utility>
0019 #include <vector>
0020 // [CLI11:public_includes:end]
0021 
0022 namespace CLI {
0023 // [CLI11:option_inl_hpp:verbatim]
0024 
0025 template <typename CRTP> template <typename T> void OptionBase<CRTP>::copy_to(T *other) const {
0026     other->group(group_);
0027     other->required(required_);
0028     other->ignore_case(ignore_case_);
0029     other->ignore_underscore(ignore_underscore_);
0030     other->configurable(configurable_);
0031     other->disable_flag_override(disable_flag_override_);
0032     other->delimiter(delimiter_);
0033     other->always_capture_default(always_capture_default_);
0034     other->multi_option_policy(multi_option_policy_);
0035     other->callback_priority(callback_priority_);
0036 }
0037 
0038 CLI11_INLINE Option *Option::expected(int value) {
0039     if(value < 0) {
0040         expected_min_ = -value;
0041         if(expected_max_ < expected_min_) {
0042             expected_max_ = expected_min_;
0043         }
0044         allow_extra_args_ = true;
0045         flag_like_ = false;
0046     } else if(value == detail::expected_max_vector_size) {
0047         expected_min_ = 1;
0048         expected_max_ = detail::expected_max_vector_size;
0049         allow_extra_args_ = true;
0050         flag_like_ = false;
0051     } else {
0052         expected_min_ = value;
0053         expected_max_ = value;
0054         flag_like_ = (expected_min_ == 0);
0055     }
0056     return this;
0057 }
0058 
0059 CLI11_INLINE Option *Option::expected(int value_min, int value_max) {
0060     if(value_min < 0) {
0061         value_min = -value_min;
0062     }
0063 
0064     if(value_max < 0) {
0065         value_max = detail::expected_max_vector_size;
0066     }
0067     if(value_max < value_min) {
0068         expected_min_ = value_max;
0069         expected_max_ = value_min;
0070     } else {
0071         expected_max_ = value_max;
0072         expected_min_ = value_min;
0073     }
0074 
0075     return this;
0076 }
0077 
0078 CLI11_INLINE Option *Option::check(Validator_p validator) {
0079     validator->non_modifying();
0080     validators_.push_back(std::move(validator));
0081 
0082     return this;
0083 }
0084 
0085 CLI11_INLINE Option *Option::check(Validator validator, const std::string &validator_name) {
0086     validator.non_modifying();
0087     auto vp = std::make_shared<Validator>(std::move(validator));
0088     if(!validator_name.empty()) {
0089         vp->name(validator_name);
0090     }
0091     validators_.push_back(std::move(vp));
0092 
0093     return this;
0094 }
0095 
0096 CLI11_INLINE Option *Option::check(std::function<std::string(const std::string &)> validator_func,
0097                                    std::string validator_description,
0098                                    std::string validator_name) {
0099 
0100     auto vp = std::make_shared<Validator>(
0101         std::move(validator_func), std::move(validator_description), std::move(validator_name));
0102     vp->non_modifying();
0103     validators_.push_back(std::move(vp));
0104     return this;
0105 }
0106 
0107 CLI11_INLINE Option *Option::transform(Validator_p validator) {
0108     validators_.insert(validators_.begin(), std::move(validator));
0109 
0110     return this;
0111 }
0112 
0113 CLI11_INLINE Option *Option::transform(Validator validator, const std::string &transform_name) {
0114     auto vp = std::make_shared<Validator>(std::move(validator));
0115     if(!transform_name.empty()) {
0116         vp->name(transform_name);
0117     }
0118     validators_.insert(validators_.begin(), std::move(vp));
0119     return this;
0120 }
0121 
0122 CLI11_INLINE Option *Option::transform(const std::function<std::string(std::string)> &transform_func,
0123                                        std::string transform_description,
0124                                        std::string transform_name) {
0125     auto vp = std::make_shared<Validator>(
0126         [transform_func](std::string &val) {
0127             val = transform_func(val);
0128             return std::string{};
0129         },
0130         std::move(transform_description),
0131         std::move(transform_name));
0132     validators_.insert(validators_.begin(), std::move(vp));
0133 
0134     return this;
0135 }
0136 
0137 CLI11_INLINE Option *Option::each(const std::function<void(std::string)> &func) {
0138     auto vp = std::make_shared<Validator>(
0139         [func](std::string &inout) {
0140             func(inout);
0141             return std::string{};
0142         },
0143         std::string{});
0144     validators_.push_back(std::move(vp));
0145     return this;
0146 }
0147 
0148 CLI11_INLINE Validator *Option::get_validator(const std::string &validator_name) {
0149     for(auto &validator : validators_) {
0150         if(validator_name == validator->get_name()) {
0151             return validator.get();
0152         }
0153     }
0154     if((validator_name.empty()) && (!validators_.empty())) {
0155         return validators_.front().get();
0156     }
0157     throw OptionNotFound(std::string{"Validator "} + validator_name + " Not Found");
0158 }
0159 
0160 CLI11_INLINE Validator *Option::get_validator(int index) {
0161     // This is a signed int so that it is not equivalent to a pointer.
0162     if(index >= 0 && index < static_cast<int>(validators_.size())) {
0163         return validators_[static_cast<decltype(validators_)::size_type>(index)].get();
0164     }
0165     throw OptionNotFound("Validator index is not valid");
0166 }
0167 
0168 CLI11_INLINE bool Option::remove_needs(Option *opt) {
0169     auto iterator = std::find(std::begin(needs_), std::end(needs_), opt);
0170 
0171     if(iterator == std::end(needs_)) {
0172         return false;
0173     }
0174     needs_.erase(iterator);
0175     return true;
0176 }
0177 
0178 CLI11_INLINE Option *Option::excludes(Option *opt) {
0179     if(opt == this) {
0180         throw(IncorrectConstruction("and option cannot exclude itself"));
0181     }
0182     excludes_.insert(opt);
0183 
0184     // Help text should be symmetric - excluding a should exclude b
0185     opt->excludes_.insert(this);
0186 
0187     // Ignoring the insert return value, excluding twice is now allowed.
0188     // (Mostly to allow both directions to be excluded by user, even though the library does it for you.)
0189 
0190     return this;
0191 }
0192 
0193 CLI11_INLINE bool Option::remove_excludes(Option *opt) {
0194     auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt);
0195 
0196     if(iterator == std::end(excludes_)) {
0197         return false;
0198     }
0199     excludes_.erase(iterator);
0200     return true;
0201 }
0202 
0203 template <typename T> Option *Option::ignore_case(bool value) {
0204     if(!ignore_case_ && value) {
0205         ignore_case_ = value;
0206         auto *parent = static_cast<T *>(parent_);
0207         for(const Option_p &opt : parent->options_) {
0208             if(opt.get() == this) {
0209                 continue;
0210             }
0211             const auto &omatch = opt->matching_name(*this);
0212             if(!omatch.empty()) {
0213                 ignore_case_ = false;
0214                 throw OptionAlreadyAdded("adding ignore case caused a name conflict with " + omatch);
0215             }
0216         }
0217     } else {
0218         ignore_case_ = value;
0219     }
0220     return this;
0221 }
0222 
0223 template <typename T> Option *Option::ignore_underscore(bool value) {
0224 
0225     if(!ignore_underscore_ && value) {
0226         ignore_underscore_ = value;
0227         auto *parent = static_cast<T *>(parent_);
0228         for(const Option_p &opt : parent->options_) {
0229             if(opt.get() == this) {
0230                 continue;
0231             }
0232             const auto &omatch = opt->matching_name(*this);
0233             if(!omatch.empty()) {
0234                 ignore_underscore_ = false;
0235                 throw OptionAlreadyAdded("adding ignore underscore caused a name conflict with " + omatch);
0236             }
0237         }
0238     } else {
0239         ignore_underscore_ = value;
0240     }
0241     return this;
0242 }
0243 
0244 CLI11_INLINE Option *Option::multi_option_policy(MultiOptionPolicy value) {
0245     if(value != multi_option_policy_) {
0246         if(multi_option_policy_ == MultiOptionPolicy::Throw && expected_max_ == detail::expected_max_vector_size &&
0247            expected_min_ > 1) {  // this bizarre condition is to maintain backwards compatibility
0248                                  // with the previous behavior of expected_ with vectors
0249             expected_max_ = expected_min_;
0250         }
0251         multi_option_policy_ = value;
0252         current_option_state_ = option_state::parsing;
0253     }
0254     return this;
0255 }
0256 
0257 CLI11_NODISCARD CLI11_INLINE std::string Option::get_name(bool positional, bool all_options) const {
0258     if(get_group().empty())
0259         return {};  // Hidden
0260 
0261     if(all_options) {
0262 
0263         std::vector<std::string> name_list;
0264 
0265         /// The all list will never include a positional unless asked or that's the only name.
0266         if((positional && (!pname_.empty())) || (snames_.empty() && lnames_.empty())) {
0267             name_list.push_back(pname_);
0268         }
0269         if((get_items_expected() == 0) && (!fnames_.empty())) {
0270             for(const std::string &sname : snames_) {
0271                 name_list.push_back("-" + sname);
0272                 if(check_fname(sname)) {
0273                     name_list.back() += "{" + get_flag_value(sname, "") + "}";
0274                 }
0275             }
0276 
0277             for(const std::string &lname : lnames_) {
0278                 name_list.push_back("--" + lname);
0279                 if(check_fname(lname)) {
0280                     name_list.back() += "{" + get_flag_value(lname, "") + "}";
0281                 }
0282             }
0283         } else {
0284             for(const std::string &sname : snames_)
0285                 name_list.push_back("-" + sname);
0286 
0287             for(const std::string &lname : lnames_)
0288                 name_list.push_back("--" + lname);
0289         }
0290 
0291         return detail::join(name_list);
0292     }
0293 
0294     // This returns the positional name no matter what
0295     if(positional)
0296         return pname_;
0297 
0298     // Prefer long name
0299     if(!lnames_.empty())
0300         return std::string(2, '-') + lnames_[0];
0301 
0302     // Or short name if no long name
0303     if(!snames_.empty())
0304         return std::string(1, '-') + snames_[0];
0305 
0306     // If positional is the only name, it's okay to use that
0307     return pname_;
0308 }
0309 
0310 CLI11_INLINE void Option::run_callback() {
0311     bool used_default_str = false;
0312     if(force_callback_ && results_.empty()) {
0313         used_default_str = true;
0314         add_result(default_str_);
0315     }
0316     if(current_option_state_ == option_state::parsing) {
0317         _validate_results(results_);
0318         current_option_state_ = option_state::validated;
0319     }
0320 
0321     if(current_option_state_ < option_state::reduced) {
0322         _reduce_results(proc_results_, results_);
0323     }
0324 
0325     current_option_state_ = option_state::callback_run;
0326     if(callback_) {
0327         const results_t &send_results = proc_results_.empty() ? results_ : proc_results_;
0328         if(send_results.empty()) {
0329             return;
0330         }
0331         bool local_result = callback_(send_results);
0332         if(used_default_str) {
0333             // we only clear the results if the callback was actually used
0334             // otherwise the callback is the storage of the default
0335             results_.clear();
0336             proc_results_.clear();
0337         }
0338         if(!local_result)
0339             throw ConversionError(get_name(), results_);
0340     }
0341 }
0342 
0343 CLI11_NODISCARD CLI11_INLINE const std::string &Option::matching_name(const Option &other) const {
0344     static const std::string estring;
0345     bool bothConfigurable = configurable_ && other.configurable_;
0346     for(const std::string &sname : snames_) {
0347         if(other.check_sname(sname))
0348             return sname;
0349         if(bothConfigurable && other.check_lname(sname))
0350             return sname;
0351     }
0352     for(const std::string &lname : lnames_) {
0353         if(other.check_lname(lname))
0354             return lname;
0355         if(lname.size() == 1 && bothConfigurable) {
0356             if(other.check_sname(lname)) {
0357                 return lname;
0358             }
0359         }
0360     }
0361     if(bothConfigurable && snames_.empty() && lnames_.empty() && !pname_.empty()) {
0362         if(other.check_sname(pname_) || other.check_lname(pname_) || pname_ == other.pname_)
0363             return pname_;
0364     }
0365     if(bothConfigurable && other.snames_.empty() && other.fnames_.empty() && !other.pname_.empty()) {
0366         if(check_sname(other.pname_) || check_lname(other.pname_) || (pname_ == other.pname_))
0367             return other.pname_;
0368     }
0369     if(ignore_case_ ||
0370        ignore_underscore_) {  // We need to do the inverse, in case we are ignore_case or ignore underscore
0371         for(const std::string &sname : other.snames_)
0372             if(check_sname(sname))
0373                 return sname;
0374         for(const std::string &lname : other.lnames_)
0375             if(check_lname(lname))
0376                 return lname;
0377     }
0378     return estring;
0379 }
0380 
0381 CLI11_NODISCARD CLI11_INLINE bool Option::check_name(const std::string &name) const {
0382 
0383     if(name.length() > 2 && name[0] == '-' && name[1] == '-')
0384         return check_lname(name.substr(2));
0385     if(name.length() > 1 && name.front() == '-')
0386         return check_sname(name.substr(1));
0387     if(!pname_.empty()) {
0388         std::string local_pname = pname_;
0389         std::string local_name = name;
0390         if(ignore_underscore_) {
0391             local_pname = detail::remove_underscore(local_pname);
0392             local_name = detail::remove_underscore(local_name);
0393         }
0394         if(ignore_case_) {
0395             local_pname = detail::to_lower(local_pname);
0396             local_name = detail::to_lower(local_name);
0397         }
0398         if(local_name == local_pname) {
0399             return true;
0400         }
0401     }
0402 
0403     if(!envname_.empty()) {
0404         // this needs to be the original since envname_ shouldn't match on case insensitivity
0405         return (name == envname_);
0406     }
0407     return false;
0408 }
0409 
0410 CLI11_NODISCARD CLI11_INLINE std::string Option::get_flag_value(const std::string &name,
0411                                                                 std::string input_value) const {
0412     static const std::string trueString{"true"};
0413     static const std::string falseString{"false"};
0414     static const std::string emptyString{"{}"};
0415     // check for disable flag override_
0416     if(disable_flag_override_) {
0417         if(!((input_value.empty()) || (input_value == emptyString))) {
0418             auto default_ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
0419             if(default_ind >= 0) {
0420                 // We can static cast this to std::size_t because it is more than 0 in this block
0421                 if(default_flag_values_[static_cast<std::size_t>(default_ind)].second != input_value) {
0422                     if(input_value == default_str_ && force_callback_) {
0423                         return input_value;
0424                     }
0425                     throw(ArgumentMismatch::FlagOverride(name));
0426                 }
0427             } else {
0428                 if(input_value != trueString) {
0429                     throw(ArgumentMismatch::FlagOverride(name));
0430                 }
0431             }
0432         }
0433     }
0434     auto ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
0435     if((input_value.empty()) || (input_value == emptyString)) {
0436         if(flag_like_) {
0437             return (ind < 0) ? trueString : default_flag_values_[static_cast<std::size_t>(ind)].second;
0438         }
0439         return (ind < 0) ? default_str_ : default_flag_values_[static_cast<std::size_t>(ind)].second;
0440     }
0441     if(ind < 0) {
0442         return input_value;
0443     }
0444     if(default_flag_values_[static_cast<std::size_t>(ind)].second == falseString) {
0445         errno = 0;
0446         auto val = detail::to_flag_value(input_value);
0447         if(errno != 0) {
0448             errno = 0;
0449             return input_value;
0450         }
0451         return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
0452     }
0453     return input_value;
0454 }
0455 
0456 CLI11_INLINE Option *Option::add_result(std::string s) {
0457     _add_result(std::move(s), results_);
0458     current_option_state_ = option_state::parsing;
0459     return this;
0460 }
0461 
0462 CLI11_INLINE Option *Option::add_result(std::string s, int &results_added) {
0463     results_added = _add_result(std::move(s), results_);
0464     current_option_state_ = option_state::parsing;
0465     return this;
0466 }
0467 
0468 CLI11_INLINE Option *Option::add_result(std::vector<std::string> s) {
0469     current_option_state_ = option_state::parsing;
0470     for(auto &str : s) {
0471         _add_result(std::move(str), results_);
0472     }
0473     return this;
0474 }
0475 
0476 CLI11_NODISCARD CLI11_INLINE results_t Option::reduced_results() const {
0477     results_t res = proc_results_.empty() ? results_ : proc_results_;
0478     if(current_option_state_ < option_state::reduced) {
0479         if(current_option_state_ == option_state::parsing) {
0480             res = results_;
0481             _validate_results(res);
0482         }
0483         if(!res.empty()) {
0484             results_t extra;
0485             _reduce_results(extra, res);
0486             if(!extra.empty()) {
0487                 res = std::move(extra);
0488             }
0489         }
0490     }
0491     return res;
0492 }
0493 
0494 CLI11_INLINE Option *Option::type_size(int option_type_size) {
0495     if(option_type_size < 0) {
0496         // this section is included for backwards compatibility
0497         type_size_max_ = -option_type_size;
0498         type_size_min_ = -option_type_size;
0499         expected_max_ = detail::expected_max_vector_size;
0500     } else {
0501         type_size_max_ = option_type_size;
0502         if(type_size_max_ < detail::expected_max_vector_size) {
0503             type_size_min_ = option_type_size;
0504         } else {
0505             inject_separator_ = true;
0506         }
0507         if(type_size_max_ == 0)
0508             required_ = false;
0509     }
0510     return this;
0511 }
0512 
0513 CLI11_INLINE Option *Option::type_size(int option_type_size_min, int option_type_size_max) {
0514     if(option_type_size_min < 0 || option_type_size_max < 0) {
0515         // this section is included for backwards compatibility
0516         expected_max_ = detail::expected_max_vector_size;
0517         option_type_size_min = (std::abs)(option_type_size_min);
0518         option_type_size_max = (std::abs)(option_type_size_max);
0519     }
0520 
0521     if(option_type_size_min > option_type_size_max) {
0522         type_size_max_ = option_type_size_min;
0523         type_size_min_ = option_type_size_max;
0524     } else {
0525         type_size_min_ = option_type_size_min;
0526         type_size_max_ = option_type_size_max;
0527     }
0528     if(type_size_max_ == 0) {
0529         required_ = false;
0530     }
0531     if(type_size_max_ >= detail::expected_max_vector_size) {
0532         inject_separator_ = true;
0533     }
0534     return this;
0535 }
0536 
0537 CLI11_NODISCARD CLI11_INLINE std::string Option::get_type_name() const {
0538     std::string full_type_name = type_name_();
0539     if(!validators_.empty()) {
0540         for(const auto &validator : validators_) {
0541             std::string vtype = validator->get_description();
0542             if(!vtype.empty()) {
0543                 full_type_name += ":" + vtype;
0544             }
0545         }
0546     }
0547     return full_type_name;
0548 }
0549 
0550 CLI11_INLINE void Option::_validate_results(results_t &res) const {
0551     // Run the Validators (can change the string)
0552     if(!validators_.empty()) {
0553         if(type_size_max_ > 1) {  // in this context index refers to the index in the type
0554             int index = 0;
0555             if(get_items_expected_max() < static_cast<int>(res.size()) &&
0556                (multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast ||
0557                 multi_option_policy_ == CLI::MultiOptionPolicy::Reverse)) {
0558                 // create a negative index for the earliest ones
0559                 index = get_items_expected_max() - static_cast<int>(res.size());
0560             }
0561 
0562             for(std::string &result : res) {
0563                 if(detail::is_separator(result) && type_size_max_ != type_size_min_ && index >= 0) {
0564                     index = 0;  // reset index for variable size chunks
0565                     continue;
0566                 }
0567                 auto err_msg = _validate(result, (index >= 0) ? (index % type_size_max_) : index);
0568                 if(!err_msg.empty())
0569                     throw ValidationError(get_name(), err_msg);
0570                 ++index;
0571             }
0572         } else {
0573             int index = 0;
0574             if(expected_max_ < static_cast<int>(res.size()) &&
0575                (multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast ||
0576                 multi_option_policy_ == CLI::MultiOptionPolicy::Reverse)) {
0577                 // create a negative index for the earliest ones
0578                 index = expected_max_ - static_cast<int>(res.size());
0579             }
0580             for(std::string &result : res) {
0581                 auto err_msg = _validate(result, index);
0582                 ++index;
0583                 if(!err_msg.empty())
0584                     throw ValidationError(get_name(), err_msg);
0585             }
0586         }
0587     }
0588 }
0589 
0590 CLI11_INLINE void Option::_reduce_results(results_t &out, const results_t &original) const {
0591 
0592     // max num items expected or length of vector, always at least 1
0593     // Only valid for a trimming policy
0594 
0595     out.clear();
0596     // Operation depends on the policy setting
0597     switch(multi_option_policy_) {
0598     case MultiOptionPolicy::TakeAll:
0599         break;
0600     case MultiOptionPolicy::TakeLast: {
0601         // Allow multi-option sizes (including 0)
0602         std::size_t trim_size = std::min<std::size_t>(
0603             static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
0604         if(original.size() != trim_size) {
0605             out.assign(original.end() - static_cast<results_t::difference_type>(trim_size), original.end());
0606         }
0607     } break;
0608     case MultiOptionPolicy::Reverse: {
0609         // Allow multi-option sizes (including 0)
0610         std::size_t trim_size = std::min<std::size_t>(
0611             static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
0612         if(original.size() != trim_size || trim_size > 1) {
0613             out.assign(original.end() - static_cast<results_t::difference_type>(trim_size), original.end());
0614         }
0615         std::reverse(out.begin(), out.end());
0616     } break;
0617     case MultiOptionPolicy::TakeFirst: {
0618         std::size_t trim_size = std::min<std::size_t>(
0619             static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
0620         if(original.size() != trim_size) {
0621             out.assign(original.begin(), original.begin() + static_cast<results_t::difference_type>(trim_size));
0622         }
0623     } break;
0624     case MultiOptionPolicy::Join:
0625         if(results_.size() > 1) {
0626             out.push_back(detail::join(original, std::string(1, (delimiter_ == '\0') ? '\n' : delimiter_)));
0627         }
0628         break;
0629     case MultiOptionPolicy::Sum:
0630         out.push_back(detail::sum_string_vector(original));
0631         break;
0632     case MultiOptionPolicy::Throw:
0633     default: {
0634         auto num_min = static_cast<std::size_t>(get_items_expected_min());
0635         auto num_max = static_cast<std::size_t>(get_items_expected_max());
0636         if(num_min == 0) {
0637             num_min = 1;
0638         }
0639         if(num_max == 0) {
0640             num_max = 1;
0641         }
0642         if(original.size() < num_min) {
0643             throw ArgumentMismatch::AtLeast(get_name(), static_cast<int>(num_min), original.size());
0644         }
0645         if(original.size() > num_max) {
0646             if(original.size() == 2 && num_max == 1 && original[1] == "%%" && original[0] == "{}") {
0647                 // this condition is a trap for the following empty indicator check on config files, it may not be used
0648                 // anymore
0649                 out = original;  // LCOV_EXCL_LINE
0650             } else {
0651                 throw ArgumentMismatch::AtMost(get_name(), static_cast<int>(num_max), original.size());
0652             }
0653         }
0654         break;
0655     }
0656     }
0657     // this check is to allow an empty vector in certain circumstances but not if expected is not zero.
0658     // {} is the indicator for an empty container
0659     if(out.empty()) {
0660         if(original.size() == 1 && original[0] == "{}" && get_items_expected_min() > 0) {
0661             out.emplace_back("{}");
0662             out.emplace_back("%%");
0663         }
0664     } else if(out.size() == 1 && out[0] == "{}" && get_items_expected_min() > 0) {
0665         out.emplace_back("%%");
0666     }
0667 }
0668 
0669 CLI11_INLINE std::string Option::_validate(std::string &result, int index) const {
0670     std::string err_msg;
0671     if(result.empty() && expected_min_ == 0) {
0672         // an empty with nothing expected is allowed
0673         return err_msg;
0674     }
0675     for(const auto &vali : validators_) {
0676         auto v = vali->get_application_index();
0677         if(v == -1 || v == index) {
0678             try {
0679                 err_msg = (*vali)(result);
0680             } catch(const ValidationError &err) {
0681                 err_msg = err.what();
0682             }
0683             if(!err_msg.empty())
0684                 break;
0685         }
0686     }
0687 
0688     return err_msg;
0689 }
0690 
0691 CLI11_INLINE int Option::_add_result(std::string &&result, std::vector<std::string> &res) const {
0692     int result_count = 0;
0693 
0694     // Handle the vector escape possibility all characters duplicated and starting with [[ ending with ]]
0695     // this is always a single result
0696     if(result.size() >= 4 && result[0] == '[' && result[1] == '[' && result.back() == ']' &&
0697        (*(result.end() - 2) == ']')) {
0698         // this is an escape clause for odd strings
0699         std::string nstrs{'['};
0700         bool duplicated{true};
0701         for(std::size_t ii = 2; ii < result.size() - 2; ii += 2) {
0702             if(result[ii] == result[ii + 1]) {
0703                 nstrs.push_back(result[ii]);
0704             } else {
0705                 duplicated = false;
0706                 break;
0707             }
0708         }
0709         if(duplicated) {
0710             nstrs.push_back(']');
0711             res.push_back(std::move(nstrs));
0712             ++result_count;
0713             return result_count;
0714         }
0715     }
0716 
0717     if((allow_extra_args_ || get_expected_max() > 1 || get_type_size() > 1) && !result.empty() &&
0718        result.front() == '[' &&
0719        result.back() == ']') {  // this is now a vector string likely from the default or user entry
0720 
0721         result.pop_back();
0722         result.erase(result.begin());
0723         bool skipSection{false};
0724         for(auto &var : CLI::detail::split_up(result, ',')) {
0725             if(!var.empty()) {
0726                 result_count += _add_result(std::move(var), res);
0727             }
0728         }
0729         if(!skipSection) {
0730             return result_count;
0731         }
0732     }
0733     if(delimiter_ == '\0') {
0734         res.push_back(std::move(result));
0735         ++result_count;
0736     } else {
0737         if((result.find_first_of(delimiter_) != std::string::npos)) {
0738             for(const auto &var : CLI::detail::split(result, delimiter_)) {
0739                 if(!var.empty()) {
0740                     res.push_back(var);
0741                     ++result_count;
0742                 }
0743             }
0744         } else {
0745             res.push_back(std::move(result));
0746             ++result_count;
0747         }
0748     }
0749     return result_count;
0750 }
0751 // [CLI11:option_inl_hpp:end]
0752 }  // namespace CLI