Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-09 08:29:26

0001 // Copyright (c) 2017-2024, 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 #include "Error.hpp"
0012 #include "Macros.hpp"
0013 #include "StringTools.hpp"
0014 #include "TypeTools.hpp"
0015 
0016 // [CLI11:public_includes:set]
0017 #include <cmath>
0018 #include <cstdint>
0019 #include <functional>
0020 #include <iostream>
0021 #include <limits>
0022 #include <map>
0023 #include <memory>
0024 #include <string>
0025 #include <utility>
0026 #include <vector>
0027 // [CLI11:public_includes:end]
0028 
0029 // [CLI11:validators_hpp_filesystem:verbatim]
0030 
0031 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
0032 #include <filesystem>  // NOLINT(build/include)
0033 #else
0034 #include <sys/stat.h>
0035 #include <sys/types.h>
0036 #endif
0037 
0038 // [CLI11:validators_hpp_filesystem:end]
0039 
0040 namespace CLI {
0041 // [CLI11:validators_hpp:verbatim]
0042 
0043 class Option;
0044 
0045 /// @defgroup validator_group Validators
0046 
0047 /// @brief Some validators that are provided
0048 ///
0049 /// These are simple `std::string(const std::string&)` validators that are useful. They return
0050 /// a string if the validation fails. A custom struct is provided, as well, with the same user
0051 /// semantics, but with the ability to provide a new type name.
0052 /// @{
0053 
0054 ///
0055 class Validator {
0056   protected:
0057     /// This is the description function, if empty the description_ will be used
0058     std::function<std::string()> desc_function_{[]() { return std::string{}; }};
0059 
0060     /// This is the base function that is to be called.
0061     /// Returns a string error message if validation fails.
0062     std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
0063     /// The name for search purposes of the Validator
0064     std::string name_{};
0065     /// A Validator will only apply to an indexed value (-1 is all elements)
0066     int application_index_ = -1;
0067     /// Enable for Validator to allow it to be disabled if need be
0068     bool active_{true};
0069     /// specify that a validator should not modify the input
0070     bool non_modifying_{false};
0071 
0072     Validator(std::string validator_desc, std::function<std::string(std::string &)> func)
0073         : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(func)) {}
0074 
0075   public:
0076     Validator() = default;
0077     /// Construct a Validator with just the description string
0078     explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
0079     /// Construct Validator from basic information
0080     Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
0081         : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
0082           name_(std::move(validator_name)) {}
0083     /// Set the Validator operation function
0084     Validator &operation(std::function<std::string(std::string &)> op) {
0085         func_ = std::move(op);
0086         return *this;
0087     }
0088     /// This is the required operator for a Validator - provided to help
0089     /// users (CLI11 uses the member `func` directly)
0090     std::string operator()(std::string &str) const;
0091 
0092     /// This is the required operator for a Validator - provided to help
0093     /// users (CLI11 uses the member `func` directly)
0094     std::string operator()(const std::string &str) const {
0095         std::string value = str;
0096         return (active_) ? func_(value) : std::string{};
0097     }
0098 
0099     /// Specify the type string
0100     Validator &description(std::string validator_desc) {
0101         desc_function_ = [validator_desc]() { return validator_desc; };
0102         return *this;
0103     }
0104     /// Specify the type string
0105     CLI11_NODISCARD Validator description(std::string validator_desc) const;
0106 
0107     /// Generate type description information for the Validator
0108     CLI11_NODISCARD std::string get_description() const {
0109         if(active_) {
0110             return desc_function_();
0111         }
0112         return std::string{};
0113     }
0114     /// Specify the type string
0115     Validator &name(std::string validator_name) {
0116         name_ = std::move(validator_name);
0117         return *this;
0118     }
0119     /// Specify the type string
0120     CLI11_NODISCARD Validator name(std::string validator_name) const {
0121         Validator newval(*this);
0122         newval.name_ = std::move(validator_name);
0123         return newval;
0124     }
0125     /// Get the name of the Validator
0126     CLI11_NODISCARD const std::string &get_name() const { return name_; }
0127     /// Specify whether the Validator is active or not
0128     Validator &active(bool active_val = true) {
0129         active_ = active_val;
0130         return *this;
0131     }
0132     /// Specify whether the Validator is active or not
0133     CLI11_NODISCARD Validator active(bool active_val = true) const {
0134         Validator newval(*this);
0135         newval.active_ = active_val;
0136         return newval;
0137     }
0138 
0139     /// Specify whether the Validator can be modifying or not
0140     Validator &non_modifying(bool no_modify = true) {
0141         non_modifying_ = no_modify;
0142         return *this;
0143     }
0144     /// Specify the application index of a validator
0145     Validator &application_index(int app_index) {
0146         application_index_ = app_index;
0147         return *this;
0148     }
0149     /// Specify the application index of a validator
0150     CLI11_NODISCARD Validator application_index(int app_index) const {
0151         Validator newval(*this);
0152         newval.application_index_ = app_index;
0153         return newval;
0154     }
0155     /// Get the current value of the application index
0156     CLI11_NODISCARD int get_application_index() const { return application_index_; }
0157     /// Get a boolean if the validator is active
0158     CLI11_NODISCARD bool get_active() const { return active_; }
0159 
0160     /// Get a boolean if the validator is allowed to modify the input returns true if it can modify the input
0161     CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; }
0162 
0163     /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the
0164     /// same.
0165     Validator operator&(const Validator &other) const;
0166 
0167     /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the
0168     /// same.
0169     Validator operator|(const Validator &other) const;
0170 
0171     /// Create a validator that fails when a given validator succeeds
0172     Validator operator!() const;
0173 
0174   private:
0175     void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger);
0176 };
0177 
0178 /// Class wrapping some of the accessors of Validator
0179 class CustomValidator : public Validator {
0180   public:
0181 };
0182 // The implementation of the built in validators is using the Validator class;
0183 // the user is only expected to use the const (static) versions (since there's no setup).
0184 // Therefore, this is in detail.
0185 namespace detail {
0186 
0187 /// CLI enumeration of different file types
0188 enum class path_type { nonexistent, file, directory };
0189 
0190 /// get the type of the path from a file name
0191 CLI11_INLINE path_type check_path(const char *file) noexcept;
0192 
0193 /// Check for an existing file (returns error message if check fails)
0194 class ExistingFileValidator : public Validator {
0195   public:
0196     ExistingFileValidator();
0197 };
0198 
0199 /// Check for an existing directory (returns error message if check fails)
0200 class ExistingDirectoryValidator : public Validator {
0201   public:
0202     ExistingDirectoryValidator();
0203 };
0204 
0205 /// Check for an existing path
0206 class ExistingPathValidator : public Validator {
0207   public:
0208     ExistingPathValidator();
0209 };
0210 
0211 /// Check for an non-existing path
0212 class NonexistentPathValidator : public Validator {
0213   public:
0214     NonexistentPathValidator();
0215 };
0216 
0217 /// Validate the given string is a legal ipv4 address
0218 class IPV4Validator : public Validator {
0219   public:
0220     IPV4Validator();
0221 };
0222 
0223 class EscapedStringTransformer : public Validator {
0224   public:
0225     EscapedStringTransformer();
0226 };
0227 
0228 }  // namespace detail
0229 
0230 // Static is not needed here, because global const implies static.
0231 
0232 /// Check for existing file (returns error message if check fails)
0233 const detail::ExistingFileValidator ExistingFile;
0234 
0235 /// Check for an existing directory (returns error message if check fails)
0236 const detail::ExistingDirectoryValidator ExistingDirectory;
0237 
0238 /// Check for an existing path
0239 const detail::ExistingPathValidator ExistingPath;
0240 
0241 /// Check for an non-existing path
0242 const detail::NonexistentPathValidator NonexistentPath;
0243 
0244 /// Check for an IP4 address
0245 const detail::IPV4Validator ValidIPV4;
0246 
0247 /// convert escaped characters into their associated values
0248 const detail::EscapedStringTransformer EscapedString;
0249 
0250 /// Validate the input as a particular type
0251 template <typename DesiredType> class TypeValidator : public Validator {
0252   public:
0253     explicit TypeValidator(const std::string &validator_name)
0254         : Validator(validator_name, [](std::string &input_string) {
0255               using CLI::detail::lexical_cast;
0256               auto val = DesiredType();
0257               if(!lexical_cast(input_string, val)) {
0258                   return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
0259               }
0260               return std::string();
0261           }) {}
0262     TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}
0263 };
0264 
0265 /// Check for a number
0266 const TypeValidator<double> Number("NUMBER");
0267 
0268 /// Modify a path if the file is a particular default location, can be used as Check or transform
0269 /// with the error return optionally disabled
0270 class FileOnDefaultPath : public Validator {
0271   public:
0272     explicit FileOnDefaultPath(std::string default_path, bool enableErrorReturn = true);
0273 };
0274 
0275 /// Produce a range (factory). Min and max are inclusive.
0276 class Range : public Validator {
0277   public:
0278     /// This produces a range with min and max inclusive.
0279     ///
0280     /// Note that the constructor is templated, but the struct is not, so C++17 is not
0281     /// needed to provide nice syntax for Range(a,b).
0282     template <typename T>
0283     Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) {
0284         if(validator_name.empty()) {
0285             std::stringstream out;
0286             out << detail::type_name<T>() << " in [" << min_val << " - " << max_val << "]";
0287             description(out.str());
0288         }
0289 
0290         func_ = [min_val, max_val](std::string &input) {
0291             using CLI::detail::lexical_cast;
0292             T val;
0293             bool converted = lexical_cast(input, val);
0294             if((!converted) || (val < min_val || val > max_val)) {
0295                 std::stringstream out;
0296                 out << "Value " << input << " not in range [";
0297                 out << min_val << " - " << max_val << "]";
0298                 return out.str();
0299             }
0300             return std::string{};
0301         };
0302     }
0303 
0304     /// Range of one value is 0 to value
0305     template <typename T>
0306     explicit Range(T max_val, const std::string &validator_name = std::string{})
0307         : Range(static_cast<T>(0), max_val, validator_name) {}
0308 };
0309 
0310 /// Check for a non negative number
0311 const Range NonNegativeNumber((std::numeric_limits<double>::max)(), "NONNEGATIVE");
0312 
0313 /// Check for a positive valued number (val>0.0), <double>::min  here is the smallest positive number
0314 const Range PositiveNumber((std::numeric_limits<double>::min)(), (std::numeric_limits<double>::max)(), "POSITIVE");
0315 
0316 /// Produce a bounded range (factory). Min and max are inclusive.
0317 class Bound : public Validator {
0318   public:
0319     /// This bounds a value with min and max inclusive.
0320     ///
0321     /// Note that the constructor is templated, but the struct is not, so C++17 is not
0322     /// needed to provide nice syntax for Range(a,b).
0323     template <typename T> Bound(T min_val, T max_val) {
0324         std::stringstream out;
0325         out << detail::type_name<T>() << " bounded to [" << min_val << " - " << max_val << "]";
0326         description(out.str());
0327 
0328         func_ = [min_val, max_val](std::string &input) {
0329             using CLI::detail::lexical_cast;
0330             T val;
0331             bool converted = lexical_cast(input, val);
0332             if(!converted) {
0333                 return std::string("Value ") + input + " could not be converted";
0334             }
0335             if(val < min_val)
0336                 input = detail::to_string(min_val);
0337             else if(val > max_val)
0338                 input = detail::to_string(max_val);
0339 
0340             return std::string{};
0341         };
0342     }
0343 
0344     /// Range of one value is 0 to value
0345     template <typename T> explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}
0346 };
0347 
0348 namespace detail {
0349 template <typename T,
0350           enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0351 auto smart_deref(T value) -> decltype(*value) {
0352     return *value;
0353 }
0354 
0355 template <
0356     typename T,
0357     enable_if_t<!is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0358 typename std::remove_reference<T>::type &smart_deref(T &value) {
0359     return value;
0360 }
0361 /// Generate a string representation of a set
0362 template <typename T> std::string generate_set(const T &set) {
0363     using element_t = typename detail::element_type<T>::type;
0364     using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;  // the type of the object pair
0365     std::string out(1, '{');
0366     out.append(detail::join(
0367         detail::smart_deref(set),
0368         [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
0369         ","));
0370     out.push_back('}');
0371     return out;
0372 }
0373 
0374 /// Generate a string representation of a map
0375 template <typename T> std::string generate_map(const T &map, bool key_only = false) {
0376     using element_t = typename detail::element_type<T>::type;
0377     using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;  // the type of the object pair
0378     std::string out(1, '{');
0379     out.append(detail::join(
0380         detail::smart_deref(map),
0381         [key_only](const iteration_type_t &v) {
0382             std::string res{detail::to_string(detail::pair_adaptor<element_t>::first(v))};
0383 
0384             if(!key_only) {
0385                 res.append("->");
0386                 res += detail::to_string(detail::pair_adaptor<element_t>::second(v));
0387             }
0388             return res;
0389         },
0390         ","));
0391     out.push_back('}');
0392     return out;
0393 }
0394 
0395 template <typename C, typename V> struct has_find {
0396     template <typename CC, typename VV>
0397     static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
0398     template <typename, typename> static auto test(...) -> decltype(std::false_type());
0399 
0400     static const auto value = decltype(test<C, V>(0))::value;
0401     using type = std::integral_constant<bool, value>;
0402 };
0403 
0404 /// A search function
0405 template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
0406 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0407     using element_t = typename detail::element_type<T>::type;
0408     auto &setref = detail::smart_deref(set);
0409     auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
0410         return (detail::pair_adaptor<element_t>::first(v) == val);
0411     });
0412     return {(it != std::end(setref)), it};
0413 }
0414 
0415 /// A search function that uses the built in find function
0416 template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
0417 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0418     auto &setref = detail::smart_deref(set);
0419     auto it = setref.find(val);
0420     return {(it != std::end(setref)), it};
0421 }
0422 
0423 /// A search function with a filter function
0424 template <typename T, typename V>
0425 auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
0426     -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0427     using element_t = typename detail::element_type<T>::type;
0428     // do the potentially faster first search
0429     auto res = search(set, val);
0430     if((res.first) || (!(filter_function))) {
0431         return res;
0432     }
0433     // if we haven't found it do the longer linear search with all the element translations
0434     auto &setref = detail::smart_deref(set);
0435     auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
0436         V a{detail::pair_adaptor<element_t>::first(v)};
0437         a = filter_function(a);
0438         return (a == val);
0439     });
0440     return {(it != std::end(setref)), it};
0441 }
0442 
0443 // the following suggestion was made by Nikita Ofitserov(@himikof)
0444 // done in templates to prevent compiler warnings on negation of unsigned numbers
0445 
0446 /// Do a check for overflow on signed numbers
0447 template <typename T>
0448 inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
0449     if((a > 0) == (b > 0)) {
0450         return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
0451     }
0452     return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
0453 }
0454 /// Do a check for overflow on unsigned numbers
0455 template <typename T>
0456 inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
0457     return ((std::numeric_limits<T>::max)() / a < b);
0458 }
0459 
0460 /// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise.
0461 template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
0462     if(a == 0 || b == 0 || a == 1 || b == 1) {
0463         a *= b;
0464         return true;
0465     }
0466     if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
0467         return false;
0468     }
0469     if(overflowCheck(a, b)) {
0470         return false;
0471     }
0472     a *= b;
0473     return true;
0474 }
0475 
0476 /// Performs a *= b; if it doesn't equal infinity. Returns false otherwise.
0477 template <typename T>
0478 typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
0479     T c = a * b;
0480     if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
0481         return false;
0482     }
0483     a = c;
0484     return true;
0485 }
0486 
0487 }  // namespace detail
0488 /// Verify items are in a set
0489 class IsMember : public Validator {
0490   public:
0491     using filter_fn_t = std::function<std::string(std::string)>;
0492 
0493     /// This allows in-place construction using an initializer list
0494     template <typename T, typename... Args>
0495     IsMember(std::initializer_list<T> values, Args &&...args)
0496         : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
0497 
0498     /// This checks to see if an item is in a set (empty function)
0499     template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
0500 
0501     /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
0502     /// both sides of the comparison before computing the comparison.
0503     template <typename T, typename F> explicit IsMember(T set, F filter_function) {
0504 
0505         // Get the type of the contained item - requires a container have ::value_type
0506         // if the type does not have first_type and second_type, these are both value_type
0507         using element_t = typename detail::element_type<T>::type;             // Removes (smart) pointers if needed
0508         using item_t = typename detail::pair_adaptor<element_t>::first_type;  // Is value_type if not a map
0509 
0510         using local_item_t = typename IsMemberType<item_t>::type;  // This will convert bad types to good ones
0511                                                                    // (const char * to std::string)
0512 
0513         // Make a local copy of the filter function, using a std::function if not one already
0514         std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0515 
0516         // This is the type name for help, it will take the current version of the set contents
0517         desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
0518 
0519         // This is the function that validates
0520         // It stores a copy of the set pointer-like, so shared_ptr will stay alive
0521         func_ = [set, filter_fn](std::string &input) {
0522             using CLI::detail::lexical_cast;
0523             local_item_t b;
0524             if(!lexical_cast(input, b)) {
0525                 throw ValidationError(input);  // name is added later
0526             }
0527             if(filter_fn) {
0528                 b = filter_fn(b);
0529             }
0530             auto res = detail::search(set, b, filter_fn);
0531             if(res.first) {
0532                 // Make sure the version in the input string is identical to the one in the set
0533                 if(filter_fn) {
0534                     input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
0535                 }
0536 
0537                 // Return empty error string (success)
0538                 return std::string{};
0539             }
0540 
0541             // If you reach this point, the result was not found
0542             return input + " not in " + detail::generate_set(detail::smart_deref(set));
0543         };
0544     }
0545 
0546     /// You can pass in as many filter functions as you like, they nest (string only currently)
0547     template <typename T, typename... Args>
0548     IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0549         : IsMember(
0550               std::forward<T>(set),
0551               [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0552               other...) {}
0553 };
0554 
0555 /// definition of the default transformation object
0556 template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
0557 
0558 /// Translate named items to other or a value set
0559 class Transformer : public Validator {
0560   public:
0561     using filter_fn_t = std::function<std::string(std::string)>;
0562 
0563     /// This allows in-place construction
0564     template <typename... Args>
0565     Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
0566         : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0567 
0568     /// direct map of std::string to std::string
0569     template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
0570 
0571     /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
0572     /// both sides of the comparison before computing the comparison.
0573     template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
0574 
0575         static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0576                       "mapping must produce value pairs");
0577         // Get the type of the contained item - requires a container have ::value_type
0578         // if the type does not have first_type and second_type, these are both value_type
0579         using element_t = typename detail::element_type<T>::type;             // Removes (smart) pointers if needed
0580         using item_t = typename detail::pair_adaptor<element_t>::first_type;  // Is value_type if not a map
0581         using local_item_t = typename IsMemberType<item_t>::type;             // Will convert bad types to good ones
0582                                                                               // (const char * to std::string)
0583 
0584         // Make a local copy of the filter function, using a std::function if not one already
0585         std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0586 
0587         // This is the type name for help, it will take the current version of the set contents
0588         desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
0589 
0590         func_ = [mapping, filter_fn](std::string &input) {
0591             using CLI::detail::lexical_cast;
0592             local_item_t b;
0593             if(!lexical_cast(input, b)) {
0594                 return std::string();
0595                 // there is no possible way we can match anything in the mapping if we can't convert so just return
0596             }
0597             if(filter_fn) {
0598                 b = filter_fn(b);
0599             }
0600             auto res = detail::search(mapping, b, filter_fn);
0601             if(res.first) {
0602                 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0603             }
0604             return std::string{};
0605         };
0606     }
0607 
0608     /// You can pass in as many filter functions as you like, they nest
0609     template <typename T, typename... Args>
0610     Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0611         : Transformer(
0612               std::forward<T>(mapping),
0613               [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0614               other...) {}
0615 };
0616 
0617 /// translate named items to other or a value set
0618 class CheckedTransformer : public Validator {
0619   public:
0620     using filter_fn_t = std::function<std::string(std::string)>;
0621 
0622     /// This allows in-place construction
0623     template <typename... Args>
0624     CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
0625         : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0626 
0627     /// direct map of std::string to std::string
0628     template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
0629 
0630     /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
0631     /// both sides of the comparison before computing the comparison.
0632     template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
0633 
0634         static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0635                       "mapping must produce value pairs");
0636         // Get the type of the contained item - requires a container have ::value_type
0637         // if the type does not have first_type and second_type, these are both value_type
0638         using element_t = typename detail::element_type<T>::type;             // Removes (smart) pointers if needed
0639         using item_t = typename detail::pair_adaptor<element_t>::first_type;  // Is value_type if not a map
0640         using local_item_t = typename IsMemberType<item_t>::type;             // Will convert bad types to good ones
0641                                                                               // (const char * to std::string)
0642         using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;  // the type of the object pair
0643 
0644         // Make a local copy of the filter function, using a std::function if not one already
0645         std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0646 
0647         auto tfunc = [mapping]() {
0648             std::string out("value in ");
0649             out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
0650             out += detail::join(
0651                 detail::smart_deref(mapping),
0652                 [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
0653                 ",");
0654             out.push_back('}');
0655             return out;
0656         };
0657 
0658         desc_function_ = tfunc;
0659 
0660         func_ = [mapping, tfunc, filter_fn](std::string &input) {
0661             using CLI::detail::lexical_cast;
0662             local_item_t b;
0663             bool converted = lexical_cast(input, b);
0664             if(converted) {
0665                 if(filter_fn) {
0666                     b = filter_fn(b);
0667                 }
0668                 auto res = detail::search(mapping, b, filter_fn);
0669                 if(res.first) {
0670                     input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0671                     return std::string{};
0672                 }
0673             }
0674             for(const auto &v : detail::smart_deref(mapping)) {
0675                 auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v));
0676                 if(output_string == input) {
0677                     return std::string();
0678                 }
0679             }
0680 
0681             return "Check " + input + " " + tfunc() + " FAILED";
0682         };
0683     }
0684 
0685     /// You can pass in as many filter functions as you like, they nest
0686     template <typename T, typename... Args>
0687     CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0688         : CheckedTransformer(
0689               std::forward<T>(mapping),
0690               [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0691               other...) {}
0692 };
0693 
0694 /// Helper function to allow ignore_case to be passed to IsMember or Transform
0695 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
0696 
0697 /// Helper function to allow ignore_underscore to be passed to IsMember or Transform
0698 inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
0699 
0700 /// Helper function to allow checks to ignore spaces to be passed to IsMember or Transform
0701 inline std::string ignore_space(std::string item) {
0702     item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
0703     item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
0704     return item;
0705 }
0706 
0707 /// Multiply a number by a factor using given mapping.
0708 /// Can be used to write transforms for SIZE or DURATION inputs.
0709 ///
0710 /// Example:
0711 ///   With mapping = `{"b"->1, "kb"->1024, "mb"->1024*1024}`
0712 ///   one can recognize inputs like "100", "12kb", "100 MB",
0713 ///   that will be automatically transformed to 100, 14448, 104857600.
0714 ///
0715 /// Output number type matches the type in the provided mapping.
0716 /// Therefore, if it is required to interpret real inputs like "0.42 s",
0717 /// the mapping should be of a type <string, float> or <string, double>.
0718 class AsNumberWithUnit : public Validator {
0719   public:
0720     /// Adjust AsNumberWithUnit behavior.
0721     /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched.
0722     /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError
0723     ///   if UNIT_REQUIRED is set and unit literal is not found.
0724     enum Options {
0725         CASE_SENSITIVE = 0,
0726         CASE_INSENSITIVE = 1,
0727         UNIT_OPTIONAL = 0,
0728         UNIT_REQUIRED = 2,
0729         DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
0730     };
0731 
0732     template <typename Number>
0733     explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
0734                               Options opts = DEFAULT,
0735                               const std::string &unit_name = "UNIT") {
0736         description(generate_description<Number>(unit_name, opts));
0737         validate_mapping(mapping, opts);
0738 
0739         // transform function
0740         func_ = [mapping, opts](std::string &input) -> std::string {
0741             Number num{};
0742 
0743             detail::rtrim(input);
0744             if(input.empty()) {
0745                 throw ValidationError("Input is empty");
0746             }
0747 
0748             // Find split position between number and prefix
0749             auto unit_begin = input.end();
0750             while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
0751                 --unit_begin;
0752             }
0753 
0754             std::string unit{unit_begin, input.end()};
0755             input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
0756             detail::trim(input);
0757 
0758             if(opts & UNIT_REQUIRED && unit.empty()) {
0759                 throw ValidationError("Missing mandatory unit");
0760             }
0761             if(opts & CASE_INSENSITIVE) {
0762                 unit = detail::to_lower(unit);
0763             }
0764             if(unit.empty()) {
0765                 using CLI::detail::lexical_cast;
0766                 if(!lexical_cast(input, num)) {
0767                     throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0768                                           detail::type_name<Number>());
0769                 }
0770                 // No need to modify input if no unit passed
0771                 return {};
0772             }
0773 
0774             // find corresponding factor
0775             auto it = mapping.find(unit);
0776             if(it == mapping.end()) {
0777                 throw ValidationError(unit +
0778                                       " unit not recognized. "
0779                                       "Allowed values: " +
0780                                       detail::generate_map(mapping, true));
0781             }
0782 
0783             if(!input.empty()) {
0784                 using CLI::detail::lexical_cast;
0785                 bool converted = lexical_cast(input, num);
0786                 if(!converted) {
0787                     throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0788                                           detail::type_name<Number>());
0789                 }
0790                 // perform safe multiplication
0791                 bool ok = detail::checked_multiply(num, it->second);
0792                 if(!ok) {
0793                     throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
0794                                           " factor would cause number overflow. Use smaller value.");
0795                 }
0796             } else {
0797                 num = static_cast<Number>(it->second);
0798             }
0799 
0800             input = detail::to_string(num);
0801 
0802             return {};
0803         };
0804     }
0805 
0806   private:
0807     /// Check that mapping contains valid units.
0808     /// Update mapping for CASE_INSENSITIVE mode.
0809     template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
0810         for(auto &kv : mapping) {
0811             if(kv.first.empty()) {
0812                 throw ValidationError("Unit must not be empty.");
0813             }
0814             if(!detail::isalpha(kv.first)) {
0815                 throw ValidationError("Unit must contain only letters.");
0816             }
0817         }
0818 
0819         // make all units lowercase if CASE_INSENSITIVE
0820         if(opts & CASE_INSENSITIVE) {
0821             std::map<std::string, Number> lower_mapping;
0822             for(auto &kv : mapping) {
0823                 auto s = detail::to_lower(kv.first);
0824                 if(lower_mapping.count(s)) {
0825                     throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
0826                                           s);
0827                 }
0828                 lower_mapping[detail::to_lower(kv.first)] = kv.second;
0829             }
0830             mapping = std::move(lower_mapping);
0831         }
0832     }
0833 
0834     /// Generate description like this: NUMBER [UNIT]
0835     template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
0836         std::stringstream out;
0837         out << detail::type_name<Number>() << ' ';
0838         if(opts & UNIT_REQUIRED) {
0839             out << name;
0840         } else {
0841             out << '[' << name << ']';
0842         }
0843         return out.str();
0844     }
0845 };
0846 
0847 inline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a, const AsNumberWithUnit::Options &b) {
0848     return static_cast<AsNumberWithUnit::Options>(static_cast<int>(a) | static_cast<int>(b));
0849 }
0850 
0851 /// Converts a human-readable size string (with unit literal) to uin64_t size.
0852 /// Example:
0853 ///   "100" => 100
0854 ///   "1 b" => 100
0855 ///   "10Kb" => 10240 // you can configure this to be interpreted as kilobyte (*1000) or kibibyte (*1024)
0856 ///   "10 KB" => 10240
0857 ///   "10 kb" => 10240
0858 ///   "10 kib" => 10240 // *i, *ib are always interpreted as *bibyte (*1024)
0859 ///   "10kb" => 10240
0860 ///   "2 MB" => 2097152
0861 ///   "2 EiB" => 2^61 // Units up to exibyte are supported
0862 class AsSizeValue : public AsNumberWithUnit {
0863   public:
0864     using result_t = std::uint64_t;
0865 
0866     /// If kb_is_1000 is true,
0867     /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024
0868     /// (same applies to higher order units as well).
0869     /// Otherwise, interpret all literals as factors of 1024.
0870     /// The first option is formally correct, but
0871     /// the second interpretation is more wide-spread
0872     /// (see https://en.wikipedia.org/wiki/Binary_prefix).
0873     explicit AsSizeValue(bool kb_is_1000);
0874 
0875   private:
0876     /// Get <size unit, factor> mapping
0877     static std::map<std::string, result_t> init_mapping(bool kb_is_1000);
0878 
0879     /// Cache calculated mapping
0880     static std::map<std::string, result_t> get_mapping(bool kb_is_1000);
0881 };
0882 
0883 namespace detail {
0884 /// Split a string into a program name and command line arguments
0885 /// the string is assumed to contain a file name followed by other arguments
0886 /// the return value contains is a pair with the first argument containing the program name and the second
0887 /// everything else.
0888 CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline);
0889 
0890 }  // namespace detail
0891 /// @}
0892 
0893 // [CLI11:validators_hpp:end]
0894 }  // namespace CLI
0895 
0896 #ifndef CLI11_COMPILE
0897 #include "impl/Validators_inl.hpp"  // IWYU pragma: export
0898 #endif