File indexing completed on 2025-01-18 09:54:45
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "Error.hpp"
0010 #include "Macros.hpp"
0011 #include "StringTools.hpp"
0012 #include "TypeTools.hpp"
0013
0014
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
0026
0027
0028
0029
0030
0031 #if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
0032 #if __has_include(<filesystem>)
0033
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
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
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
0065
0066 namespace CLI {
0067
0068
0069 class Option;
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081 class Validator {
0082 protected:
0083
0084 std::function<std::string()> desc_function_{[]() { return std::string{}; }};
0085
0086
0087
0088 std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
0089
0090 std::string name_{};
0091
0092 int application_index_ = -1;
0093
0094 bool active_{true};
0095
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
0104 explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
0105
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
0110 Validator &operation(std::function<std::string(std::string &)> op) {
0111 func_ = std::move(op);
0112 return *this;
0113 }
0114
0115
0116 std::string operator()(std::string &str) const;
0117
0118
0119
0120 std::string operator()(const std::string &str) const {
0121 std::string value = str;
0122 return (active_) ? func_(value) : std::string{};
0123 }
0124
0125
0126 Validator &description(std::string validator_desc) {
0127 desc_function_ = [validator_desc]() { return validator_desc; };
0128 return *this;
0129 }
0130
0131 CLI11_NODISCARD Validator description(std::string validator_desc) const;
0132
0133
0134 CLI11_NODISCARD std::string get_description() const {
0135 if(active_) {
0136 return desc_function_();
0137 }
0138 return std::string{};
0139 }
0140
0141 Validator &name(std::string validator_name) {
0142 name_ = std::move(validator_name);
0143 return *this;
0144 }
0145
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
0152 CLI11_NODISCARD const std::string &get_name() const { return name_; }
0153
0154 Validator &active(bool active_val = true) {
0155 active_ = active_val;
0156 return *this;
0157 }
0158
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
0166 Validator &non_modifying(bool no_modify = true) {
0167 non_modifying_ = no_modify;
0168 return *this;
0169 }
0170
0171 Validator &application_index(int app_index) {
0172 application_index_ = app_index;
0173 return *this;
0174 }
0175
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
0182 CLI11_NODISCARD int get_application_index() const { return application_index_; }
0183
0184 CLI11_NODISCARD bool get_active() const { return active_; }
0185
0186
0187 CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; }
0188
0189
0190
0191 Validator operator&(const Validator &other) const;
0192
0193
0194
0195 Validator operator|(const Validator &other) const;
0196
0197
0198 Validator operator!() const;
0199
0200 private:
0201 void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger);
0202 };
0203
0204
0205 class CustomValidator : public Validator {
0206 public:
0207 };
0208
0209
0210
0211 namespace detail {
0212
0213
0214 enum class path_type { nonexistent, file, directory };
0215
0216
0217 CLI11_INLINE path_type check_path(const char *file) noexcept;
0218
0219
0220 class ExistingFileValidator : public Validator {
0221 public:
0222 ExistingFileValidator();
0223 };
0224
0225
0226 class ExistingDirectoryValidator : public Validator {
0227 public:
0228 ExistingDirectoryValidator();
0229 };
0230
0231
0232 class ExistingPathValidator : public Validator {
0233 public:
0234 ExistingPathValidator();
0235 };
0236
0237
0238 class NonexistentPathValidator : public Validator {
0239 public:
0240 NonexistentPathValidator();
0241 };
0242
0243
0244 class IPV4Validator : public Validator {
0245 public:
0246 IPV4Validator();
0247 };
0248
0249 }
0250
0251
0252
0253
0254 const detail::ExistingFileValidator ExistingFile;
0255
0256
0257 const detail::ExistingDirectoryValidator ExistingDirectory;
0258
0259
0260 const detail::ExistingPathValidator ExistingPath;
0261
0262
0263 const detail::NonexistentPathValidator NonexistentPath;
0264
0265
0266 const detail::IPV4Validator ValidIPV4;
0267
0268
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
0284 const TypeValidator<double> Number("NUMBER");
0285
0286
0287
0288 class FileOnDefaultPath : public Validator {
0289 public:
0290 explicit FileOnDefaultPath(std::string default_path, bool enableErrorReturn = true);
0291 };
0292
0293
0294 class Range : public Validator {
0295 public:
0296
0297
0298
0299
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
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
0329 const Range NonNegativeNumber((std::numeric_limits<double>::max)(), "NONNEGATIVE");
0330
0331
0332 const Range PositiveNumber((std::numeric_limits<double>::min)(), (std::numeric_limits<double>::max)(), "POSITIVE");
0333
0334
0335 class Bound : public Validator {
0336 public:
0337
0338
0339
0340
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
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
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;
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
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;
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
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
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
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
0447 auto res = search(set, val);
0448 if((res.first) || (!(filter_function))) {
0449 return res;
0450 }
0451
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
0462
0463
0464
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
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
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
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 }
0506
0507 class IsMember : public Validator {
0508 public:
0509 using filter_fn_t = std::function<std::string(std::string)>;
0510
0511
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
0517 template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
0518
0519
0520
0521 template <typename T, typename F> explicit IsMember(T set, F filter_function) {
0522
0523
0524
0525 using element_t = typename detail::element_type<T>::type;
0526 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0527
0528 using local_item_t = typename IsMemberType<item_t>::type;
0529
0530
0531
0532 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0533
0534
0535 desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
0536
0537
0538
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);
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
0551 if(filter_fn) {
0552 input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
0553 }
0554
0555
0556 return std::string{};
0557 }
0558
0559
0560 return input + " not in " + detail::generate_set(detail::smart_deref(set));
0561 };
0562 }
0563
0564
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
0574 template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
0575
0576
0577 class Transformer : public Validator {
0578 public:
0579 using filter_fn_t = std::function<std::string(std::string)>;
0580
0581
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
0587 template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
0588
0589
0590
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
0596
0597 using element_t = typename detail::element_type<T>::type;
0598 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0599 using local_item_t = typename IsMemberType<item_t>::type;
0600
0601
0602
0603 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0604
0605
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
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
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
0636 class CheckedTransformer : public Validator {
0637 public:
0638 using filter_fn_t = std::function<std::string(std::string)>;
0639
0640
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
0646 template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
0647
0648
0649
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
0655
0656 using element_t = typename detail::element_type<T>::type;
0657 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0658 using local_item_t = typename IsMemberType<item_t>::type;
0659
0660 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0661
0662
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
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
0713 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
0714
0715
0716 inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
0717
0718
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
0726
0727
0728
0729
0730
0731
0732
0733
0734
0735
0736 class AsNumberWithUnit : public Validator {
0737 public:
0738
0739
0740
0741
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
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
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
0789 return {};
0790 }
0791
0792
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
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
0826
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
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
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
0870
0871
0872
0873
0874
0875
0876
0877
0878
0879
0880 class AsSizeValue : public AsNumberWithUnit {
0881 public:
0882 using result_t = std::uint64_t;
0883
0884
0885
0886
0887
0888
0889
0890
0891 explicit AsSizeValue(bool kb_is_1000);
0892
0893 private:
0894
0895 static std::map<std::string, result_t> init_mapping(bool kb_is_1000);
0896
0897
0898 static std::map<std::string, result_t> get_mapping(bool kb_is_1000);
0899 };
0900
0901 namespace detail {
0902
0903
0904
0905
0906 CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline);
0907
0908 }
0909
0910
0911
0912 }
0913
0914 #ifndef CLI11_COMPILE
0915 #include "impl/Validators_inl.hpp"
0916 #endif