CLI11 2.1.2
Validators.hpp
Go to the documentation of this file.
1// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
2// under NSF AWARD 1414736 and by the respective contributors.
3// All rights reserved.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7#pragma once
8
9#include "Macros.hpp"
10#include "StringTools.hpp"
11#include "TypeTools.hpp"
12
13// [CLI11:public_includes:set]
14#include <cmath>
15#include <cstdint>
16#include <functional>
17#include <iostream>
18#include <limits>
19#include <map>
20#include <memory>
21#include <string>
22#include <utility>
23#include <vector>
24// [CLI11:public_includes:end]
25
26// [CLI11:validators_hpp_filesystem:verbatim]
27
28// C standard library
29// Only needed for existence checking
30#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
31#if __has_include(<filesystem>)
32// Filesystem cannot be used if targeting macOS < 10.15
33#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
34#define CLI11_HAS_FILESYSTEM 0
35#else
36#include <filesystem>
37#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
38#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
39#define CLI11_HAS_FILESYSTEM 1
40#elif defined(__GLIBCXX__)
41// if we are using gcc and Version <9 default to no filesystem
42#define CLI11_HAS_FILESYSTEM 0
43#else
44#define CLI11_HAS_FILESYSTEM 1
45#endif
46#else
47#define CLI11_HAS_FILESYSTEM 0
48#endif
49#endif
50#endif
51#endif
52
53#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
54#include <filesystem> // NOLINT(build/include)
55#else
56#include <sys/stat.h>
57#include <sys/types.h>
58#endif
59
60// [CLI11:validators_hpp_filesystem:end]
61
62namespace CLI {
63// [CLI11:validators_hpp:verbatim]
64
65class Option;
66
68
75
77class Validator {
78 protected:
80 std::function<std::string()> desc_function_{[]() { return std::string{}; }};
81
84 std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
86 std::string name_{};
90 bool active_{true};
92 bool non_modifying_{false};
93
94 public:
95 Validator() = default;
97 explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
99 Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
100 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
101 name_(std::move(validator_name)) {}
103 Validator &operation(std::function<std::string(std::string &)> op) {
104 func_ = std::move(op);
105 return *this;
106 }
109 std::string operator()(std::string &str) const {
110 std::string retstring;
111 if(active_) {
112 if(non_modifying_) {
113 std::string value = str;
114 retstring = func_(value);
115 } else {
116 retstring = func_(str);
117 }
118 }
119 return retstring;
120 }
121
124 std::string operator()(const std::string &str) const {
125 std::string value = str;
126 return (active_) ? func_(value) : std::string{};
127 }
128
130 Validator &description(std::string validator_desc) {
131 desc_function_ = [validator_desc]() { return validator_desc; };
132 return *this;
133 }
135 Validator description(std::string validator_desc) const {
136 Validator newval(*this);
137 newval.desc_function_ = [validator_desc]() { return validator_desc; };
138 return newval;
139 }
141 std::string get_description() const {
142 if(active_) {
143 return desc_function_();
144 }
145 return std::string{};
146 }
148 Validator &name(std::string validator_name) {
149 name_ = std::move(validator_name);
150 return *this;
151 }
153 Validator name(std::string validator_name) const {
154 Validator newval(*this);
155 newval.name_ = std::move(validator_name);
156 return newval;
157 }
159 const std::string &get_name() const { return name_; }
161 Validator &active(bool active_val = true) {
162 active_ = active_val;
163 return *this;
164 }
166 Validator active(bool active_val = true) const {
167 Validator newval(*this);
168 newval.active_ = active_val;
169 return newval;
170 }
171
173 Validator &non_modifying(bool no_modify = true) {
174 non_modifying_ = no_modify;
175 return *this;
176 }
178 Validator &application_index(int app_index) {
179 application_index_ = app_index;
180 return *this;
181 }
183 Validator application_index(int app_index) const {
184 Validator newval(*this);
185 newval.application_index_ = app_index;
186 return newval;
187 }
191 bool get_active() const { return active_; }
192
194 bool get_modifying() const { return !non_modifying_; }
195
198 Validator operator&(const Validator &other) const {
199 Validator newval;
200
201 newval._merge_description(*this, other, " AND ");
202
203 // Give references (will make a copy in lambda function)
204 const std::function<std::string(std::string & filename)> &f1 = func_;
205 const std::function<std::string(std::string & filename)> &f2 = other.func_;
206
207 newval.func_ = [f1, f2](std::string &input) {
208 std::string s1 = f1(input);
209 std::string s2 = f2(input);
210 if(!s1.empty() && !s2.empty())
211 return std::string("(") + s1 + ") AND (" + s2 + ")";
212 else
213 return s1 + s2;
214 };
215
216 newval.active_ = (active_ & other.active_);
218 return newval;
219 }
220
223 Validator operator|(const Validator &other) const {
224 Validator newval;
225
226 newval._merge_description(*this, other, " OR ");
227
228 // Give references (will make a copy in lambda function)
229 const std::function<std::string(std::string &)> &f1 = func_;
230 const std::function<std::string(std::string &)> &f2 = other.func_;
231
232 newval.func_ = [f1, f2](std::string &input) {
233 std::string s1 = f1(input);
234 std::string s2 = f2(input);
235 if(s1.empty() || s2.empty())
236 return std::string();
237
238 return std::string("(") + s1 + ") OR (" + s2 + ")";
239 };
240 newval.active_ = (active_ & other.active_);
242 return newval;
243 }
244
247 Validator newval;
248 const std::function<std::string()> &dfunc1 = desc_function_;
249 newval.desc_function_ = [dfunc1]() {
250 auto str = dfunc1();
251 return (!str.empty()) ? std::string("NOT ") + str : std::string{};
252 };
253 // Give references (will make a copy in lambda function)
254 const std::function<std::string(std::string & res)> &f1 = func_;
255
256 newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
257 std::string s1 = f1(test);
258 if(s1.empty()) {
259 return std::string("check ") + dfunc1() + " succeeded improperly";
260 }
261 return std::string{};
262 };
263 newval.active_ = active_;
265 return newval;
266 }
267
268 private:
269 void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
270
271 const std::function<std::string()> &dfunc1 = val1.desc_function_;
272 const std::function<std::string()> &dfunc2 = val2.desc_function_;
273
274 desc_function_ = [=]() {
275 std::string f1 = dfunc1();
276 std::string f2 = dfunc2();
277 if((f1.empty()) || (f2.empty())) {
278 return f1 + f2;
279 }
280 return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
281 };
282 }
283}; // namespace CLI
284
287 public:
288};
289// The implementation of the built in validators is using the Validator class;
290// the user is only expected to use the const (static) versions (since there's no setup).
291// Therefore, this is in detail.
292namespace detail {
293
296
297#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
299inline path_type check_path(const char *file) noexcept {
300 std::error_code ec;
301 auto stat = std::filesystem::status(file, ec);
302 if(ec) {
304 }
305 switch(stat.type()) {
306 case std::filesystem::file_type::none:
307 case std::filesystem::file_type::not_found:
309 case std::filesystem::file_type::directory:
311 case std::filesystem::file_type::symlink:
312 case std::filesystem::file_type::block:
313 case std::filesystem::file_type::character:
314 case std::filesystem::file_type::fifo:
315 case std::filesystem::file_type::socket:
316 case std::filesystem::file_type::regular:
317 case std::filesystem::file_type::unknown:
318 default:
319 return path_type::file;
320 }
321}
322#else
324inline path_type check_path(const char *file) noexcept {
325#if defined(_MSC_VER)
326 struct __stat64 buffer;
327 if(_stat64(file, &buffer) == 0) {
328 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
329 }
330#else
331 struct stat buffer;
332 if(stat(file, &buffer) == 0) {
333 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
334 }
335#endif
337}
338#endif
341 public:
343 func_ = [](std::string &filename) {
344 auto path_result = check_path(filename.c_str());
345 if(path_result == path_type::nonexistent) {
346 return "File does not exist: " + filename;
347 }
348 if(path_result == path_type::directory) {
349 return "File is actually a directory: " + filename;
350 }
351 return std::string();
352 };
353 }
354};
355
358 public:
360 func_ = [](std::string &filename) {
361 auto path_result = check_path(filename.c_str());
362 if(path_result == path_type::nonexistent) {
363 return "Directory does not exist: " + filename;
364 }
365 if(path_result == path_type::file) {
366 return "Directory is actually a file: " + filename;
367 }
368 return std::string();
369 };
370 }
371};
372
375 public:
376 ExistingPathValidator() : Validator("PATH(existing)") {
377 func_ = [](std::string &filename) {
378 auto path_result = check_path(filename.c_str());
379 if(path_result == path_type::nonexistent) {
380 return "Path does not exist: " + filename;
381 }
382 return std::string();
383 };
384 }
385};
386
389 public:
390 NonexistentPathValidator() : Validator("PATH(non-existing)") {
391 func_ = [](std::string &filename) {
392 auto path_result = check_path(filename.c_str());
393 if(path_result != path_type::nonexistent) {
394 return "Path already exists: " + filename;
395 }
396 return std::string();
397 };
398 }
399};
400
402class IPV4Validator : public Validator {
403 public:
405 func_ = [](std::string &ip_addr) {
406 auto result = CLI::detail::split(ip_addr, '.');
407 if(result.size() != 4) {
408 return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
409 }
410 int num;
411 for(const auto &var : result) {
412 bool retval = detail::lexical_cast(var, num);
413 if(!retval) {
414 return std::string("Failed parsing number (") + var + ')';
415 }
416 if(num < 0 || num > 255) {
417 return std::string("Each IP number must be between 0 and 255 ") + var;
418 }
419 }
420 return std::string();
421 };
422 }
423};
424
425} // namespace detail
426
427// Static is not needed here, because global const implies static.
428
431
434
437
440
443
445template <typename DesiredType> class TypeValidator : public Validator {
446 public:
447 explicit TypeValidator(const std::string &validator_name) : Validator(validator_name) {
448 func_ = [](std::string &input_string) {
449 auto val = DesiredType();
450 if(!detail::lexical_cast(input_string, val)) {
451 return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
452 }
453 return std::string();
454 };
455 }
456 TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}
457};
458
461
463class Range : public Validator {
464 public:
469 template <typename T>
470 Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) {
471 if(validator_name.empty()) {
472 std::stringstream out;
473 out << detail::type_name<T>() << " in [" << min_val << " - " << max_val << "]";
474 description(out.str());
475 }
476
477 func_ = [min_val, max_val](std::string &input) {
478 T val;
479 bool converted = detail::lexical_cast(input, val);
480 if((!converted) || (val < min_val || val > max_val))
481 return std::string("Value ") + input + " not in range " + std::to_string(min_val) + " to " +
482 std::to_string(max_val);
483
484 return std::string{};
485 };
486 }
487
489 template <typename T>
490 explicit Range(T max_val, const std::string &validator_name = std::string{})
491 : Range(static_cast<T>(0), max_val, validator_name) {}
492};
493
495const Range NonNegativeNumber((std::numeric_limits<double>::max)(), "NONNEGATIVE");
496
498const Range PositiveNumber((std::numeric_limits<double>::min)(), (std::numeric_limits<double>::max)(), "POSITIVE");
499
501class Bound : public Validator {
502 public:
507 template <typename T> Bound(T min_val, T max_val) {
508 std::stringstream out;
509 out << detail::type_name<T>() << " bounded to [" << min_val << " - " << max_val << "]";
510 description(out.str());
511
512 func_ = [min_val, max_val](std::string &input) {
513 T val;
514 bool converted = detail::lexical_cast(input, val);
515 if(!converted) {
516 return std::string("Value ") + input + " could not be converted";
517 }
518 if(val < min_val)
519 input = detail::to_string(min_val);
520 else if(val > max_val)
521 input = detail::to_string(max_val);
522
523 return std::string{};
524 };
525 }
526
528 template <typename T> explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}
529};
530
531namespace detail {
532template <typename T,
533 enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
534auto smart_deref(T value) -> decltype(*value) {
535 return *value;
536}
537
538template <
539 typename T,
541typename std::remove_reference<T>::type &smart_deref(T &value) {
542 return value;
543}
545template <typename T> std::string generate_set(const T &set) {
546 using element_t = typename detail::element_type<T>::type;
547 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
548 std::string out(1, '{');
549 out.append(detail::join(
551 [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
552 ","));
553 out.push_back('}');
554 return out;
555}
556
558template <typename T> std::string generate_map(const T &map, bool key_only = false) {
559 using element_t = typename detail::element_type<T>::type;
560 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
561 std::string out(1, '{');
562 out.append(detail::join(
564 [key_only](const iteration_type_t &v) {
566
567 if(!key_only) {
568 res.append("->");
570 }
571 return res;
572 },
573 ","));
574 out.push_back('}');
575 return out;
576}
577
578template <typename C, typename V> struct has_find {
579 template <typename CC, typename VV>
580 static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
581 template <typename, typename> static auto test(...) -> decltype(std::false_type());
582
583 static const auto value = decltype(test<C, V>(0))::value;
584 using type = std::integral_constant<bool, value>;
585};
586
588template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
589auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
590 using element_t = typename detail::element_type<T>::type;
591 auto &setref = detail::smart_deref(set);
592 auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
594 });
595 return {(it != std::end(setref)), it};
596}
597
599template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
600auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
601 auto &setref = detail::smart_deref(set);
602 auto it = setref.find(val);
603 return {(it != std::end(setref)), it};
604}
605
607template <typename T, typename V>
608auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
609 -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
610 using element_t = typename detail::element_type<T>::type;
611 // do the potentially faster first search
612 auto res = search(set, val);
613 if((res.first) || (!(filter_function))) {
614 return res;
615 }
616 // if we haven't found it do the longer linear search with all the element translations
617 auto &setref = detail::smart_deref(set);
618 auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
620 a = filter_function(a);
621 return (a == val);
622 });
623 return {(it != std::end(setref)), it};
624}
625
626// the following suggestion was made by Nikita Ofitserov(@himikof)
627// done in templates to prevent compiler warnings on negation of unsigned numbers
628
630template <typename T>
631inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
632 if((a > 0) == (b > 0)) {
633 return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
634 } else {
635 return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
636 }
637}
639template <typename T>
640inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
641 return ((std::numeric_limits<T>::max)() / a < b);
642}
643
645template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
646 if(a == 0 || b == 0 || a == 1 || b == 1) {
647 a *= b;
648 return true;
649 }
650 if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
651 return false;
652 }
653 if(overflowCheck(a, b)) {
654 return false;
655 }
656 a *= b;
657 return true;
658}
659
661template <typename T>
662typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
663 T c = a * b;
664 if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
665 return false;
666 }
667 a = c;
668 return true;
669}
670
671} // namespace detail
673class IsMember : public Validator {
674 public:
675 using filter_fn_t = std::function<std::string(std::string)>;
676
678 template <typename T, typename... Args>
679 IsMember(std::initializer_list<T> values, Args &&...args)
680 : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
681
683 template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
684
687 template <typename T, typename F> explicit IsMember(T set, F filter_function) {
688
689 // Get the type of the contained item - requires a container have ::value_type
690 // if the type does not have first_type and second_type, these are both value_type
691 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
692 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
693
694 using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
695 // (const char * to std::string)
696
697 // Make a local copy of the filter function, using a std::function if not one already
698 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
699
700 // This is the type name for help, it will take the current version of the set contents
701 desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
702
703 // This is the function that validates
704 // It stores a copy of the set pointer-like, so shared_ptr will stay alive
705 func_ = [set, filter_fn](std::string &input) {
706 local_item_t b;
707 if(!detail::lexical_cast(input, b)) {
708 throw ValidationError(input); // name is added later
709 }
710 if(filter_fn) {
711 b = filter_fn(b);
712 }
713 auto res = detail::search(set, b, filter_fn);
714 if(res.first) {
715 // Make sure the version in the input string is identical to the one in the set
716 if(filter_fn) {
718 }
719
720 // Return empty error string (success)
721 return std::string{};
722 }
723
724 // If you reach this point, the result was not found
725 return input + " not in " + detail::generate_set(detail::smart_deref(set));
726 };
727 }
728
730 template <typename T, typename... Args>
731 IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
732 : IsMember(
733 std::forward<T>(set),
734 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
735 other...) {}
736};
737
739template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
740
742class Transformer : public Validator {
743 public:
744 using filter_fn_t = std::function<std::string(std::string)>;
745
747 template <typename... Args>
748 Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
749 : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
750
752 template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
753
756 template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
757
759 "mapping must produce value pairs");
760 // Get the type of the contained item - requires a container have ::value_type
761 // if the type does not have first_type and second_type, these are both value_type
762 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
763 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
764 using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
765 // (const char * to std::string)
766
767 // Make a local copy of the filter function, using a std::function if not one already
768 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
769
770 // This is the type name for help, it will take the current version of the set contents
771 desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
772
773 func_ = [mapping, filter_fn](std::string &input) {
774 local_item_t b;
775 if(!detail::lexical_cast(input, b)) {
776 return std::string();
777 // there is no possible way we can match anything in the mapping if we can't convert so just return
778 }
779 if(filter_fn) {
780 b = filter_fn(b);
781 }
782 auto res = detail::search(mapping, b, filter_fn);
783 if(res.first) {
785 }
786 return std::string{};
787 };
788 }
789
791 template <typename T, typename... Args>
792 Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
793 : Transformer(
794 std::forward<T>(mapping),
795 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
796 other...) {}
797};
798
801 public:
802 using filter_fn_t = std::function<std::string(std::string)>;
803
805 template <typename... Args>
806 CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
807 : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
808
810 template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
811
814 template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
815
817 "mapping must produce value pairs");
818 // Get the type of the contained item - requires a container have ::value_type
819 // if the type does not have first_type and second_type, these are both value_type
820 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
821 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
822 using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
823 // (const char * to std::string)
824 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
825
826 // Make a local copy of the filter function, using a std::function if not one already
827 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
828
829 auto tfunc = [mapping]() {
830 std::string out("value in ");
831 out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
832 out += detail::join(
833 detail::smart_deref(mapping),
834 [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
835 ",");
836 out.push_back('}');
837 return out;
838 };
839
840 desc_function_ = tfunc;
841
842 func_ = [mapping, tfunc, filter_fn](std::string &input) {
843 local_item_t b;
844 bool converted = detail::lexical_cast(input, b);
845 if(converted) {
846 if(filter_fn) {
847 b = filter_fn(b);
848 }
849 auto res = detail::search(mapping, b, filter_fn);
850 if(res.first) {
852 return std::string{};
853 }
854 }
855 for(const auto &v : detail::smart_deref(mapping)) {
857 if(output_string == input) {
858 return std::string();
859 }
860 }
861
862 return "Check " + input + " " + tfunc() + " FAILED";
863 };
864 }
865
867 template <typename T, typename... Args>
868 CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
870 std::forward<T>(mapping),
871 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
872 other...) {}
873};
874
876inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
877
879inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
880
882inline std::string ignore_space(std::string item) {
883 item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
884 item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
885 return item;
886}
887
900 public:
905 enum Options {
911 };
912
913 template <typename Number>
914 explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
915 Options opts = DEFAULT,
916 const std::string &unit_name = "UNIT") {
917 description(generate_description<Number>(unit_name, opts));
918 validate_mapping(mapping, opts);
919
920 // transform function
921 func_ = [mapping, opts](std::string &input) -> std::string {
922 Number num;
923
924 detail::rtrim(input);
925 if(input.empty()) {
926 throw ValidationError("Input is empty");
927 }
928
929 // Find split position between number and prefix
930 auto unit_begin = input.end();
931 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
932 --unit_begin;
933 }
934
935 std::string unit{unit_begin, input.end()};
936 input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
937 detail::trim(input);
938
939 if(opts & UNIT_REQUIRED && unit.empty()) {
940 throw ValidationError("Missing mandatory unit");
941 }
942 if(opts & CASE_INSENSITIVE) {
943 unit = detail::to_lower(unit);
944 }
945 if(unit.empty()) {
946 if(!detail::lexical_cast(input, num)) {
947 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
948 detail::type_name<Number>());
949 }
950 // No need to modify input if no unit passed
951 return {};
952 }
953
954 // find corresponding factor
955 auto it = mapping.find(unit);
956 if(it == mapping.end()) {
957 throw ValidationError(unit +
958 " unit not recognized. "
959 "Allowed values: " +
960 detail::generate_map(mapping, true));
961 }
962
963 if(!input.empty()) {
964 bool converted = detail::lexical_cast(input, num);
965 if(!converted) {
966 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
967 detail::type_name<Number>());
968 }
969 // perform safe multiplication
970 bool ok = detail::checked_multiply(num, it->second);
971 if(!ok) {
972 throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
973 " factor would cause number overflow. Use smaller value.");
974 }
975 } else {
976 num = static_cast<Number>(it->second);
977 }
978
979 input = detail::to_string(num);
980
981 return {};
982 };
983 }
984
985 private:
988 template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
989 for(auto &kv : mapping) {
990 if(kv.first.empty()) {
991 throw ValidationError("Unit must not be empty.");
992 }
993 if(!detail::isalpha(kv.first)) {
994 throw ValidationError("Unit must contain only letters.");
995 }
996 }
997
998 // make all units lowercase if CASE_INSENSITIVE
999 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
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
1038 public:
1039 using result_t = std::uint64_t;
1040
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:
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
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
1088namespace detail {
1093inline 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 }
1117 } else {
1118 esp = commandline.find_first_of(' ', 1);
1119 }
1120 } else {
1121 esp = commandline.find_first_of(' ', 1);
1122 }
1123
1124 break;
1125 }
1126 }
1127 if(vals.first.empty()) {
1128 vals.first = commandline.substr(0, esp);
1129 rtrim(vals.first);
1130 }
1131
1132 // strip the program name
1133 vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
1134 ltrim(vals.second);
1135 return vals;
1136}
1137
1138} // namespace detail
1140
1141// [CLI11:validators_hpp:end]
1142} // namespace CLI
Definition: Validators.hpp:899
Options
Definition: Validators.hpp:905
@ UNIT_OPTIONAL
Definition: Validators.hpp:908
@ CASE_INSENSITIVE
Definition: Validators.hpp:907
@ DEFAULT
Definition: Validators.hpp:910
@ UNIT_REQUIRED
Definition: Validators.hpp:909
@ CASE_SENSITIVE
Definition: Validators.hpp:906
AsNumberWithUnit(std::map< std::string, Number > mapping, Options opts=DEFAULT, const std::string &unit_name="UNIT")
Definition: Validators.hpp:914
Definition: Validators.hpp:1037
AsSizeValue(bool kb_is_1000)
Definition: Validators.hpp:1048
std::uint64_t result_t
Definition: Validators.hpp:1039
Produce a bounded range (factory). Min and max are inclusive.
Definition: Validators.hpp:501
Bound(T min_val, T max_val)
Definition: Validators.hpp:507
Bound(T max_val)
Range of one value is 0 to value.
Definition: Validators.hpp:528
translate named items to other or a value set
Definition: Validators.hpp:800
CheckedTransformer(T mapping)
direct map of std::string to std::string
Definition: Validators.hpp:810
CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
You can pass in as many filter functions as you like, they nest.
Definition: Validators.hpp:868
std::function< std::string(std::string)> filter_fn_t
Definition: Validators.hpp:802
CheckedTransformer(std::initializer_list< std::pair< std::string, std::string > > values, Args &&...args)
This allows in-place construction.
Definition: Validators.hpp:806
CheckedTransformer(T mapping, F filter_function)
Definition: Validators.hpp:814
Class wrapping some of the accessors of Validator.
Definition: Validators.hpp:286
Verify items are in a set.
Definition: Validators.hpp:673
IsMember(T &&set)
This checks to see if an item is in a set (empty function)
Definition: Validators.hpp:683
IsMember(T set, F filter_function)
Definition: Validators.hpp:687
IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
You can pass in as many filter functions as you like, they nest (string only currently)
Definition: Validators.hpp:731
IsMember(std::initializer_list< T > values, Args &&...args)
This allows in-place construction using an initializer list.
Definition: Validators.hpp:679
std::function< std::string(std::string)> filter_fn_t
Definition: Validators.hpp:675
Produce a range (factory). Min and max are inclusive.
Definition: Validators.hpp:463
Range(T min_val, T max_val, const std::string &validator_name=std::string{})
Definition: Validators.hpp:470
Range(T max_val, const std::string &validator_name=std::string{})
Range of one value is 0 to value.
Definition: Validators.hpp:490
Translate named items to other or a value set.
Definition: Validators.hpp:742
Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
You can pass in as many filter functions as you like, they nest.
Definition: Validators.hpp:792
Transformer(std::initializer_list< std::pair< std::string, std::string > > values, Args &&...args)
This allows in-place construction.
Definition: Validators.hpp:748
std::function< std::string(std::string)> filter_fn_t
Definition: Validators.hpp:744
Transformer(T &&mapping)
direct map of std::string to std::string
Definition: Validators.hpp:752
Transformer(T mapping, F filter_function)
Definition: Validators.hpp:756
Validate the input as a particular type.
Definition: Validators.hpp:445
TypeValidator()
Definition: Validators.hpp:456
TypeValidator(const std::string &validator_name)
Definition: Validators.hpp:447
Thrown when validation of results fails.
Definition: Error.hpp:212
Some validators that are provided.
Definition: Validators.hpp:77
bool get_active() const
Get a boolean if the validator is active.
Definition: Validators.hpp:191
int application_index_
A Validator will only apply to an indexed value (-1 is all elements)
Definition: Validators.hpp:88
Validator & non_modifying(bool no_modify=true)
Specify whether the Validator can be modifying or not.
Definition: Validators.hpp:173
Validator & description(std::string validator_desc)
Specify the type string.
Definition: Validators.hpp:130
std::string operator()(const std::string &str) const
Definition: Validators.hpp:124
Validator operator&(const Validator &other) const
Definition: Validators.hpp:198
bool active_
Enable for Validator to allow it to be disabled if need be.
Definition: Validators.hpp:90
Validator name(std::string validator_name) const
Specify the type string.
Definition: Validators.hpp:153
Validator application_index(int app_index) const
Specify the application index of a validator.
Definition: Validators.hpp:183
bool non_modifying_
specify that a validator should not modify the input
Definition: Validators.hpp:92
const std::string & get_name() const
Get the name of the Validator.
Definition: Validators.hpp:159
Validator(std::string validator_desc)
Construct a Validator with just the description string.
Definition: Validators.hpp:97
std::string get_description() const
Generate type description information for the Validator.
Definition: Validators.hpp:141
Validator & name(std::string validator_name)
Specify the type string.
Definition: Validators.hpp:148
Validator()=default
std::string operator()(std::string &str) const
Definition: Validators.hpp:109
Validator active(bool active_val=true) const
Specify whether the Validator is active or not.
Definition: Validators.hpp:166
std::function< std::string()> desc_function_
This is the description function, if empty the description_ will be used.
Definition: Validators.hpp:80
int get_application_index() const
Get the current value of the application index.
Definition: Validators.hpp:189
std::function< std::string(std::string &)> func_
Definition: Validators.hpp:84
Validator operator!() const
Create a validator that fails when a given validator succeeds.
Definition: Validators.hpp:246
Validator operator|(const Validator &other) const
Definition: Validators.hpp:223
Validator & active(bool active_val=true)
Specify whether the Validator is active or not.
Definition: Validators.hpp:161
std::string name_
The name for search purposes of the Validator.
Definition: Validators.hpp:86
Validator description(std::string validator_desc) const
Specify the type string.
Definition: Validators.hpp:135
bool get_modifying() const
Get a boolean if the validator is allowed to modify the input returns true if it can modify the input...
Definition: Validators.hpp:194
Validator & application_index(int app_index)
Specify the application index of a validator.
Definition: Validators.hpp:178
Validator(std::function< std::string(std::string &)> op, std::string validator_desc, std::string validator_name="")
Construct Validator from basic information.
Definition: Validators.hpp:99
Validator & operation(std::function< std::string(std::string &)> op)
Set the Validator operation function.
Definition: Validators.hpp:103
Check for an existing directory (returns error message if check fails)
Definition: Validators.hpp:357
ExistingDirectoryValidator()
Definition: Validators.hpp:359
Check for an existing file (returns error message if check fails)
Definition: Validators.hpp:340
ExistingFileValidator()
Definition: Validators.hpp:342
Check for an existing path.
Definition: Validators.hpp:374
ExistingPathValidator()
Definition: Validators.hpp:376
Validate the given string is a legal ipv4 address.
Definition: Validators.hpp:402
IPV4Validator()
Definition: Validators.hpp:404
Check for an non-existing path.
Definition: Validators.hpp:388
NonexistentPathValidator()
Definition: Validators.hpp:390
constexpr enabler dummy
An instance to use in EnableIf.
Definition: TypeTools.hpp:33
auto smart_deref(T value) -> decltype(*value)
Definition: Validators.hpp:534
auto to_string(T &&value) -> decltype(std::forward< T >(value))
Convert an object to a string (directly forward if this can become a string)
Definition: TypeTools.hpp:269
path_type check_path(const char *file) noexcept
get the type of the path from a file name
Definition: Validators.hpp:324
path_type
CLI enumeration of different file types.
Definition: Validators.hpp:295
std::string generate_map(const T &map, bool key_only=false)
Generate a string representation of a map.
Definition: Validators.hpp:558
std::string & rtrim(std::string &str)
Trim whitespace from right of string.
Definition: StringTools.hpp:123
std::string remove_underscore(std::string str)
remove underscores from a string
Definition: StringTools.hpp:267
std::enable_if< std::is_signed< T >::value, T >::type overflowCheck(const T &a, const T &b)
Do a check for overflow on signed numbers.
Definition: Validators.hpp:631
std::enable_if< std::is_integral< T >::value, bool >::type checked_multiply(T &a, T b)
Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise.
Definition: Validators.hpp:645
std::string & trim(std::string &str)
Trim whitespace from string.
Definition: StringTools.hpp:138
std::string generate_set(const T &set)
Generate a string representation of a set.
Definition: Validators.hpp:545
std::pair< std::string, std::string > split_program_name(std::string commandline)
Definition: Validators.hpp:1093
std::string value_string(const T &value)
get a string as a convertible value for arithmetic types
Definition: TypeTools.hpp:336
std::string & ltrim(std::string &str)
Trim whitespace from left of string.
Definition: StringTools.hpp:109
auto search(const T &set, const V &val) -> std::pair< bool, decltype(std::begin(detail::smart_deref(set)))>
A search function.
Definition: Validators.hpp:589
std::string type_name()
Print type name for tuples with 2 or more elements.
Definition: TypeTools.hpp:773
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:63
std::string find_and_replace(std::string str, std::string from, std::string to)
Find and replace a substring with another substring.
Definition: StringTools.hpp:273
return str
Definition: TypeTools.hpp:766
std::vector< std::string > split(const std::string &s, char delim)
Split a string by a delim.
Definition: StringTools.hpp:46
bool isalpha(const std::string &str)
Verify that str consists of letters only.
Definition: StringTools.hpp:254
std::string to_lower(std::string str)
Return a lower case version of a string.
Definition: StringTools.hpp:259
enabler
Simple empty scoped class.
Definition: TypeTools.hpp:30
bool lexical_cast(const std::string &input, T &output)
Integer conversion.
Definition: TypeTools.hpp:862
Definition: App.hpp:34
std::string ignore_case(std::string item)
Helper function to allow ignore_case to be passed to IsMember or Transform.
Definition: Validators.hpp:876
std::string ignore_underscore(std::string item)
Helper function to allow ignore_underscore to be passed to IsMember or Transform.
Definition: Validators.hpp:879
typename std::enable_if< B, T >::type enable_if_t
Definition: TypeTools.hpp:41
const detail::ExistingDirectoryValidator ExistingDirectory
Check for an existing directory (returns error message if check fails)
Definition: Validators.hpp:433
const detail::NonexistentPathValidator NonexistentPath
Check for an non-existing path.
Definition: Validators.hpp:439
const detail::IPV4Validator ValidIPV4
Check for an IP4 address.
Definition: Validators.hpp:442
const detail::ExistingPathValidator ExistingPath
Check for an existing path.
Definition: Validators.hpp:436
std::vector< std::pair< std::string, T > > TransformPairs
definition of the default transformation object
Definition: Validators.hpp:739
const detail::ExistingFileValidator ExistingFile
Check for existing file (returns error message if check fails)
Definition: Validators.hpp:430
const Range NonNegativeNumber((std::numeric_limits< double >::max)(), "NONNEGATIVE")
Check for a non negative number.
std::string ignore_space(std::string item)
Helper function to allow checks to ignore spaces to be passed to IsMember or Transform.
Definition: Validators.hpp:882
const Range PositiveNumber((std::numeric_limits< double >::min)(),(std::numeric_limits< double >::max)(), "POSITIVE")
Check for a positive valued number (val>0.0), min() her is the smallest positive number.
const TypeValidator< double > Number("NUMBER")
Check for a number.
T type
Definition: TypeTools.hpp:73
T type
Definition: TypeTools.hpp:86
Definition: Validators.hpp:578
static const auto value
Definition: Validators.hpp:583
std::integral_constant< bool, value > type
Definition: Validators.hpp:584
static auto test(int) -> decltype(std::declval< CC >().find(std::declval< VV >()), std::true_type())
static auto test(...) -> decltype(std::false_type())
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:97
typename T::value_type value_type
Definition: TypeTools.hpp:98
typename std::remove_const< value_type >::type first_type
Definition: TypeTools.hpp:99
static auto first(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:103