CLI11  1.9.0
Validators.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 // Distributed under the 3-Clause BSD License. See accompanying
4 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
5 
6 #include "CLI/Macros.hpp"
7 #include "CLI/StringTools.hpp"
8 #include "CLI/TypeTools.hpp"
9 
10 #include <cmath>
11 #include <functional>
12 #include <iostream>
13 #include <limits>
14 #include <map>
15 #include <memory>
16 #include <string>
17 
18 // [CLI11:verbatim]
19 
20 // C standard library
21 // Only needed for existence checking
22 #if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
23 #if __has_include(<filesystem>)
24 // Filesystem cannot be used if targeting macOS < 10.15
25 #if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
26 #define CLI11_HAS_FILESYSTEM 0
27 #else
28 #include <filesystem>
29 #if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
30 #define CLI11_HAS_FILESYSTEM 1
31 #else
32 #define CLI11_HAS_FILESYSTEM 0
33 #endif
34 #endif
35 #endif
36 #endif
37 
38 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
39 #include <filesystem> // NOLINT(build/include)
40 #else
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #endif
44 
45 // [CLI11:verbatim]
46 
47 namespace CLI {
48 
49 class Option;
50 
52 
59 
61 class Validator {
62  protected:
64  std::function<std::string()> desc_function_{[]() { return std::string{}; }};
65 
68  std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
70  std::string name_{};
74  bool active_{true};
76  bool non_modifying_{false};
77 
78  public:
79  Validator() = default;
81  explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
83  Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
84  : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
85  name_(std::move(validator_name)) {}
87  Validator &operation(std::function<std::string(std::string &)> op) {
88  func_ = std::move(op);
89  return *this;
90  }
93  std::string operator()(std::string &str) const {
94  std::string retstring;
95  if(active_) {
96  if(non_modifying_) {
97  std::string value = str;
98  retstring = func_(value);
99  } else {
100  retstring = func_(str);
101  }
102  }
103  return retstring;
104  };
105 
108  std::string operator()(const std::string &str) const {
109  std::string value = str;
110  return (active_) ? func_(value) : std::string{};
111  };
112 
114  Validator &description(std::string validator_desc) {
115  desc_function_ = [validator_desc]() { return validator_desc; };
116  return *this;
117  }
119  Validator description(std::string validator_desc) const {
120  Validator newval(*this);
121  newval.desc_function_ = [validator_desc]() { return validator_desc; };
122  return newval;
123  }
125  std::string get_description() const {
126  if(active_) {
127  return desc_function_();
128  }
129  return std::string{};
130  }
132  Validator &name(std::string validator_name) {
133  name_ = std::move(validator_name);
134  return *this;
135  }
137  Validator name(std::string validator_name) const {
138  Validator newval(*this);
139  newval.name_ = std::move(validator_name);
140  return newval;
141  }
143  const std::string &get_name() const { return name_; }
145  Validator &active(bool active_val = true) {
146  active_ = active_val;
147  return *this;
148  }
150  Validator active(bool active_val = true) const {
151  Validator newval(*this);
152  newval.active_ = active_val;
153  return newval;
154  }
155 
157  Validator &non_modifying(bool no_modify = true) {
158  non_modifying_ = no_modify;
159  return *this;
160  }
162  Validator &application_index(int app_index) {
163  application_index_ = app_index;
164  return *this;
165  };
167  Validator application_index(int app_index) const {
168  Validator newval(*this);
169  newval.application_index_ = app_index;
170  return newval;
171  };
175  bool get_active() const { return active_; }
176 
178  bool get_modifying() const { return !non_modifying_; }
179 
182  Validator operator&(const Validator &other) const {
183  Validator newval;
184 
185  newval._merge_description(*this, other, " AND ");
186 
187  // Give references (will make a copy in lambda function)
188  const std::function<std::string(std::string & filename)> &f1 = func_;
189  const std::function<std::string(std::string & filename)> &f2 = other.func_;
190 
191  newval.func_ = [f1, f2](std::string &input) {
192  std::string s1 = f1(input);
193  std::string s2 = f2(input);
194  if(!s1.empty() && !s2.empty())
195  return std::string("(") + s1 + ") AND (" + s2 + ")";
196  else
197  return s1 + s2;
198  };
199 
200  newval.active_ = (active_ & other.active_);
202  return newval;
203  }
204 
207  Validator operator|(const Validator &other) const {
208  Validator newval;
209 
210  newval._merge_description(*this, other, " OR ");
211 
212  // Give references (will make a copy in lambda function)
213  const std::function<std::string(std::string &)> &f1 = func_;
214  const std::function<std::string(std::string &)> &f2 = other.func_;
215 
216  newval.func_ = [f1, f2](std::string &input) {
217  std::string s1 = f1(input);
218  std::string s2 = f2(input);
219  if(s1.empty() || s2.empty())
220  return std::string();
221 
222  return std::string("(") + s1 + ") OR (" + s2 + ")";
223  };
224  newval.active_ = (active_ & other.active_);
226  return newval;
227  }
228 
231  Validator newval;
232  const std::function<std::string()> &dfunc1 = desc_function_;
233  newval.desc_function_ = [dfunc1]() {
234  auto str = dfunc1();
235  return (!str.empty()) ? std::string("NOT ") + str : std::string{};
236  };
237  // Give references (will make a copy in lambda function)
238  const std::function<std::string(std::string & res)> &f1 = func_;
239 
240  newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
241  std::string s1 = f1(test);
242  if(s1.empty()) {
243  return std::string("check ") + dfunc1() + " succeeded improperly";
244  }
245  return std::string{};
246  };
247  newval.active_ = active_;
249  return newval;
250  }
251 
252  private:
253  void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
254 
255  const std::function<std::string()> &dfunc1 = val1.desc_function_;
256  const std::function<std::string()> &dfunc2 = val2.desc_function_;
257 
258  desc_function_ = [=]() {
259  std::string f1 = dfunc1();
260  std::string f2 = dfunc2();
261  if((f1.empty()) || (f2.empty())) {
262  return f1 + f2;
263  }
264  return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
265  };
266  }
267 }; // namespace CLI
268 
270 class CustomValidator : public Validator {
271  public:
272 };
273 // The implementation of the built in validators is using the Validator class;
274 // the user is only expected to use the const (static) versions (since there's no setup).
275 // Therefore, this is in detail.
276 namespace detail {
277 
280 
281 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
282 inline path_type check_path(const char *file) noexcept {
284  std::error_code ec;
285  auto stat = std::filesystem::status(file, ec);
286  if(ec) {
287  return path_type::nonexistant;
288  }
289  switch(stat.type()) {
290  case std::filesystem::file_type::none:
291  case std::filesystem::file_type::not_found:
292  return path_type::nonexistant;
293  case std::filesystem::file_type::directory:
294  return path_type::directory;
295  case std::filesystem::file_type::symlink:
296  case std::filesystem::file_type::block:
297  case std::filesystem::file_type::character:
298  case std::filesystem::file_type::fifo:
299  case std::filesystem::file_type::socket:
300  case std::filesystem::file_type::regular:
301  case std::filesystem::file_type::unknown:
302  default:
303  return path_type::file;
304  }
305 }
306 #else
307 inline path_type check_path(const char *file) noexcept {
309 #if defined(_MSC_VER)
310  struct __stat64 buffer;
311  if(_stat64(file, &buffer) == 0) {
312  return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
313  }
314 #else
315  struct stat buffer;
316  if(stat(file, &buffer) == 0) {
317  return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
318  }
319 #endif
320  return path_type::nonexistant;
321 }
322 #endif
323 class ExistingFileValidator : public Validator {
325  public:
327  func_ = [](std::string &filename) {
328  auto path_result = check_path(filename.c_str());
329  if(path_result == path_type::nonexistant) {
330  return "File does not exist: " + filename;
331  }
332  if(path_result == path_type::directory) {
333  return "File is actually a directory: " + filename;
334  }
335  return std::string();
336  };
337  }
338 };
339 
342  public:
344  func_ = [](std::string &filename) {
345  auto path_result = check_path(filename.c_str());
346  if(path_result == path_type::nonexistant) {
347  return "Directory does not exist: " + filename;
348  }
349  if(path_result == path_type::file) {
350  return "Directory is actually a file: " + filename;
351  }
352  return std::string();
353  };
354  }
355 };
356 
359  public:
360  ExistingPathValidator() : Validator("PATH(existing)") {
361  func_ = [](std::string &filename) {
362  auto path_result = check_path(filename.c_str());
363  if(path_result == path_type::nonexistant) {
364  return "Path does not exist: " + filename;
365  }
366  return std::string();
367  };
368  }
369 };
370 
373  public:
374  NonexistentPathValidator() : Validator("PATH(non-existing)") {
375  func_ = [](std::string &filename) {
376  auto path_result = check_path(filename.c_str());
377  if(path_result != path_type::nonexistant) {
378  return "Path already exists: " + filename;
379  }
380  return std::string();
381  };
382  }
383 };
384 
386 class IPV4Validator : public Validator {
387  public:
388  IPV4Validator() : Validator("IPV4") {
389  func_ = [](std::string &ip_addr) {
390  auto result = CLI::detail::split(ip_addr, '.');
391  if(result.size() != 4) {
392  return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
393  }
394  int num;
395  for(const auto &var : result) {
396  bool retval = detail::lexical_cast(var, num);
397  if(!retval) {
398  return std::string("Failed parsing number (") + var + ')';
399  }
400  if(num < 0 || num > 255) {
401  return std::string("Each IP number must be between 0 and 255 ") + var;
402  }
403  }
404  return std::string();
405  };
406  }
407 };
408 
410 class PositiveNumber : public Validator {
411  public:
412  PositiveNumber() : Validator("POSITIVE") {
413  func_ = [](std::string &number_str) {
414  double number;
415  if(!detail::lexical_cast(number_str, number)) {
416  return std::string("Failed parsing number: (") + number_str + ')';
417  }
418  if(number <= 0) {
419  return std::string("Number less or equal to 0: (") + number_str + ')';
420  }
421  return std::string();
422  };
423  }
424 };
426 class NonNegativeNumber : public Validator {
427  public:
428  NonNegativeNumber() : Validator("NONNEGATIVE") {
429  func_ = [](std::string &number_str) {
430  double number;
431  if(!detail::lexical_cast(number_str, number)) {
432  return std::string("Failed parsing number: (") + number_str + ')';
433  }
434  if(number < 0) {
435  return std::string("Number less than 0: (") + number_str + ')';
436  }
437  return std::string();
438  };
439  }
440 };
441 
443 class Number : public Validator {
444  public:
445  Number() : Validator("NUMBER") {
446  func_ = [](std::string &number_str) {
447  double number;
448  if(!detail::lexical_cast(number_str, number)) {
449  return std::string("Failed parsing as a number (") + number_str + ')';
450  }
451  return std::string();
452  };
453  }
454 };
455 
456 } // namespace detail
457 
458 // Static is not needed here, because global const implies static.
459 
462 
465 
468 
471 
474 
477 
480 
483 
485 class Range : public Validator {
486  public:
491  template <typename T> Range(T min, T max) {
492  std::stringstream out;
493  out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
494  description(out.str());
495 
496  func_ = [min, max](std::string &input) {
497  T val;
498  bool converted = detail::lexical_cast(input, val);
499  if((!converted) || (val < min || val > max))
500  return std::string("Value ") + input + " not in range " + std::to_string(min) + " to " +
501  std::to_string(max);
502 
503  return std::string();
504  };
505  }
506 
508  template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {}
509 };
510 
512 class Bound : public Validator {
513  public:
518  template <typename T> Bound(T min, T max) {
519  std::stringstream out;
520  out << detail::type_name<T>() << " bounded to [" << min << " - " << max << "]";
521  description(out.str());
522 
523  func_ = [min, max](std::string &input) {
524  T val;
525  bool converted = detail::lexical_cast(input, val);
526  if(!converted) {
527  return std::string("Value ") + input + " could not be converted";
528  }
529  if(val < min)
530  input = detail::to_string(min);
531  else if(val > max)
532  input = detail::to_string(max);
533 
534  return std::string{};
535  };
536  }
537 
539  template <typename T> explicit Bound(T max) : Bound(static_cast<T>(0), max) {}
540 };
541 
542 namespace detail {
543 template <typename T,
544  enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
545 auto smart_deref(T value) -> decltype(*value) {
546  return *value;
547 }
548 
549 template <
550  typename T,
552 typename std::remove_reference<T>::type &smart_deref(T &value) {
553  return value;
554 }
556 template <typename T> std::string generate_set(const T &set) {
557  using element_t = typename detail::element_type<T>::type;
558  using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
559  std::string out(1, '{');
560  out.append(detail::join(
561  detail::smart_deref(set),
562  [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
563  ","));
564  out.push_back('}');
565  return out;
566 }
567 
569 template <typename T> std::string generate_map(const T &map, bool key_only = false) {
570  using element_t = typename detail::element_type<T>::type;
571  using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
572  std::string out(1, '{');
573  out.append(detail::join(
574  detail::smart_deref(map),
575  [key_only](const iteration_type_t &v) {
577 
578  if(!key_only) {
579  res.append("->");
581  }
582  return res;
583  },
584  ","));
585  out.push_back('}');
586  return out;
587 }
588 
589 template <typename C, typename V> struct has_find {
590  template <typename CC, typename VV>
591  static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
592  template <typename, typename> static auto test(...) -> decltype(std::false_type());
593 
594  static const auto value = decltype(test<C, V>(0))::value;
595  using type = std::integral_constant<bool, value>;
596 };
597 
599 template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
600 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
601  using element_t = typename detail::element_type<T>::type;
602  auto &setref = detail::smart_deref(set);
603  auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
604  return (detail::pair_adaptor<element_t>::first(v) == val);
605  });
606  return {(it != std::end(setref)), it};
607 }
608 
610 template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
611 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
612  auto &setref = detail::smart_deref(set);
613  auto it = setref.find(val);
614  return {(it != std::end(setref)), it};
615 }
616 
618 template <typename T, typename V>
619 auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
620  -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
621  using element_t = typename detail::element_type<T>::type;
622  // do the potentially faster first search
623  auto res = search(set, val);
624  if((res.first) || (!(filter_function))) {
625  return res;
626  }
627  // if we haven't found it do the longer linear search with all the element translations
628  auto &setref = detail::smart_deref(set);
629  auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
631  a = filter_function(a);
632  return (a == val);
633  });
634  return {(it != std::end(setref)), it};
635 }
636 
637 // the following suggestion was made by Nikita Ofitserov(@himikof)
638 // done in templates to prevent compiler warnings on negation of unsigned numbers
639 
641 template <typename T>
642 inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
643  if((a > 0) == (b > 0)) {
644  return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
645  } else {
646  return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
647  }
648 }
650 template <typename T>
651 inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
652  return ((std::numeric_limits<T>::max)() / a < b);
653 }
654 
656 template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
657  if(a == 0 || b == 0 || a == 1 || b == 1) {
658  a *= b;
659  return true;
660  }
661  if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
662  return false;
663  }
664  if(overflowCheck(a, b)) {
665  return false;
666  }
667  a *= b;
668  return true;
669 }
670 
672 template <typename T>
673 typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
674  T c = a * b;
675  if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
676  return false;
677  }
678  a = c;
679  return true;
680 }
681 
682 } // namespace detail
684 class IsMember : public Validator {
685  public:
686  using filter_fn_t = std::function<std::string(std::string)>;
687 
689  template <typename T, typename... Args>
690  IsMember(std::initializer_list<T> values, Args &&... args)
691  : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
692 
694  template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
695 
698  template <typename T, typename F> explicit IsMember(T set, F filter_function) {
699 
700  // Get the type of the contained item - requires a container have ::value_type
701  // if the type does not have first_type and second_type, these are both value_type
702  using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
703  using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
704 
705  using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
706  // (const char * to std::string)
707 
708  // Make a local copy of the filter function, using a std::function if not one already
709  std::function<local_item_t(local_item_t)> filter_fn = filter_function;
710 
711  // This is the type name for help, it will take the current version of the set contents
712  desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
713 
714  // This is the function that validates
715  // It stores a copy of the set pointer-like, so shared_ptr will stay alive
716  func_ = [set, filter_fn](std::string &input) {
717  local_item_t b;
718  if(!detail::lexical_cast(input, b)) {
719  throw ValidationError(input); // name is added later
720  }
721  if(filter_fn) {
722  b = filter_fn(b);
723  }
724  auto res = detail::search(set, b, filter_fn);
725  if(res.first) {
726  // Make sure the version in the input string is identical to the one in the set
727  if(filter_fn) {
729  }
730 
731  // Return empty error string (success)
732  return std::string{};
733  }
734 
735  // If you reach this point, the result was not found
736  std::string out(" not in ");
738  return out;
739  };
740  }
741 
743  template <typename T, typename... Args>
744  IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
745  : IsMember(
746  std::forward<T>(set),
747  [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
748  other...) {}
749 };
750 
752 template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
753 
755 class Transformer : public Validator {
756  public:
757  using filter_fn_t = std::function<std::string(std::string)>;
758 
760  template <typename... Args>
761  Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
762  : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
763 
765  template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
766 
769  template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
770 
771  static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
772  "mapping must produce value pairs");
773  // Get the type of the contained item - requires a container have ::value_type
774  // if the type does not have first_type and second_type, these are both value_type
775  using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
776  using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
777  using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
778  // (const char * to std::string)
779 
780  // Make a local copy of the filter function, using a std::function if not one already
781  std::function<local_item_t(local_item_t)> filter_fn = filter_function;
782 
783  // This is the type name for help, it will take the current version of the set contents
784  desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
785 
786  func_ = [mapping, filter_fn](std::string &input) {
787  local_item_t b;
788  if(!detail::lexical_cast(input, b)) {
789  return std::string();
790  // there is no possible way we can match anything in the mapping if we can't convert so just return
791  }
792  if(filter_fn) {
793  b = filter_fn(b);
794  }
795  auto res = detail::search(mapping, b, filter_fn);
796  if(res.first) {
798  }
799  return std::string{};
800  };
801  }
802 
804  template <typename T, typename... Args>
805  Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
806  : Transformer(
807  std::forward<T>(mapping),
808  [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
809  other...) {}
810 };
811 
814  public:
815  using filter_fn_t = std::function<std::string(std::string)>;
816 
818  template <typename... Args>
819  CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
820  : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
821 
823  template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
824 
827  template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
828 
829  static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
830  "mapping must produce value pairs");
831  // Get the type of the contained item - requires a container have ::value_type
832  // if the type does not have first_type and second_type, these are both value_type
833  using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
834  using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
835  using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
836  // (const char * to std::string)
837  using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair //
838  // the type of the object pair
839 
840  // Make a local copy of the filter function, using a std::function if not one already
841  std::function<local_item_t(local_item_t)> filter_fn = filter_function;
842 
843  auto tfunc = [mapping]() {
844  std::string out("value in ");
845  out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
846  out += detail::join(
847  detail::smart_deref(mapping),
848  [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
849  ",");
850  out.push_back('}');
851  return out;
852  };
853 
854  desc_function_ = tfunc;
855 
856  func_ = [mapping, tfunc, filter_fn](std::string &input) {
857  local_item_t b;
858  bool converted = detail::lexical_cast(input, b);
859  if(converted) {
860  if(filter_fn) {
861  b = filter_fn(b);
862  }
863  auto res = detail::search(mapping, b, filter_fn);
864  if(res.first) {
866  return std::string{};
867  }
868  }
869  for(const auto &v : detail::smart_deref(mapping)) {
871  if(output_string == input) {
872  return std::string();
873  }
874  }
875 
876  return "Check " + input + " " + tfunc() + " FAILED";
877  };
878  }
879 
881  template <typename T, typename... Args>
882  CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
884  std::forward<T>(mapping),
885  [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
886  other...) {}
887 };
888 
890 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
891 
893 inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
894 
896 inline std::string ignore_space(std::string item) {
897  item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
898  item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
899  return item;
900 }
901 
913 class AsNumberWithUnit : public Validator {
914  public:
919  enum Options {
925  };
926 
927  template <typename Number>
928  explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
929  Options opts = DEFAULT,
930  const std::string &unit_name = "UNIT") {
931  description(generate_description<Number>(unit_name, opts));
932  validate_mapping(mapping, opts);
933 
934  // transform function
935  func_ = [mapping, opts](std::string &input) -> std::string {
936  Number num;
937 
938  detail::rtrim(input);
939  if(input.empty()) {
940  throw ValidationError("Input is empty");
941  }
942 
943  // Find split position between number and prefix
944  auto unit_begin = input.end();
945  while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
946  --unit_begin;
947  }
948 
949  std::string unit{unit_begin, input.end()};
950  input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
951  detail::trim(input);
952 
953  if(opts & UNIT_REQUIRED && unit.empty()) {
954  throw ValidationError("Missing mandatory unit");
955  }
956  if(opts & CASE_INSENSITIVE) {
957  unit = detail::to_lower(unit);
958  }
959 
960  bool converted = detail::lexical_cast(input, num);
961  if(!converted) {
962  throw ValidationError(std::string("Value ") + input + " could not be converted to " +
963  detail::type_name<Number>());
964  }
965 
966  if(unit.empty()) {
967  // No need to modify input if no unit passed
968  return {};
969  }
970 
971  // find corresponding factor
972  auto it = mapping.find(unit);
973  if(it == mapping.end()) {
974  throw ValidationError(unit +
975  " unit not recognized. "
976  "Allowed values: " +
977  detail::generate_map(mapping, true));
978  }
979 
980  // perform safe multiplication
981  bool ok = detail::checked_multiply(num, it->second);
982  if(!ok) {
983  throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
984  " factor would cause number overflow. Use smaller value.");
985  }
986  input = detail::to_string(num);
987 
988  return {};
989  };
990  }
991 
992  private:
995  template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
996  for(auto &kv : mapping) {
997  if(kv.first.empty()) {
998  throw ValidationError("Unit must not be empty.");
999  }
1000  if(!detail::isalpha(kv.first)) {
1001  throw ValidationError("Unit must contain only letters.");
1002  }
1003  }
1004 
1005  // make all units lowercase if CASE_INSENSITIVE
1006  if(opts & CASE_INSENSITIVE) {
1007  std::map<std::string, Number> lower_mapping;
1008  for(auto &kv : mapping) {
1009  auto s = detail::to_lower(kv.first);
1010  if(lower_mapping.count(s)) {
1011  throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
1012  s);
1013  }
1014  lower_mapping[detail::to_lower(kv.first)] = kv.second;
1015  }
1016  mapping = std::move(lower_mapping);
1017  }
1018  }
1019 
1021  template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
1022  std::stringstream out;
1023  out << detail::type_name<Number>() << ' ';
1024  if(opts & UNIT_REQUIRED) {
1025  out << name;
1026  } else {
1027  out << '[' << name << ']';
1028  }
1029  return out.str();
1030  }
1031 };
1032 
1045  public:
1046  using result_t = uint64_t;
1047 
1055  explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
1056  if(kb_is_1000) {
1057  description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
1058  } else {
1059  description("SIZE [b, kb(=1024b), ...]");
1060  }
1061  }
1062 
1063  private:
1065  static std::map<std::string, result_t> init_mapping(bool kb_is_1000) {
1066  std::map<std::string, result_t> m;
1067  result_t k_factor = kb_is_1000 ? 1000 : 1024;
1068  result_t ki_factor = 1024;
1069  result_t k = 1;
1070  result_t ki = 1;
1071  m["b"] = 1;
1072  for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
1073  k *= k_factor;
1074  ki *= ki_factor;
1075  m[p] = k;
1076  m[p + "b"] = k;
1077  m[p + "i"] = ki;
1078  m[p + "ib"] = ki;
1079  }
1080  return m;
1081  }
1082 
1084  static std::map<std::string, result_t> get_mapping(bool kb_is_1000) {
1085  if(kb_is_1000) {
1086  static auto m = init_mapping(true);
1087  return m;
1088  } else {
1089  static auto m = init_mapping(false);
1090  return m;
1091  }
1092  }
1093 };
1094 
1095 namespace detail {
1100 inline std::pair<std::string, std::string> split_program_name(std::string commandline) {
1101  // try to determine the programName
1102  std::pair<std::string, std::string> vals;
1103  trim(commandline);
1104  auto esp = commandline.find_first_of(' ', 1);
1105  while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
1106  esp = commandline.find_first_of(' ', esp + 1);
1107  if(esp == std::string::npos) {
1108  // if we have reached the end and haven't found a valid file just assume the first argument is the
1109  // program name
1110  esp = commandline.find_first_of(' ', 1);
1111  break;
1112  }
1113  }
1114  vals.first = commandline.substr(0, esp);
1115  rtrim(vals.first);
1116  // strip the program name
1117  vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
1118  ltrim(vals.second);
1119  return vals;
1120 }
1121 
1122 } // namespace detail
1124 
1125 } // namespace CLI
CLI::detail::ExistingDirectoryValidator::ExistingDirectoryValidator
ExistingDirectoryValidator()
Definition: Validators.hpp:343
CLI::Range::Range
Range(T max)
Range of one value is 0 to value.
Definition: Validators.hpp:508
CLI::Validator::get_active
bool get_active() const
Get a boolean if the validator is active.
Definition: Validators.hpp:175
CLI::PositiveNumber
const detail::PositiveNumber PositiveNumber
Check for a positive number.
Definition: Validators.hpp:476
CLI::AsNumberWithUnit::UNIT_REQUIRED
@ UNIT_REQUIRED
Definition: Validators.hpp:923
CLI::ignore_space
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:896
CLI::Range::Range
Range(T min, T max)
Definition: Validators.hpp:491
CLI::Validator::description
Validator description(std::string validator_desc) const
Specify the type string.
Definition: Validators.hpp:119
CLI::Validator::get_description
std::string get_description() const
Generate type description information for the Validator.
Definition: Validators.hpp:125
CLI::Number
const detail::Number Number
Check for a number.
Definition: Validators.hpp:482
CLI::CheckedTransformer::CheckedTransformer
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:882
CLI::detail::path_type::nonexistant
@ nonexistant
CLI::Validator::operator|
Validator operator|(const Validator &other) const
Definition: Validators.hpp:207
CLI::Bound
Produce a bounded range (factory). Min and max are inclusive.
Definition: Validators.hpp:512
CLI::detail::has_find::test
static auto test(int) -> decltype(std::declval< CC >().find(std::declval< VV >()), std::true_type())
CLI::Validator::active
Validator & active(bool active_val=true)
Specify whether the Validator is active or not.
Definition: Validators.hpp:145
CLI::IsMemberType::type
T type
Definition: TypeTools.hpp:74
CLI::detail::enabler
enabler
Simple empty scoped class.
Definition: TypeTools.hpp:22
CLI::Transformer::Transformer
Transformer(T mapping, F filter_function)
Definition: Validators.hpp:769
CLI::detail::ExistingPathValidator::ExistingPathValidator
ExistingPathValidator()
Definition: Validators.hpp:360
CLI::detail::overflowCheck
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:642
CLI::AsSizeValue::AsSizeValue
AsSizeValue(bool kb_is_1000)
Definition: Validators.hpp:1055
CLI::Validator
Some validators that are provided.
Definition: Validators.hpp:61
CLI::Validator::operator&
Validator operator&(const Validator &other) const
Definition: Validators.hpp:182
CLI::Validator::get_application_index
int get_application_index() const
Get the current value of the application index.
Definition: Validators.hpp:173
CLI::Bound::Bound
Bound(T max)
Range of one value is 0 to value.
Definition: Validators.hpp:539
CLI::detail::has_find
Definition: Validators.hpp:589
CLI::detail::split
std::vector< std::string > split(const std::string &s, char delim)
Split a string by a delim.
Definition: StringTools.hpp:39
CLI::AsSizeValue::result_t
uint64_t result_t
Definition: Validators.hpp:1046
CLI::AsNumberWithUnit::Options
Options
Definition: Validators.hpp:919
CLI::Transformer::Transformer
Transformer(std::initializer_list< std::pair< std::string, std::string >> values, Args &&... args)
This allows in-place construction.
Definition: Validators.hpp:761
TypeTools.hpp
CLI::detail::PositiveNumber
Validate the argument is a number and greater than 0.
Definition: Validators.hpp:410
CLI::Validator::func_
std::function< std::string(std::string &)> func_
Definition: Validators.hpp:68
CLI::Validator::name
Validator name(std::string validator_name) const
Specify the type string.
Definition: Validators.hpp:137
CLI::enable_if_t
typename std::enable_if< B, T >::type enable_if_t
Definition: TypeTools.hpp:33
CLI::detail::has_find::value
static const auto value
Definition: Validators.hpp:594
CLI::AsNumberWithUnit::AsNumberWithUnit
AsNumberWithUnit(std::map< std::string, Number > mapping, Options opts=DEFAULT, const std::string &unit_name="UNIT")
Definition: Validators.hpp:928
CLI::Validator::get_modifying
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:178
CLI::AsSizeValue
Definition: Validators.hpp:1044
CLI::Transformer::filter_fn_t
std::function< std::string(std::string)> filter_fn_t
Definition: Validators.hpp:757
CLI::CustomValidator
Class wrapping some of the accessors of Validator.
Definition: Validators.hpp:270
CLI::Validator::active_
bool active_
Enable for Validator to allow it to be disabled if need be.
Definition: Validators.hpp:74
CLI::detail::rtrim
std::string & rtrim(std::string &str)
Trim whitespace from right of string.
Definition: StringTools.hpp:112
CLI::detail::generate_set
std::string generate_set(const T &set)
Generate a string representation of a set.
Definition: Validators.hpp:556
CLI::Range
Produce a range (factory). Min and max are inclusive.
Definition: Validators.hpp:485
CLI::CheckedTransformer
translate named items to other or a value set
Definition: Validators.hpp:813
CLI::detail::join
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:56
CLI::detail::pair_adaptor::first_type
typename std::remove_const< value_type >::type first_type
Definition: TypeTools.hpp:100
CLI::Validator::get_name
const std::string & get_name() const
Get the name of the Validator.
Definition: Validators.hpp:143
CLI::detail::check_path
path_type check_path(const char *file) noexcept
get the type of the path from a file name
Definition: Validators.hpp:308
CLI::detail::smart_deref
auto smart_deref(T value) -> decltype(*value)
Definition: Validators.hpp:545
CLI::detail::to_string
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:222
CLI::detail::PositiveNumber::PositiveNumber
PositiveNumber()
Definition: Validators.hpp:412
CLI::detail::IPV4Validator
Validate the given string is a legal ipv4 address.
Definition: Validators.hpp:386
CLI::detail::ExistingDirectoryValidator
Check for an existing directory (returns error message if check fails)
Definition: Validators.hpp:341
CLI::detail::lexical_cast
bool lexical_cast(const std::string &input, T &output)
Signed integers.
Definition: TypeTools.hpp:596
CLI::IsMember::IsMember
IsMember(T &&set)
This checks to see if an item is in a set (empty function)
Definition: Validators.hpp:694
CLI::Transformer::Transformer
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:805
CLI
Definition: App.hpp:27
CLI::detail::pair_adaptor::first
static auto first(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:104
CLI::Validator::application_index
Validator application_index(int app_index) const
Specify the application index of a validator.
Definition: Validators.hpp:167
CLI::detail::search
auto search(const T &set, const V &val) -> std::pair< bool, decltype(std::begin(detail::smart_deref(set)))>
A search function.
Definition: Validators.hpp:600
CLI::detail::Number
Validate the argument is a number.
Definition: Validators.hpp:443
CLI::detail::split_program_name
std::pair< std::string, std::string > split_program_name(std::string commandline)
Definition: Validators.hpp:1100
CLI::detail::dummy
constexpr enabler dummy
An instance to use in EnableIf.
Definition: TypeTools.hpp:25
CLI::detail::ltrim
std::string & ltrim(std::string &str)
Trim whitespace from left of string.
Definition: StringTools.hpp:98
CLI::Validator::name_
std::string name_
The name for search purposes of the Validator.
Definition: Validators.hpp:70
CLI::ignore_case
std::string ignore_case(std::string item)
Helper function to allow ignore_case to be passed to IsMember or Transform.
Definition: Validators.hpp:890
CLI::detail::ExistingFileValidator
Check for an existing file (returns error message if check fails)
Definition: Validators.hpp:324
CLI::CheckedTransformer::CheckedTransformer
CheckedTransformer(T mapping)
direct map of std::string to std::string
Definition: Validators.hpp:823
CLI::ExistingDirectory
const detail::ExistingDirectoryValidator ExistingDirectory
Check for an existing directory (returns error message if check fails)
Definition: Validators.hpp:464
CLI::detail::path_type::directory
@ directory
CLI::ignore_underscore
std::string ignore_underscore(std::string item)
Helper function to allow ignore_underscore to be passed to IsMember or Transform.
Definition: Validators.hpp:893
CLI::Transformer
Translate named items to other or a value set.
Definition: Validators.hpp:755
CLI::detail::path_type::file
@ file
CLI::detail::has_find::type
std::integral_constant< bool, value > type
Definition: Validators.hpp:595
CLI::Validator::application_index
Validator & application_index(int app_index)
Specify the application index of a validator.
Definition: Validators.hpp:162
CLI::detail::to_lower
std::string to_lower(std::string str)
Return a lower case version of a string.
Definition: StringTools.hpp:196
CLI::Validator::operator()
std::string operator()(std::string &str) const
Definition: Validators.hpp:93
CLI::CheckedTransformer::CheckedTransformer
CheckedTransformer(T mapping, F filter_function)
Definition: Validators.hpp:827
CLI::ValidIPV4
const detail::IPV4Validator ValidIPV4
Check for an IP4 address.
Definition: Validators.hpp:473
CLI::IsMember::IsMember
IsMember(std::initializer_list< T > values, Args &&... args)
This allows in-place construction using an initializer list.
Definition: Validators.hpp:690
CLI::AsNumberWithUnit::UNIT_OPTIONAL
@ UNIT_OPTIONAL
Definition: Validators.hpp:922
CLI::detail::NonexistentPathValidator
Check for an non-existing path.
Definition: Validators.hpp:372
CLI::AsNumberWithUnit::CASE_INSENSITIVE
@ CASE_INSENSITIVE
Definition: Validators.hpp:921
CLI::detail::remove_underscore
std::string remove_underscore(std::string str)
remove underscores from a string
Definition: StringTools.hpp:204
CLI::IsMember
Verify items are in a set.
Definition: Validators.hpp:684
CLI::AsNumberWithUnit::CASE_SENSITIVE
@ CASE_SENSITIVE
Definition: Validators.hpp:920
CLI::ValidationError
Thrown when validation of results fails.
Definition: Error.hpp:198
StringTools.hpp
CLI::detail::trim
std::string & trim(std::string &str)
Trim whitespace from string.
Definition: StringTools.hpp:127
CLI::Transformer::Transformer
Transformer(T &&mapping)
direct map of std::string to std::string
Definition: Validators.hpp:765
CLI::detail::NonNegativeNumber::NonNegativeNumber
NonNegativeNumber()
Definition: Validators.hpp:428
Macros.hpp
CLI::ExistingPath
const detail::ExistingPathValidator ExistingPath
Check for an existing path.
Definition: Validators.hpp:467
CLI::detail::ExistingFileValidator::ExistingFileValidator
ExistingFileValidator()
Definition: Validators.hpp:326
CLI::AsNumberWithUnit
Definition: Validators.hpp:913
CLI::detail::pair_adaptor::value_type
typename T::value_type value_type
Definition: TypeTools.hpp:99
CLI::detail::element_type::type
T type
Definition: TypeTools.hpp:87
CLI::IsMember::IsMember
IsMember(T set, F filter_function)
Definition: Validators.hpp:698
CLI::TransformPairs
std::vector< std::pair< std::string, T > > TransformPairs
definition of the default transformation object
Definition: Validators.hpp:752
CLI::detail::Number::Number
Number()
Definition: Validators.hpp:445
CLI::Validator::operation
Validator & operation(std::function< std::string(std::string &)> op)
Set the Validator operation function.
Definition: Validators.hpp:87
CLI::Bound::Bound
Bound(T min, T max)
Definition: Validators.hpp:518
CLI::detail::pair_adaptor
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:98
CLI::ExistingFile
const detail::ExistingFileValidator ExistingFile
Check for existing file (returns error message if check fails)
Definition: Validators.hpp:461
CLI::Validator::name
Validator & name(std::string validator_name)
Specify the type string.
Definition: Validators.hpp:132
CLI::detail::NonNegativeNumber
Validate the argument is a number and greater than or equal to 0.
Definition: Validators.hpp:426
CLI::Validator::operator!
Validator operator!() const
Create a validator that fails when a given validator succeeds.
Definition: Validators.hpp:230
CLI::Validator::non_modifying
Validator & non_modifying(bool no_modify=true)
Specify whether the Validator can be modifying or not.
Definition: Validators.hpp:157
CLI::Validator::non_modifying_
bool non_modifying_
specify that a validator should not modify the input
Definition: Validators.hpp:76
CLI::detail::value_string
std::string value_string(const T &value)
get a string as a convertible value for arithmetic types
Definition: TypeTools.hpp:281
CLI::detail::checked_multiply
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:656
CLI::NonexistentPath
const detail::NonexistentPathValidator NonexistentPath
Check for an non-existing path.
Definition: Validators.hpp:470
CLI::detail::ExistingPathValidator
Check for an existing path.
Definition: Validators.hpp:358
CLI::AsNumberWithUnit::DEFAULT
@ DEFAULT
Definition: Validators.hpp:924
CLI::CheckedTransformer::filter_fn_t
std::function< std::string(std::string)> filter_fn_t
Definition: Validators.hpp:815
CLI::CheckedTransformer::CheckedTransformer
CheckedTransformer(std::initializer_list< std::pair< std::string, std::string >> values, Args &&... args)
This allows in-place construction.
Definition: Validators.hpp:819
CLI::Validator::Validator
Validator(std::function< std::string(std::string &)> op, std::string validator_desc, std::string validator_name="")
Construct Validator from basic information.
Definition: Validators.hpp:83
CLI::Validator::operator()
std::string operator()(const std::string &str) const
Definition: Validators.hpp:108
CLI::IsMember::filter_fn_t
std::function< std::string(std::string)> filter_fn_t
Definition: Validators.hpp:686
CLI::Validator::active
Validator active(bool active_val=true) const
Specify whether the Validator is active or not.
Definition: Validators.hpp:150
CLI::detail::isalpha
bool isalpha(const std::string &str)
Verify that str consists of letters only.
Definition: StringTools.hpp:191
CLI::ExitCodes::ValidationError
@ ValidationError
CLI::detail::IPV4Validator::IPV4Validator
IPV4Validator()
Definition: Validators.hpp:388
CLI::NonNegativeNumber
const detail::NonNegativeNumber NonNegativeNumber
Check for a non-negative number.
Definition: Validators.hpp:479
CLI::detail::NonexistentPathValidator::NonexistentPathValidator
NonexistentPathValidator()
Definition: Validators.hpp:374
CLI::Validator::description
Validator & description(std::string validator_desc)
Specify the type string.
Definition: Validators.hpp:114
CLI::Validator::application_index_
int application_index_
A Validator will only apply to an indexed value (-1 is all elements)
Definition: Validators.hpp:72
CLI::detail::path_type
path_type
CLI enumeration of different file types.
Definition: Validators.hpp:279
CLI::Validator::Validator
Validator()=default
CLI::IsMember::IsMember
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:744
CLI::Validator::Validator
Validator(std::string validator_desc)
Construct a Validator with just the description string.
Definition: Validators.hpp:81
CLI::Validator::desc_function_
std::function< std::string()> desc_function_
This is the description function, if empty the description_ will be used.
Definition: Validators.hpp:64
CLI::detail::generate_map
std::string generate_map(const T &map, bool key_only=false)
Generate a string representation of a map.
Definition: Validators.hpp:569