CLI11  1.9.1
TypeTools.hpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2020, 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 "StringTools.hpp"
10 #include <cstdint>
11 #include <exception>
12 #include <memory>
13 #include <string>
14 #include <type_traits>
15 #include <utility>
16 #include <vector>
17 
18 namespace CLI {
19 
20 // Type tools
21 
22 // Utilities for type enabling
23 namespace detail {
24 // Based generally on https://rmf.io/cxx11/almost-static-if
26 enum class enabler {};
27 
29 constexpr enabler dummy = {};
30 } // namespace detail
31 
37 template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
38 
40 template <typename... Ts> struct make_void { using type = void; };
41 
43 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
44 
46 template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;
47 
49 template <typename T> struct is_vector : std::false_type {};
50 
52 template <class T, class A> struct is_vector<std::vector<T, A>> : std::true_type {};
53 
55 template <class T, class A> struct is_vector<const std::vector<T, A>> : std::true_type {};
56 
58 template <typename T> struct is_bool : std::false_type {};
59 
61 template <> struct is_bool<bool> : std::true_type {};
62 
64 template <typename T> struct is_shared_ptr : std::false_type {};
65 
67 template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
68 
70 template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};
71 
73 template <typename T> struct is_copyable_ptr {
74  static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
75 };
76 
78 template <typename T> struct IsMemberType { using type = T; };
79 
81 template <> struct IsMemberType<const char *> { using type = std::string; };
82 
83 namespace detail {
84 
85 // These are utilities for IsMember and other transforming objects
86 
89 
91 template <typename T, typename Enable = void> struct element_type { using type = T; };
92 
93 template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
94  using type = typename std::pointer_traits<T>::element_type;
95 };
96 
99 template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };
100 
102 template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
103  using value_type = typename T::value_type;
104  using first_type = typename std::remove_const<value_type>::type;
105  using second_type = typename std::remove_const<value_type>::type;
106 
108  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
109  return std::forward<Q>(pair_value);
110  }
112  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
113  return std::forward<Q>(pair_value);
114  }
115 };
116 
119 template <typename T>
121  T,
122  conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
123  : std::true_type {
124  using value_type = typename T::value_type;
125  using first_type = typename std::remove_const<typename value_type::first_type>::type;
126  using second_type = typename std::remove_const<typename value_type::second_type>::type;
127 
129  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
130  return std::get<0>(std::forward<Q>(pair_value));
131  }
133  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
134  return std::get<1>(std::forward<Q>(pair_value));
135  }
136 };
137 
138 // Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning
139 // in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in
140 // brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a
141 // little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out.
142 // But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be
143 // suppressed
144 #ifdef __GNUC__
145 #pragma GCC diagnostic push
146 #pragma GCC diagnostic ignored "-Wnarrowing"
147 #endif
148 // check for constructibility from a specific type and copy assignable used in the parse detection
149 template <typename T, typename C> class is_direct_constructible {
150  template <typename TT, typename CC>
151  static auto test(int, std::true_type) -> decltype(
152 // NVCC warns about narrowing conversions here
153 #ifdef __CUDACC__
154 #pragma diag_suppress 2361
155 #endif
156  TT { std::declval<CC>() }
157 #ifdef __CUDACC__
158 #pragma diag_default 2361
159 #endif
160  ,
161  std::is_move_assignable<TT>());
162 
163  template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
164 
165  template <typename, typename> static auto test(...) -> std::false_type;
166 
167  public:
168  static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
169 };
170 #ifdef __GNUC__
171 #pragma GCC diagnostic pop
172 #endif
173 
174 // Check for output streamability
175 // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
176 
177 template <typename T, typename S = std::ostringstream> class is_ostreamable {
178  template <typename TT, typename SS>
179  static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
180 
181  template <typename, typename> static auto test(...) -> std::false_type;
182 
183  public:
184  static constexpr bool value = decltype(test<T, S>(0))::value;
185 };
186 
188 template <typename T, typename S = std::istringstream> class is_istreamable {
189  template <typename TT, typename SS>
190  static auto test(int) -> decltype(std::declval<SS &>() >> std::declval<TT &>(), std::true_type());
191 
192  template <typename, typename> static auto test(...) -> std::false_type;
193 
194  public:
195  static constexpr bool value = decltype(test<T, S>(0))::value;
196 };
197 
199 template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
200 bool from_stream(const std::string &istring, T &obj) {
201  std::istringstream is;
202  is.str(istring);
203  is >> obj;
204  return !is.fail() && !is.rdbuf()->in_avail();
205 }
206 
207 template <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> = detail::dummy>
208 bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
209  return false;
210 }
211 
212 // Check for tuple like types, as in classes with a tuple_size type trait
213 template <typename S> class is_tuple_like {
214  template <typename SS>
215  // static auto test(int)
216  // -> decltype(std::conditional<(std::tuple_size<SS>::value > 0), std::true_type, std::false_type>::type());
217  static auto test(int) -> decltype(std::tuple_size<SS>::value, std::true_type{});
218  template <typename> static auto test(...) -> std::false_type;
219 
220  public:
221  static constexpr bool value = decltype(test<S>(0))::value;
222 };
223 
225 template <typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
226 auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
227  return std::forward<T>(value);
228 }
229 
231 template <typename T,
232  enable_if_t<std::is_constructible<std::string, T>::value && !std::is_convertible<T, std::string>::value,
234 std::string to_string(const T &value) {
235  return std::string(value);
236 }
237 
239 template <typename T,
240  enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
243 std::string to_string(T &&value) {
244  std::stringstream stream;
245  stream << value;
246  return stream.str();
247 }
248 
250 template <typename T,
254 std::string to_string(T &&) {
255  return std::string{};
256 }
257 
259 template <typename T,
260  enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
261  is_vector<typename std::remove_reference<typename std::remove_const<T>::type>::type>::value,
263 std::string to_string(T &&variable) {
264  std::vector<std::string> defaults;
265  defaults.reserve(variable.size());
266  auto cval = variable.begin();
267  auto end = variable.end();
268  while(cval != end) {
269  defaults.emplace_back(CLI::detail::to_string(*cval));
270  ++cval;
271  }
272  return std::string("[" + detail::join(defaults) + "]");
273 }
274 
276 template <typename T1,
277  typename T2,
278  typename T,
279  enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
280 auto checked_to_string(T &&value) -> decltype(to_string(std::forward<T>(value))) {
281  return to_string(std::forward<T>(value));
282 }
283 
285 template <typename T1,
286  typename T2,
287  typename T,
289 std::string checked_to_string(T &&) {
290  return std::string{};
291 }
293 template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
294 std::string value_string(const T &value) {
295  return std::to_string(value);
296 }
298 template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
299 std::string value_string(const T &value) {
300  return std::to_string(static_cast<typename std::underlying_type<T>::type>(value));
301 }
303 template <typename T,
304  enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
305 auto value_string(const T &value) -> decltype(to_string(value)) {
306  return to_string(value);
307 }
308 
310 template <typename T, typename Enable = void> struct type_count { static const int value{0}; };
311 
313 template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
314  static constexpr int value{std::tuple_size<T>::value};
315 };
317 template <typename T>
318 struct type_count<
319  T,
320  typename std::enable_if<!is_vector<T>::value && !is_tuple_like<T>::value && !std::is_void<T>::value>::type> {
321  static constexpr int value{1};
322 };
323 
325 template <typename T> struct type_count<T, typename std::enable_if<is_vector<T>::value>::type> {
328 };
329 
331 template <typename T, typename Enable = void> struct expected_count { static const int value{0}; };
332 
334 template <typename T>
335 struct expected_count<T, typename std::enable_if<!is_vector<T>::value && !std::is_void<T>::value>::type> {
336  static constexpr int value{1};
337 };
339 template <typename T> struct expected_count<T, typename std::enable_if<is_vector<T>::value>::type> {
340  static constexpr int value{expected_max_vector_size};
341 };
342 
343 // Enumeration of the different supported categorizations of objects
344 enum class object_category : int {
345  integral_value = 2,
346  unsigned_integral = 4,
347  enumeration = 6,
348  boolean_value = 8,
349  floating_point = 10,
353  vector_value = 30,
354  tuple_value = 35,
355  // string assignable or greater used in a condition so anything string like must come last
356  string_assignable = 50,
358  other = 200,
359 
360 };
361 
363 template <typename T, typename Enable = void> struct classify_object {
365 };
366 
368 template <typename T>
370  typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value &&
371  !is_bool<T>::value && !std::is_enum<T>::value>::type> {
373 };
374 
376 template <typename T>
378  T,
379  typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value>::type> {
381 };
382 
384 template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
386 };
387 
389 template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
391 };
392 
394 template <typename T>
396  T,
397  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
398  std::is_assignable<T &, std::string>::value && !is_vector<T>::value>::type> {
400 };
401 
403 template <typename T>
405  T,
406  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
407  !std::is_assignable<T &, std::string>::value &&
408  std::is_constructible<T, std::string>::value && !is_vector<T>::value>::type> {
410 };
411 
413 template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
415 };
416 
419 template <typename T> struct uncommon_type {
420  using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
421  !std::is_assignable<T &, std::string>::value &&
422  !std::is_constructible<T, std::string>::value && !is_vector<T>::value &&
423  !std::is_enum<T>::value,
424  std::true_type,
425  std::false_type>::type;
426  static constexpr bool value = type::value;
427 };
428 
430 template <typename T>
432  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
433  is_direct_constructible<T, double>::value &&
434  is_direct_constructible<T, int>::value>::type> {
436 };
437 
439 template <typename T>
441  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
442  !is_direct_constructible<T, double>::value &&
443  is_direct_constructible<T, int>::value>::type> {
445 };
446 
448 template <typename T>
450  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
451  is_direct_constructible<T, double>::value &&
452  !is_direct_constructible<T, int>::value>::type> {
454 };
455 
457 template <typename T>
459  typename std::enable_if<(type_count<T>::value >= 2 && !is_vector<T>::value) ||
460  (is_tuple_like<T>::value && uncommon_type<T>::value &&
461  !is_direct_constructible<T, double>::value &&
462  !is_direct_constructible<T, int>::value)>::type> {
464 };
465 
467 template <typename T> struct classify_object<T, typename std::enable_if<is_vector<T>::value>::type> {
469 };
470 
471 // Type name print
472 
476 
477 template <typename T,
481 constexpr const char *type_name() {
482  return "INT";
483 }
484 
485 template <typename T,
487 constexpr const char *type_name() {
488  return "UINT";
489 }
490 
491 template <typename T,
492  enable_if_t<classify_object<T>::value == object_category::floating_point ||
496 constexpr const char *type_name() {
497  return "FLOAT";
498 }
499 
501 template <typename T,
502  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
503 constexpr const char *type_name() {
504  return "ENUM";
505 }
506 
508 template <typename T,
509  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
510 constexpr const char *type_name() {
511  return "BOOLEAN";
512 }
513 
515 template <typename T,
516  enable_if_t<classify_object<T>::value >= object_category::string_assignable, detail::enabler> = detail::dummy>
517 constexpr const char *type_name() {
518  return "TEXT";
519 }
520 
522 template <typename T,
523  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count<T>::value == 1,
525 inline std::string type_name() {
526  return type_name<typename std::tuple_element<0, T>::type>();
527 }
528 
530 template <typename T, std::size_t I>
531 inline typename std::enable_if<I == type_count<T>::value, std::string>::type tuple_name() {
532  return std::string{};
533 }
534 
536 template <typename T, std::size_t I>
537  inline typename std::enable_if < I<type_count<T>::value, std::string>::type tuple_name() {
538  std::string str = std::string(type_name<typename std::tuple_element<I, T>::type>()) + ',' + tuple_name<T, I + 1>();
539  if(str.back() == ',')
540  str.pop_back();
541  return str;
542 }
543 
545 template <typename T,
546  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count<T>::value >= 2,
548 std::string type_name() {
549  auto tname = std::string(1, '[') + tuple_name<T, 0>();
550  tname.push_back(']');
551  return tname;
552 }
553 
555 template <typename T,
557 inline std::string type_name() {
558  return type_name<typename T::value_type>();
559 }
560 
561 // Lexical cast
562 
564 inline std::int64_t to_flag_value(std::string val) {
565  static const std::string trueString("true");
566  static const std::string falseString("false");
567  if(val == trueString) {
568  return 1;
569  }
570  if(val == falseString) {
571  return -1;
572  }
573  val = detail::to_lower(val);
574  std::int64_t ret;
575  if(val.size() == 1) {
576  if(val[0] >= '1' && val[0] <= '9') {
577  return (static_cast<std::int64_t>(val[0]) - '0');
578  }
579  switch(val[0]) {
580  case '0':
581  case 'f':
582  case 'n':
583  case '-':
584  ret = -1;
585  break;
586  case 't':
587  case 'y':
588  case '+':
589  ret = 1;
590  break;
591  default:
592  throw std::invalid_argument("unrecognized character");
593  }
594  return ret;
595  }
596  if(val == trueString || val == "on" || val == "yes" || val == "enable") {
597  ret = 1;
598  } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
599  ret = -1;
600  } else {
601  ret = std::stoll(val);
602  }
603  return ret;
604 }
605 
607 template <typename T,
609 bool lexical_cast(const std::string &input, T &output) {
610  try {
611  std::size_t n = 0;
612  std::int64_t output_ll = std::stoll(input, &n, 0);
613  output = static_cast<T>(output_ll);
614  return n == input.size() && static_cast<std::int64_t>(output) == output_ll;
615  } catch(const std::invalid_argument &) {
616  return false;
617  } catch(const std::out_of_range &) {
618  return false;
619  }
620 }
621 
623 template <typename T,
625 bool lexical_cast(const std::string &input, T &output) {
626  if(!input.empty() && input.front() == '-')
627  return false; // std::stoull happily converts negative values to junk without any errors.
628 
629  try {
630  std::size_t n = 0;
631  std::uint64_t output_ll = std::stoull(input, &n, 0);
632  output = static_cast<T>(output_ll);
633  return n == input.size() && static_cast<std::uint64_t>(output) == output_ll;
634  } catch(const std::invalid_argument &) {
635  return false;
636  } catch(const std::out_of_range &) {
637  return false;
638  }
639 }
640 
642 template <typename T,
643  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
644 bool lexical_cast(const std::string &input, T &output) {
645  try {
646  auto out = to_flag_value(input);
647  output = (out > 0);
648  return true;
649  } catch(const std::invalid_argument &) {
650  return false;
651  } catch(const std::out_of_range &) {
652  // if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still
653  // valid all we care about the sign
654  output = (input[0] != '-');
655  return true;
656  }
657 }
658 
660 template <typename T,
661  enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
662 bool lexical_cast(const std::string &input, T &output) {
663  try {
664  std::size_t n = 0;
665  output = static_cast<T>(std::stold(input, &n));
666  return n == input.size();
667  } catch(const std::invalid_argument &) {
668  return false;
669  } catch(const std::out_of_range &) {
670  return false;
671  }
672 }
673 
675 template <typename T,
676  enable_if_t<classify_object<T>::value == object_category::string_assignable, detail::enabler> = detail::dummy>
677 bool lexical_cast(const std::string &input, T &output) {
678  output = input;
679  return true;
680 }
681 
683 template <
684  typename T,
685  enable_if_t<classify_object<T>::value == object_category::string_constructible, detail::enabler> = detail::dummy>
686 bool lexical_cast(const std::string &input, T &output) {
687  output = T(input);
688  return true;
689 }
690 
692 template <typename T,
693  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
694 bool lexical_cast(const std::string &input, T &output) {
695  typename std::underlying_type<T>::type val;
696  bool retval = detail::lexical_cast(input, val);
697  if(!retval) {
698  return false;
699  }
700  output = static_cast<T>(val);
701  return true;
702 }
703 
705 template <
706  typename T,
707  enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
708 bool lexical_cast(const std::string &input, T &output) {
709  int val;
710  if(lexical_cast(input, val)) {
711  output = T(val);
712  return true;
713  } else {
714  double dval;
715  if(lexical_cast(input, dval)) {
716  output = T{dval};
717  return true;
718  }
719  }
720  return from_stream(input, output);
721 }
722 
724 template <
725  typename T,
726  enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
727 bool lexical_cast(const std::string &input, T &output) {
728  int val;
729  if(lexical_cast(input, val)) {
730  output = T(val);
731  return true;
732  }
733  return from_stream(input, output);
734 }
735 
737 template <
738  typename T,
739  enable_if_t<classify_object<T>::value == object_category::double_constructible, detail::enabler> = detail::dummy>
740 bool lexical_cast(const std::string &input, T &output) {
741  double val;
742  if(lexical_cast(input, val)) {
743  output = T{val};
744  return true;
745  }
746  return from_stream(input, output);
747 }
748 
750 template <typename T, enable_if_t<classify_object<T>::value == object_category::other, detail::enabler> = detail::dummy>
751 bool lexical_cast(const std::string &input, T &output) {
752  static_assert(is_istreamable<T>::value,
753  "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it "
754  "is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
755  return from_stream(input, output);
756 }
757 
759 template <
760  typename T,
761  typename XC,
762  enable_if_t<std::is_same<T, XC>::value && (classify_object<T>::value == object_category::string_assignable ||
765 bool lexical_assign(const std::string &input, T &output) {
766  return lexical_cast(input, output);
767 }
768 
770 template <typename T,
771  typename XC,
775 bool lexical_assign(const std::string &input, T &output) {
776  if(input.empty()) {
777  output = T{};
778  return true;
779  }
780  return lexical_cast(input, output);
781 }
782 
784 template <
785  typename T,
786  typename XC,
787  enable_if_t<!std::is_same<T, XC>::value && std::is_assignable<T &, XC &>::value, detail::enabler> = detail::dummy>
788 bool lexical_assign(const std::string &input, T &output) {
789  XC val{};
790  bool parse_result = (!input.empty()) ? lexical_cast<XC>(input, val) : true;
791  if(parse_result) {
792  output = val;
793  }
794  return parse_result;
795 }
796 
798 template <typename T,
799  typename XC,
800  enable_if_t<!std::is_same<T, XC>::value && !std::is_assignable<T &, XC &>::value &&
801  std::is_move_assignable<T>::value,
803 bool lexical_assign(const std::string &input, T &output) {
804  XC val{};
805  bool parse_result = input.empty() ? true : lexical_cast<XC>(input, val);
806  if(parse_result) {
807  output = T(val); // use () form of constructor to allow some implicit conversions
808  }
809  return parse_result;
810 }
812 template <
813  typename T,
814  typename XC,
815  enable_if_t<!is_tuple_like<T>::value && !is_tuple_like<XC>::value && !is_vector<T>::value && !is_vector<XC>::value,
817 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
818  return lexical_assign<T, XC>(strings[0], output);
819 }
820 
822 template <typename T,
823  typename XC,
825 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
826  typename std::tuple_element<0, XC>::type v1;
827  typename std::tuple_element<1, XC>::type v2;
828  bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);
829  if(strings.size() > 1) {
830  retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);
831  }
832  if(retval) {
833  output = T{v1, v2};
834  }
835  return retval;
836 }
837 
839 template <class T,
840  class XC,
841  enable_if_t<expected_count<T>::value == expected_max_vector_size &&
844 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
845  output.clear();
846  output.reserve(strings.size());
847  for(const auto &elem : strings) {
848 
849  output.emplace_back();
850  bool retval = lexical_assign<typename T::value_type, typename XC::value_type>(elem, output.back());
851  if(!retval) {
852  return false;
853  }
854  }
855  return (!output.empty());
856 }
857 
859 template <class T,
860  class XC,
861  enable_if_t<expected_count<T>::value == expected_max_vector_size &&
864 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
865  output.clear();
866  for(std::size_t ii = 0; ii < strings.size(); ii += 2) {
867 
868  typename std::tuple_element<0, typename XC::value_type>::type v1;
869  typename std::tuple_element<1, typename XC::value_type>::type v2;
870  bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[ii], v1);
871  if(strings.size() > ii + 1) {
872  retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[ii + 1], v2);
873  }
874  if(retval) {
875  output.emplace_back(v1, v2);
876  } else {
877  return false;
878  }
879  }
880  return (!output.empty());
881 }
882 
884 template <class T,
885  class XC,
887  (type_count<XC>::value == 1),
889 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
890  bool retval = true;
891  output.clear();
892  output.reserve(strings.size());
893  for(const auto &elem : strings) {
894 
895  output.emplace_back();
896  retval = retval && lexical_assign<typename T::value_type, XC>(elem, output.back());
897  }
898  return (!output.empty()) && retval;
899 }
900 // This one is last since it can call other lexical_conversion functions
902 template <typename T,
903  typename XC,
904  enable_if_t<!is_tuple_like<T>::value && !is_vector<T>::value && is_vector<XC>::value, detail::enabler> =
906 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
907 
908  if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) {
909  XC val;
910  auto retval = lexical_conversion<XC, XC>(strings, val);
911  output = T{val};
912  return retval;
913  }
914  output = T{};
915  return true;
916 }
917 
919 template <class T, class XC, std::size_t I>
920 inline typename std::enable_if<I >= type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &,
921  T &) {
922  return true;
923 }
925 template <class T, class XC, std::size_t I>
926  inline typename std::enable_if <
927  I<type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &strings, T &output) {
928  bool retval = true;
929  if(strings.size() > I) {
930  retval = retval && lexical_assign<typename std::tuple_element<I, T>::type,
931  typename std::conditional<is_tuple_like<XC>::value,
932  typename std::tuple_element<I, XC>::type,
933  XC>::type>(strings[I], std::get<I>(output));
934  }
935  retval = retval && tuple_conversion<T, XC, I + 1>(strings, output);
936  return retval;
937 }
938 
940 template <class T, class XC, enable_if_t<is_tuple_like<T>::value, detail::enabler> = detail::dummy>
941 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
942  static_assert(
944  "if the conversion type is defined as a tuple it must be the same size as the type you are converting to");
945  return tuple_conversion<T, XC, 0>(strings, output);
946 }
947 
949 template <class T,
950  class XC,
951  enable_if_t<expected_count<T>::value == expected_max_vector_size &&
954 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
955  bool retval = true;
956  output.clear();
957  std::vector<std::string> temp;
958  std::size_t ii = 0;
959  std::size_t icount = 0;
960  std::size_t xcm = type_count<XC>::value;
961  while(ii < strings.size()) {
962  temp.push_back(strings[ii]);
963  ++ii;
964  ++icount;
965  if(icount == xcm || temp.back().empty()) {
966  if(static_cast<int>(xcm) == expected_max_vector_size) {
967  temp.pop_back();
968  }
969  output.emplace_back();
970  retval = retval && lexical_conversion<typename T::value_type, typename XC::value_type>(temp, output.back());
971  temp.clear();
972  if(!retval) {
973  return false;
974  }
975  icount = 0;
976  }
977  }
978  return retval;
979 }
985 template <typename T,
986  enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
987 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
988  std::int64_t count{0};
989  for(auto &flag : flags) {
990  count += detail::to_flag_value(flag);
991  }
992  output = (count > 0) ? static_cast<T>(count) : T{0};
993 }
994 
1000 template <typename T,
1001  enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
1002 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
1003  std::int64_t count{0};
1004  for(auto &flag : flags) {
1005  count += detail::to_flag_value(flag);
1006  }
1007  output = static_cast<T>(count);
1008 }
1009 
1010 } // namespace detail
1011 } // namespace CLI
Definition: TypeTools.hpp:149
static constexpr bool value
Definition: TypeTools.hpp:168
Check for input streamability.
Definition: TypeTools.hpp:188
static constexpr bool value
Definition: TypeTools.hpp:195
Definition: TypeTools.hpp:177
static constexpr bool value
Definition: TypeTools.hpp:184
Definition: TypeTools.hpp:213
static constexpr bool value
Definition: TypeTools.hpp:221
constexpr enabler dummy
An instance to use in EnableIf.
Definition: TypeTools.hpp:29
constexpr const char * type_name()
Print name for enumeration types.
Definition: TypeTools.hpp:481
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:226
auto checked_to_string(T &&value) -> decltype(to_string(std::forward< T >(value)))
special template overload
Definition: TypeTools.hpp:280
bool lexical_assign(const std::string &input, T &output)
Assign a value through lexical cast operations.
Definition: TypeTools.hpp:765
bool from_stream(const std::string &istring, T &obj)
Templated operation to get a value from a stream.
Definition: TypeTools.hpp:200
constexpr int expected_max_vector_size
Definition: StringTools.hpp:39
std::string value_string(const T &value)
get a string as a convertible value for arithmetic types
Definition: TypeTools.hpp:294
std::string to_string(T &&value)
Convert an object to a string (streaming must be supported for that type)
Definition: TypeTools.hpp:243
bool lexical_conversion(const std::vector< std ::string > &strings, T &output)
Lexical conversion if there is only one element.
Definition: TypeTools.hpp:817
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:59
bool ::type tuple_conversion(const std::vector< std::string > &, T &)
Definition: TypeTools.hpp:920
object_category
Definition: TypeTools.hpp:344
std::string to_lower(std::string str)
Return a lower case version of a string.
Definition: StringTools.hpp:199
std::size_t I std::enable_if< I==type_count< T >::value, std::string >::type tuple_name()
Definition: TypeTools.hpp:531
enabler
Simple empty scoped class.
Definition: TypeTools.hpp:26
bool lexical_cast(const std::string &input, T &output)
Signed integers.
Definition: TypeTools.hpp:609
std::int64_t to_flag_value(std::string val)
Convert a flag into an integer value typically binary flags.
Definition: TypeTools.hpp:564
Definition: App.hpp:32
typename std::enable_if< B, T >::type enable_if_t
Definition: TypeTools.hpp:37
typename std::conditional< B, T, F >::type conditional_t
A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine...
Definition: TypeTools.hpp:46
typename make_void< Ts... >::type void_t
A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine.
Definition: TypeTools.hpp:43
std::string type
Definition: TypeTools.hpp:81
This can be specialized to override the type deduction for IsMember.
Definition: TypeTools.hpp:78
T type
Definition: TypeTools.hpp:78
some type that is not otherwise recognized
Definition: TypeTools.hpp:363
static constexpr object_category value
Definition: TypeTools.hpp:364
typename std::pointer_traits< T >::element_type type
Definition: TypeTools.hpp:94
not a pointer
Definition: TypeTools.hpp:91
T type
Definition: TypeTools.hpp:91
Definition: TypeTools.hpp:99
typename element_type< T >::type::value_type type
Definition: TypeTools.hpp:99
This will only trigger for actual void type.
Definition: TypeTools.hpp:331
static const int value
Definition: TypeTools.hpp:331
typename std::remove_const< typename value_type::first_type >::type first_type
Definition: TypeTools.hpp:125
static auto first(Q &&pair_value) -> decltype(std::get< 0 >(std::forward< Q >(pair_value)))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:129
static auto second(Q &&pair_value) -> decltype(std::get< 1 >(std::forward< Q >(pair_value)))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:133
typename std::remove_const< typename value_type::second_type >::type second_type
Definition: TypeTools.hpp:126
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:102
typename T::value_type value_type
Definition: TypeTools.hpp:103
typename std::remove_const< value_type >::type second_type
Definition: TypeTools.hpp:105
static auto second(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:112
typename std::remove_const< value_type >::type first_type
Definition: TypeTools.hpp:104
static auto first(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:108
This will only trigger for actual void type.
Definition: TypeTools.hpp:310
static const int value
Definition: TypeTools.hpp:310
Definition: TypeTools.hpp:419
typename std::conditional<!std::is_floating_point< T >::value &&!std::is_integral< T >::value &&!std::is_assignable< T &, std::string >::value &&!std::is_constructible< T, std::string >::value &&!is_vector< T >::value &&!std::is_enum< T >::value, std::true_type, std::false_type >::type type
Definition: TypeTools.hpp:425
static constexpr bool value
Definition: TypeTools.hpp:426
Check to see if something is bool (fail check by default)
Definition: TypeTools.hpp:58
Check to see if something is copyable pointer.
Definition: TypeTools.hpp:73
static bool const value
Definition: TypeTools.hpp:74
Check to see if something is a shared pointer.
Definition: TypeTools.hpp:64
Check to see if something is a vector (fail check by default)
Definition: TypeTools.hpp:49
A copy of std::void_t from C++17 (helper for C++11 and C++14)
Definition: TypeTools.hpp:40
void type
Definition: TypeTools.hpp:40