Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:45

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