Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-06-01 07:06:11

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