File indexing completed on 2025-07-09 08:29:26
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009
0010
0011 #include "Error.hpp"
0012 #include "Macros.hpp"
0013 #include "StringTools.hpp"
0014 #include "TypeTools.hpp"
0015
0016
0017 #include <cmath>
0018 #include <cstdint>
0019 #include <functional>
0020 #include <iostream>
0021 #include <limits>
0022 #include <map>
0023 #include <memory>
0024 #include <string>
0025 #include <utility>
0026 #include <vector>
0027
0028
0029
0030
0031 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
0032 #include <filesystem> // NOLINT(build/include)
0033 #else
0034 #include <sys/stat.h>
0035 #include <sys/types.h>
0036 #endif
0037
0038
0039
0040 namespace CLI {
0041
0042
0043 class Option;
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055 class Validator {
0056 protected:
0057
0058 std::function<std::string()> desc_function_{[]() { return std::string{}; }};
0059
0060
0061
0062 std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
0063
0064 std::string name_{};
0065
0066 int application_index_ = -1;
0067
0068 bool active_{true};
0069
0070 bool non_modifying_{false};
0071
0072 Validator(std::string validator_desc, std::function<std::string(std::string &)> func)
0073 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(func)) {}
0074
0075 public:
0076 Validator() = default;
0077
0078 explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
0079
0080 Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
0081 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
0082 name_(std::move(validator_name)) {}
0083
0084 Validator &operation(std::function<std::string(std::string &)> op) {
0085 func_ = std::move(op);
0086 return *this;
0087 }
0088
0089
0090 std::string operator()(std::string &str) const;
0091
0092
0093
0094 std::string operator()(const std::string &str) const {
0095 std::string value = str;
0096 return (active_) ? func_(value) : std::string{};
0097 }
0098
0099
0100 Validator &description(std::string validator_desc) {
0101 desc_function_ = [validator_desc]() { return validator_desc; };
0102 return *this;
0103 }
0104
0105 CLI11_NODISCARD Validator description(std::string validator_desc) const;
0106
0107
0108 CLI11_NODISCARD std::string get_description() const {
0109 if(active_) {
0110 return desc_function_();
0111 }
0112 return std::string{};
0113 }
0114
0115 Validator &name(std::string validator_name) {
0116 name_ = std::move(validator_name);
0117 return *this;
0118 }
0119
0120 CLI11_NODISCARD Validator name(std::string validator_name) const {
0121 Validator newval(*this);
0122 newval.name_ = std::move(validator_name);
0123 return newval;
0124 }
0125
0126 CLI11_NODISCARD const std::string &get_name() const { return name_; }
0127
0128 Validator &active(bool active_val = true) {
0129 active_ = active_val;
0130 return *this;
0131 }
0132
0133 CLI11_NODISCARD Validator active(bool active_val = true) const {
0134 Validator newval(*this);
0135 newval.active_ = active_val;
0136 return newval;
0137 }
0138
0139
0140 Validator &non_modifying(bool no_modify = true) {
0141 non_modifying_ = no_modify;
0142 return *this;
0143 }
0144
0145 Validator &application_index(int app_index) {
0146 application_index_ = app_index;
0147 return *this;
0148 }
0149
0150 CLI11_NODISCARD Validator application_index(int app_index) const {
0151 Validator newval(*this);
0152 newval.application_index_ = app_index;
0153 return newval;
0154 }
0155
0156 CLI11_NODISCARD int get_application_index() const { return application_index_; }
0157
0158 CLI11_NODISCARD bool get_active() const { return active_; }
0159
0160
0161 CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; }
0162
0163
0164
0165 Validator operator&(const Validator &other) const;
0166
0167
0168
0169 Validator operator|(const Validator &other) const;
0170
0171
0172 Validator operator!() const;
0173
0174 private:
0175 void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger);
0176 };
0177
0178
0179 class CustomValidator : public Validator {
0180 public:
0181 };
0182
0183
0184
0185 namespace detail {
0186
0187
0188 enum class path_type { nonexistent, file, directory };
0189
0190
0191 CLI11_INLINE path_type check_path(const char *file) noexcept;
0192
0193
0194 class ExistingFileValidator : public Validator {
0195 public:
0196 ExistingFileValidator();
0197 };
0198
0199
0200 class ExistingDirectoryValidator : public Validator {
0201 public:
0202 ExistingDirectoryValidator();
0203 };
0204
0205
0206 class ExistingPathValidator : public Validator {
0207 public:
0208 ExistingPathValidator();
0209 };
0210
0211
0212 class NonexistentPathValidator : public Validator {
0213 public:
0214 NonexistentPathValidator();
0215 };
0216
0217
0218 class IPV4Validator : public Validator {
0219 public:
0220 IPV4Validator();
0221 };
0222
0223 class EscapedStringTransformer : public Validator {
0224 public:
0225 EscapedStringTransformer();
0226 };
0227
0228 }
0229
0230
0231
0232
0233 const detail::ExistingFileValidator ExistingFile;
0234
0235
0236 const detail::ExistingDirectoryValidator ExistingDirectory;
0237
0238
0239 const detail::ExistingPathValidator ExistingPath;
0240
0241
0242 const detail::NonexistentPathValidator NonexistentPath;
0243
0244
0245 const detail::IPV4Validator ValidIPV4;
0246
0247
0248 const detail::EscapedStringTransformer EscapedString;
0249
0250
0251 template <typename DesiredType> class TypeValidator : public Validator {
0252 public:
0253 explicit TypeValidator(const std::string &validator_name)
0254 : Validator(validator_name, [](std::string &input_string) {
0255 using CLI::detail::lexical_cast;
0256 auto val = DesiredType();
0257 if(!lexical_cast(input_string, val)) {
0258 return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
0259 }
0260 return std::string();
0261 }) {}
0262 TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}
0263 };
0264
0265
0266 const TypeValidator<double> Number("NUMBER");
0267
0268
0269
0270 class FileOnDefaultPath : public Validator {
0271 public:
0272 explicit FileOnDefaultPath(std::string default_path, bool enableErrorReturn = true);
0273 };
0274
0275
0276 class Range : public Validator {
0277 public:
0278
0279
0280
0281
0282 template <typename T>
0283 Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) {
0284 if(validator_name.empty()) {
0285 std::stringstream out;
0286 out << detail::type_name<T>() << " in [" << min_val << " - " << max_val << "]";
0287 description(out.str());
0288 }
0289
0290 func_ = [min_val, max_val](std::string &input) {
0291 using CLI::detail::lexical_cast;
0292 T val;
0293 bool converted = lexical_cast(input, val);
0294 if((!converted) || (val < min_val || val > max_val)) {
0295 std::stringstream out;
0296 out << "Value " << input << " not in range [";
0297 out << min_val << " - " << max_val << "]";
0298 return out.str();
0299 }
0300 return std::string{};
0301 };
0302 }
0303
0304
0305 template <typename T>
0306 explicit Range(T max_val, const std::string &validator_name = std::string{})
0307 : Range(static_cast<T>(0), max_val, validator_name) {}
0308 };
0309
0310
0311 const Range NonNegativeNumber((std::numeric_limits<double>::max)(), "NONNEGATIVE");
0312
0313
0314 const Range PositiveNumber((std::numeric_limits<double>::min)(), (std::numeric_limits<double>::max)(), "POSITIVE");
0315
0316
0317 class Bound : public Validator {
0318 public:
0319
0320
0321
0322
0323 template <typename T> Bound(T min_val, T max_val) {
0324 std::stringstream out;
0325 out << detail::type_name<T>() << " bounded to [" << min_val << " - " << max_val << "]";
0326 description(out.str());
0327
0328 func_ = [min_val, max_val](std::string &input) {
0329 using CLI::detail::lexical_cast;
0330 T val;
0331 bool converted = lexical_cast(input, val);
0332 if(!converted) {
0333 return std::string("Value ") + input + " could not be converted";
0334 }
0335 if(val < min_val)
0336 input = detail::to_string(min_val);
0337 else if(val > max_val)
0338 input = detail::to_string(max_val);
0339
0340 return std::string{};
0341 };
0342 }
0343
0344
0345 template <typename T> explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}
0346 };
0347
0348 namespace detail {
0349 template <typename T,
0350 enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0351 auto smart_deref(T value) -> decltype(*value) {
0352 return *value;
0353 }
0354
0355 template <
0356 typename T,
0357 enable_if_t<!is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0358 typename std::remove_reference<T>::type &smart_deref(T &value) {
0359 return value;
0360 }
0361
0362 template <typename T> std::string generate_set(const T &set) {
0363 using element_t = typename detail::element_type<T>::type;
0364 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0365 std::string out(1, '{');
0366 out.append(detail::join(
0367 detail::smart_deref(set),
0368 [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
0369 ","));
0370 out.push_back('}');
0371 return out;
0372 }
0373
0374
0375 template <typename T> std::string generate_map(const T &map, bool key_only = false) {
0376 using element_t = typename detail::element_type<T>::type;
0377 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0378 std::string out(1, '{');
0379 out.append(detail::join(
0380 detail::smart_deref(map),
0381 [key_only](const iteration_type_t &v) {
0382 std::string res{detail::to_string(detail::pair_adaptor<element_t>::first(v))};
0383
0384 if(!key_only) {
0385 res.append("->");
0386 res += detail::to_string(detail::pair_adaptor<element_t>::second(v));
0387 }
0388 return res;
0389 },
0390 ","));
0391 out.push_back('}');
0392 return out;
0393 }
0394
0395 template <typename C, typename V> struct has_find {
0396 template <typename CC, typename VV>
0397 static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
0398 template <typename, typename> static auto test(...) -> decltype(std::false_type());
0399
0400 static const auto value = decltype(test<C, V>(0))::value;
0401 using type = std::integral_constant<bool, value>;
0402 };
0403
0404
0405 template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
0406 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0407 using element_t = typename detail::element_type<T>::type;
0408 auto &setref = detail::smart_deref(set);
0409 auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
0410 return (detail::pair_adaptor<element_t>::first(v) == val);
0411 });
0412 return {(it != std::end(setref)), it};
0413 }
0414
0415
0416 template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
0417 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0418 auto &setref = detail::smart_deref(set);
0419 auto it = setref.find(val);
0420 return {(it != std::end(setref)), it};
0421 }
0422
0423
0424 template <typename T, typename V>
0425 auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
0426 -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0427 using element_t = typename detail::element_type<T>::type;
0428
0429 auto res = search(set, val);
0430 if((res.first) || (!(filter_function))) {
0431 return res;
0432 }
0433
0434 auto &setref = detail::smart_deref(set);
0435 auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
0436 V a{detail::pair_adaptor<element_t>::first(v)};
0437 a = filter_function(a);
0438 return (a == val);
0439 });
0440 return {(it != std::end(setref)), it};
0441 }
0442
0443
0444
0445
0446
0447 template <typename T>
0448 inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
0449 if((a > 0) == (b > 0)) {
0450 return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
0451 }
0452 return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
0453 }
0454
0455 template <typename T>
0456 inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
0457 return ((std::numeric_limits<T>::max)() / a < b);
0458 }
0459
0460
0461 template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
0462 if(a == 0 || b == 0 || a == 1 || b == 1) {
0463 a *= b;
0464 return true;
0465 }
0466 if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
0467 return false;
0468 }
0469 if(overflowCheck(a, b)) {
0470 return false;
0471 }
0472 a *= b;
0473 return true;
0474 }
0475
0476
0477 template <typename T>
0478 typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
0479 T c = a * b;
0480 if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
0481 return false;
0482 }
0483 a = c;
0484 return true;
0485 }
0486
0487 }
0488
0489 class IsMember : public Validator {
0490 public:
0491 using filter_fn_t = std::function<std::string(std::string)>;
0492
0493
0494 template <typename T, typename... Args>
0495 IsMember(std::initializer_list<T> values, Args &&...args)
0496 : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
0497
0498
0499 template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
0500
0501
0502
0503 template <typename T, typename F> explicit IsMember(T set, F filter_function) {
0504
0505
0506
0507 using element_t = typename detail::element_type<T>::type;
0508 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0509
0510 using local_item_t = typename IsMemberType<item_t>::type;
0511
0512
0513
0514 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0515
0516
0517 desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
0518
0519
0520
0521 func_ = [set, filter_fn](std::string &input) {
0522 using CLI::detail::lexical_cast;
0523 local_item_t b;
0524 if(!lexical_cast(input, b)) {
0525 throw ValidationError(input);
0526 }
0527 if(filter_fn) {
0528 b = filter_fn(b);
0529 }
0530 auto res = detail::search(set, b, filter_fn);
0531 if(res.first) {
0532
0533 if(filter_fn) {
0534 input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
0535 }
0536
0537
0538 return std::string{};
0539 }
0540
0541
0542 return input + " not in " + detail::generate_set(detail::smart_deref(set));
0543 };
0544 }
0545
0546
0547 template <typename T, typename... Args>
0548 IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0549 : IsMember(
0550 std::forward<T>(set),
0551 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0552 other...) {}
0553 };
0554
0555
0556 template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
0557
0558
0559 class Transformer : public Validator {
0560 public:
0561 using filter_fn_t = std::function<std::string(std::string)>;
0562
0563
0564 template <typename... Args>
0565 Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
0566 : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0567
0568
0569 template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
0570
0571
0572
0573 template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
0574
0575 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0576 "mapping must produce value pairs");
0577
0578
0579 using element_t = typename detail::element_type<T>::type;
0580 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0581 using local_item_t = typename IsMemberType<item_t>::type;
0582
0583
0584
0585 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0586
0587
0588 desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
0589
0590 func_ = [mapping, filter_fn](std::string &input) {
0591 using CLI::detail::lexical_cast;
0592 local_item_t b;
0593 if(!lexical_cast(input, b)) {
0594 return std::string();
0595
0596 }
0597 if(filter_fn) {
0598 b = filter_fn(b);
0599 }
0600 auto res = detail::search(mapping, b, filter_fn);
0601 if(res.first) {
0602 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0603 }
0604 return std::string{};
0605 };
0606 }
0607
0608
0609 template <typename T, typename... Args>
0610 Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0611 : Transformer(
0612 std::forward<T>(mapping),
0613 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0614 other...) {}
0615 };
0616
0617
0618 class CheckedTransformer : public Validator {
0619 public:
0620 using filter_fn_t = std::function<std::string(std::string)>;
0621
0622
0623 template <typename... Args>
0624 CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
0625 : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0626
0627
0628 template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
0629
0630
0631
0632 template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
0633
0634 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0635 "mapping must produce value pairs");
0636
0637
0638 using element_t = typename detail::element_type<T>::type;
0639 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0640 using local_item_t = typename IsMemberType<item_t>::type;
0641
0642 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0643
0644
0645 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0646
0647 auto tfunc = [mapping]() {
0648 std::string out("value in ");
0649 out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
0650 out += detail::join(
0651 detail::smart_deref(mapping),
0652 [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
0653 ",");
0654 out.push_back('}');
0655 return out;
0656 };
0657
0658 desc_function_ = tfunc;
0659
0660 func_ = [mapping, tfunc, filter_fn](std::string &input) {
0661 using CLI::detail::lexical_cast;
0662 local_item_t b;
0663 bool converted = lexical_cast(input, b);
0664 if(converted) {
0665 if(filter_fn) {
0666 b = filter_fn(b);
0667 }
0668 auto res = detail::search(mapping, b, filter_fn);
0669 if(res.first) {
0670 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0671 return std::string{};
0672 }
0673 }
0674 for(const auto &v : detail::smart_deref(mapping)) {
0675 auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v));
0676 if(output_string == input) {
0677 return std::string();
0678 }
0679 }
0680
0681 return "Check " + input + " " + tfunc() + " FAILED";
0682 };
0683 }
0684
0685
0686 template <typename T, typename... Args>
0687 CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0688 : CheckedTransformer(
0689 std::forward<T>(mapping),
0690 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0691 other...) {}
0692 };
0693
0694
0695 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
0696
0697
0698 inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
0699
0700
0701 inline std::string ignore_space(std::string item) {
0702 item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
0703 item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
0704 return item;
0705 }
0706
0707
0708
0709
0710
0711
0712
0713
0714
0715
0716
0717
0718 class AsNumberWithUnit : public Validator {
0719 public:
0720
0721
0722
0723
0724 enum Options {
0725 CASE_SENSITIVE = 0,
0726 CASE_INSENSITIVE = 1,
0727 UNIT_OPTIONAL = 0,
0728 UNIT_REQUIRED = 2,
0729 DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
0730 };
0731
0732 template <typename Number>
0733 explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
0734 Options opts = DEFAULT,
0735 const std::string &unit_name = "UNIT") {
0736 description(generate_description<Number>(unit_name, opts));
0737 validate_mapping(mapping, opts);
0738
0739
0740 func_ = [mapping, opts](std::string &input) -> std::string {
0741 Number num{};
0742
0743 detail::rtrim(input);
0744 if(input.empty()) {
0745 throw ValidationError("Input is empty");
0746 }
0747
0748
0749 auto unit_begin = input.end();
0750 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
0751 --unit_begin;
0752 }
0753
0754 std::string unit{unit_begin, input.end()};
0755 input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
0756 detail::trim(input);
0757
0758 if(opts & UNIT_REQUIRED && unit.empty()) {
0759 throw ValidationError("Missing mandatory unit");
0760 }
0761 if(opts & CASE_INSENSITIVE) {
0762 unit = detail::to_lower(unit);
0763 }
0764 if(unit.empty()) {
0765 using CLI::detail::lexical_cast;
0766 if(!lexical_cast(input, num)) {
0767 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0768 detail::type_name<Number>());
0769 }
0770
0771 return {};
0772 }
0773
0774
0775 auto it = mapping.find(unit);
0776 if(it == mapping.end()) {
0777 throw ValidationError(unit +
0778 " unit not recognized. "
0779 "Allowed values: " +
0780 detail::generate_map(mapping, true));
0781 }
0782
0783 if(!input.empty()) {
0784 using CLI::detail::lexical_cast;
0785 bool converted = lexical_cast(input, num);
0786 if(!converted) {
0787 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0788 detail::type_name<Number>());
0789 }
0790
0791 bool ok = detail::checked_multiply(num, it->second);
0792 if(!ok) {
0793 throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
0794 " factor would cause number overflow. Use smaller value.");
0795 }
0796 } else {
0797 num = static_cast<Number>(it->second);
0798 }
0799
0800 input = detail::to_string(num);
0801
0802 return {};
0803 };
0804 }
0805
0806 private:
0807
0808
0809 template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
0810 for(auto &kv : mapping) {
0811 if(kv.first.empty()) {
0812 throw ValidationError("Unit must not be empty.");
0813 }
0814 if(!detail::isalpha(kv.first)) {
0815 throw ValidationError("Unit must contain only letters.");
0816 }
0817 }
0818
0819
0820 if(opts & CASE_INSENSITIVE) {
0821 std::map<std::string, Number> lower_mapping;
0822 for(auto &kv : mapping) {
0823 auto s = detail::to_lower(kv.first);
0824 if(lower_mapping.count(s)) {
0825 throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
0826 s);
0827 }
0828 lower_mapping[detail::to_lower(kv.first)] = kv.second;
0829 }
0830 mapping = std::move(lower_mapping);
0831 }
0832 }
0833
0834
0835 template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
0836 std::stringstream out;
0837 out << detail::type_name<Number>() << ' ';
0838 if(opts & UNIT_REQUIRED) {
0839 out << name;
0840 } else {
0841 out << '[' << name << ']';
0842 }
0843 return out.str();
0844 }
0845 };
0846
0847 inline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a, const AsNumberWithUnit::Options &b) {
0848 return static_cast<AsNumberWithUnit::Options>(static_cast<int>(a) | static_cast<int>(b));
0849 }
0850
0851
0852
0853
0854
0855
0856
0857
0858
0859
0860
0861
0862 class AsSizeValue : public AsNumberWithUnit {
0863 public:
0864 using result_t = std::uint64_t;
0865
0866
0867
0868
0869
0870
0871
0872
0873 explicit AsSizeValue(bool kb_is_1000);
0874
0875 private:
0876
0877 static std::map<std::string, result_t> init_mapping(bool kb_is_1000);
0878
0879
0880 static std::map<std::string, result_t> get_mapping(bool kb_is_1000);
0881 };
0882
0883 namespace detail {
0884
0885
0886
0887
0888 CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline);
0889
0890 }
0891
0892
0893
0894 }
0895
0896 #ifndef CLI11_COMPILE
0897 #include "impl/Validators_inl.hpp" // IWYU pragma: export
0898 #endif