CLI11  1.9.0
TypeTools.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 "StringTools.hpp"
7 #include <cstdint>
8 #include <exception>
9 #include <memory>
10 #include <string>
11 #include <type_traits>
12 #include <vector>
13 
14 namespace CLI {
15 
16 // Type tools
17 
18 // Utilities for type enabling
19 namespace detail {
20 // Based generally on https://rmf.io/cxx11/almost-static-if
22 enum class enabler {};
23 
25 constexpr enabler dummy = {};
26 } // namespace detail
27 
33 template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
34 
36 template <typename... Ts> struct make_void { using type = void; };
37 
39 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
40 
42 template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;
43 
45 template <typename T> struct is_vector : std::false_type {};
46 
48 template <class T, class A> struct is_vector<std::vector<T, A>> : std::true_type {};
49 
51 template <class T, class A> struct is_vector<const std::vector<T, A>> : std::true_type {};
52 
54 template <typename T> struct is_bool : std::false_type {};
55 
57 template <> struct is_bool<bool> : std::true_type {};
58 
60 template <typename T> struct is_shared_ptr : std::false_type {};
61 
63 template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
64 
66 template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};
67 
69 template <typename T> struct is_copyable_ptr {
70  static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
71 };
72 
74 template <typename T> struct IsMemberType { using type = T; };
75 
77 template <> struct IsMemberType<const char *> { using type = std::string; };
78 
79 namespace detail {
80 
81 // These are utilities for IsMember and other transforming objects
82 
85 
87 template <typename T, typename Enable = void> struct element_type { using type = T; };
88 
89 template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
90  using type = typename std::pointer_traits<T>::element_type;
91 };
92 
95 template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };
96 
98 template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
99  using value_type = typename T::value_type;
100  using first_type = typename std::remove_const<value_type>::type;
101  using second_type = typename std::remove_const<value_type>::type;
102 
104  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
105  return std::forward<Q>(pair_value);
106  }
108  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
109  return std::forward<Q>(pair_value);
110  }
111 };
112 
115 template <typename T>
117  T,
118  conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
119  : std::true_type {
120  using value_type = typename T::value_type;
121  using first_type = typename std::remove_const<typename value_type::first_type>::type;
122  using second_type = typename std::remove_const<typename value_type::second_type>::type;
123 
125  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
126  return std::get<0>(std::forward<Q>(pair_value));
127  }
129  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
130  return std::get<1>(std::forward<Q>(pair_value));
131  }
132 };
133 
134 // Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning
135 // in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in
136 // brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a
137 // little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out.
138 // But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be
139 // suppressed
140 #ifdef __GNUC__
141 #pragma GCC diagnostic push
142 #pragma GCC diagnostic ignored "-Wnarrowing"
143 #endif
144 // check for constructibility from a specific type and copy assignable used in the parse detection
145 template <typename T, typename C> class is_direct_constructible {
146  template <typename TT, typename CC>
147  static auto test(int, std::true_type) -> decltype(
148 // NVCC warns about narrowing conversions here
149 #ifdef __CUDACC__
150 #pragma diag_suppress 2361
151 #endif
152  TT { std::declval<CC>() }
153 #ifdef __CUDACC__
154 #pragma diag_default 2361
155 #endif
156  ,
157  std::is_move_assignable<TT>());
158 
159  template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
160 
161  template <typename, typename> static auto test(...) -> std::false_type;
162 
163  public:
164  static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
165 };
166 #ifdef __GNUC__
167 #pragma GCC diagnostic pop
168 #endif
169 
170 // Check for output streamability
171 // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
172 
173 template <typename T, typename S = std::ostringstream> class is_ostreamable {
174  template <typename TT, typename SS>
175  static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
176 
177  template <typename, typename> static auto test(...) -> std::false_type;
178 
179  public:
180  static constexpr bool value = decltype(test<T, S>(0))::value;
181 };
182 
184 template <typename T, typename S = std::istringstream> class is_istreamable {
185  template <typename TT, typename SS>
186  static auto test(int) -> decltype(std::declval<SS &>() >> std::declval<TT &>(), std::true_type());
187 
188  template <typename, typename> static auto test(...) -> std::false_type;
189 
190  public:
191  static constexpr bool value = decltype(test<T, S>(0))::value;
192 };
193 
195 template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
196 bool from_stream(const std::string &istring, T &obj) {
197  std::istringstream is;
198  is.str(istring);
199  is >> obj;
200  return !is.fail() && !is.rdbuf()->in_avail();
201 }
202 
203 template <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> = detail::dummy>
204 bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
205  return false;
206 }
207 
208 // Check for tuple like types, as in classes with a tuple_size type trait
209 template <typename S> class is_tuple_like {
210  template <typename SS>
211  // static auto test(int)
212  // -> decltype(std::conditional<(std::tuple_size<SS>::value > 0), std::true_type, std::false_type>::type());
213  static auto test(int) -> decltype(std::tuple_size<SS>::value, std::true_type{});
214  template <typename> static auto test(...) -> std::false_type;
215 
216  public:
217  static constexpr bool value = decltype(test<S>(0))::value;
218 };
219 
221 template <typename T, enable_if_t<std::is_constructible<std::string, T>::value, detail::enabler> = detail::dummy>
222 auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
223  return std::forward<T>(value);
224 }
225 
227 template <typename T,
230 std::string to_string(T &&value) {
231  std::stringstream stream;
232  stream << value;
233  return stream.str();
234 }
235 
237 template <typename T,
241 std::string to_string(T &&) {
242  return std::string{};
243 }
244 
246 template <typename T,
247  enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
248  is_vector<typename std::remove_reference<typename std::remove_const<T>::type>::type>::value,
250 std::string to_string(T &&variable) {
251  std::vector<std::string> defaults;
252  defaults.reserve(variable.size());
253  auto cval = variable.begin();
254  auto end = variable.end();
255  while(cval != end) {
256  defaults.emplace_back(CLI::detail::to_string(*cval));
257  ++cval;
258  }
259  return std::string("[" + detail::join(defaults) + "]");
260 }
261 
263 template <typename T1,
264  typename T2,
265  typename T,
266  enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
267 auto checked_to_string(T &&value) -> decltype(to_string(std::forward<T>(value))) {
268  return to_string(std::forward<T>(value));
269 }
270 
272 template <typename T1,
273  typename T2,
274  typename T,
276 std::string checked_to_string(T &&) {
277  return std::string{};
278 }
280 template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
281 std::string value_string(const T &value) {
282  return std::to_string(value);
283 }
285 template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
286 std::string value_string(const T &value) {
287  return std::to_string(static_cast<typename std::underlying_type<T>::type>(value));
288 }
290 template <typename T,
291  enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
292 auto value_string(const T &value) -> decltype(to_string(value)) {
293  return to_string(value);
294 }
295 
297 template <typename T, typename Enable = void> struct type_count { static const int value{0}; };
298 
300 template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
301  static constexpr int value{std::tuple_size<T>::value};
302 };
304 template <typename T>
305 struct type_count<
306  T,
307  typename std::enable_if<!is_vector<T>::value && !is_tuple_like<T>::value && !std::is_void<T>::value>::type> {
308  static constexpr int value{1};
309 };
310 
312 template <typename T> struct type_count<T, typename std::enable_if<is_vector<T>::value>::type> {
315 };
316 
318 template <typename T, typename Enable = void> struct expected_count { static const int value{0}; };
319 
321 template <typename T>
322 struct expected_count<T, typename std::enable_if<!is_vector<T>::value && !std::is_void<T>::value>::type> {
323  static constexpr int value{1};
324 };
326 template <typename T> struct expected_count<T, typename std::enable_if<is_vector<T>::value>::type> {
327  static constexpr int value{expected_max_vector_size};
328 };
329 
330 // Enumeration of the different supported categorizations of objects
331 enum class object_category : int {
332  integral_value = 2,
333  unsigned_integral = 4,
334  enumeration = 6,
335  boolean_value = 8,
336  floating_point = 10,
340  vector_value = 30,
341  tuple_value = 35,
342  // string assignable or greater used in a condition so anything string like must come last
343  string_assignable = 50,
345  other = 200,
346 
347 };
348 
350 template <typename T, typename Enable = void> struct classify_object {
352 };
353 
355 template <typename T>
357  typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value &&
358  !is_bool<T>::value && !std::is_enum<T>::value>::type> {
360 };
361 
363 template <typename T>
365  T,
366  typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value>::type> {
368 };
369 
371 template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
373 };
374 
376 template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
378 };
379 
381 template <typename T>
383  T,
384  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
385  std::is_assignable<T &, std::string>::value && !is_vector<T>::value>::type> {
387 };
388 
390 template <typename T>
392  T,
393  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
394  !std::is_assignable<T &, std::string>::value &&
395  std::is_constructible<T, std::string>::value && !is_vector<T>::value>::type> {
397 };
398 
400 template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
402 };
403 
406 template <typename T> struct uncommon_type {
407  using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
408  !std::is_assignable<T &, std::string>::value &&
409  !std::is_constructible<T, std::string>::value && !is_vector<T>::value &&
410  !std::is_enum<T>::value,
411  std::true_type,
412  std::false_type>::type;
413  static constexpr bool value = type::value;
414 };
415 
417 template <typename T>
419  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
420  is_direct_constructible<T, double>::value &&
421  is_direct_constructible<T, int>::value>::type> {
423 };
424 
426 template <typename T>
428  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
429  !is_direct_constructible<T, double>::value &&
430  is_direct_constructible<T, int>::value>::type> {
432 };
433 
435 template <typename T>
437  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
438  is_direct_constructible<T, double>::value &&
439  !is_direct_constructible<T, int>::value>::type> {
441 };
442 
444 template <typename T>
446  typename std::enable_if<(type_count<T>::value >= 2 && !is_vector<T>::value) ||
447  (is_tuple_like<T>::value && uncommon_type<T>::value &&
448  !is_direct_constructible<T, double>::value &&
449  !is_direct_constructible<T, int>::value)>::type> {
451 };
452 
454 template <typename T> struct classify_object<T, typename std::enable_if<is_vector<T>::value>::type> {
456 };
457 
458 // Type name print
459 
463 
464 template <typename T,
468 constexpr const char *type_name() {
469  return "INT";
470 }
471 
472 template <typename T,
474 constexpr const char *type_name() {
475  return "UINT";
476 }
477 
478 template <typename T,
479  enable_if_t<classify_object<T>::value == object_category::floating_point ||
483 constexpr const char *type_name() {
484  return "FLOAT";
485 }
486 
488 template <typename T,
489  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
490 constexpr const char *type_name() {
491  return "ENUM";
492 }
493 
495 template <typename T,
496  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
497 constexpr const char *type_name() {
498  return "BOOLEAN";
499 }
500 
502 template <typename T,
503  enable_if_t<classify_object<T>::value >= object_category::string_assignable, detail::enabler> = detail::dummy>
504 constexpr const char *type_name() {
505  return "TEXT";
506 }
507 
509 template <typename T,
510  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count<T>::value == 1,
512 inline std::string type_name() {
513  return type_name<typename std::tuple_element<0, T>::type>();
514 }
515 
517 template <typename T, std::size_t I>
518 inline typename std::enable_if<I == type_count<T>::value, std::string>::type tuple_name() {
519  return std::string{};
520 }
521 
523 template <typename T, std::size_t I>
524  inline typename std::enable_if < I<type_count<T>::value, std::string>::type tuple_name() {
525  std::string str = std::string(type_name<typename std::tuple_element<I, T>::type>()) + ',' + tuple_name<T, I + 1>();
526  if(str.back() == ',')
527  str.pop_back();
528  return str;
529 }
530 
532 template <typename T,
533  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count<T>::value >= 2,
535 std::string type_name() {
536  auto tname = std::string(1, '[') + tuple_name<T, 0>();
537  tname.push_back(']');
538  return tname;
539 }
540 
542 template <typename T,
544 inline std::string type_name() {
545  return type_name<typename T::value_type>();
546 }
547 
548 // Lexical cast
549 
551 inline int64_t to_flag_value(std::string val) {
552  static const std::string trueString("true");
553  static const std::string falseString("false");
554  if(val == trueString) {
555  return 1;
556  }
557  if(val == falseString) {
558  return -1;
559  }
560  val = detail::to_lower(val);
561  int64_t ret;
562  if(val.size() == 1) {
563  if(val[0] >= '1' && val[0] <= '9') {
564  return (static_cast<int64_t>(val[0]) - '0');
565  }
566  switch(val[0]) {
567  case '0':
568  case 'f':
569  case 'n':
570  case '-':
571  ret = -1;
572  break;
573  case 't':
574  case 'y':
575  case '+':
576  ret = 1;
577  break;
578  default:
579  throw std::invalid_argument("unrecognized character");
580  }
581  return ret;
582  }
583  if(val == trueString || val == "on" || val == "yes" || val == "enable") {
584  ret = 1;
585  } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
586  ret = -1;
587  } else {
588  ret = std::stoll(val);
589  }
590  return ret;
591 }
592 
594 template <typename T,
596 bool lexical_cast(const std::string &input, T &output) {
597  try {
598  std::size_t n = 0;
599  std::int64_t output_ll = std::stoll(input, &n, 0);
600  output = static_cast<T>(output_ll);
601  return n == input.size() && static_cast<std::int64_t>(output) == output_ll;
602  } catch(const std::invalid_argument &) {
603  return false;
604  } catch(const std::out_of_range &) {
605  return false;
606  }
607 }
608 
610 template <typename T,
612 bool lexical_cast(const std::string &input, T &output) {
613  if(!input.empty() && input.front() == '-')
614  return false; // std::stoull happily converts negative values to junk without any errors.
615 
616  try {
617  std::size_t n = 0;
618  std::uint64_t output_ll = std::stoull(input, &n, 0);
619  output = static_cast<T>(output_ll);
620  return n == input.size() && static_cast<std::uint64_t>(output) == output_ll;
621  } catch(const std::invalid_argument &) {
622  return false;
623  } catch(const std::out_of_range &) {
624  return false;
625  }
626 }
627 
629 template <typename T,
630  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
631 bool lexical_cast(const std::string &input, T &output) {
632  try {
633  auto out = to_flag_value(input);
634  output = (out > 0);
635  return true;
636  } catch(const std::invalid_argument &) {
637  return false;
638  } catch(const std::out_of_range &) {
639  // 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
640  // valid all we care about the sign
641  output = (input[0] != '-');
642  return true;
643  }
644 }
645 
647 template <typename T,
648  enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
649 bool lexical_cast(const std::string &input, T &output) {
650  try {
651  std::size_t n = 0;
652  output = static_cast<T>(std::stold(input, &n));
653  return n == input.size();
654  } catch(const std::invalid_argument &) {
655  return false;
656  } catch(const std::out_of_range &) {
657  return false;
658  }
659 }
660 
662 template <typename T,
663  enable_if_t<classify_object<T>::value == object_category::string_assignable, detail::enabler> = detail::dummy>
664 bool lexical_cast(const std::string &input, T &output) {
665  output = input;
666  return true;
667 }
668 
670 template <
671  typename T,
672  enable_if_t<classify_object<T>::value == object_category::string_constructible, detail::enabler> = detail::dummy>
673 bool lexical_cast(const std::string &input, T &output) {
674  output = T(input);
675  return true;
676 }
677 
679 template <typename T,
680  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
681 bool lexical_cast(const std::string &input, T &output) {
682  typename std::underlying_type<T>::type val;
683  bool retval = detail::lexical_cast(input, val);
684  if(!retval) {
685  return false;
686  }
687  output = static_cast<T>(val);
688  return true;
689 }
690 
692 template <
693  typename T,
694  enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
695 bool lexical_cast(const std::string &input, T &output) {
696  int val;
697  if(lexical_cast(input, val)) {
698  output = T(val);
699  return true;
700  } else {
701  double dval;
702  if(lexical_cast(input, dval)) {
703  output = T{dval};
704  return true;
705  }
706  }
707  return from_stream(input, output);
708 }
709 
711 template <
712  typename T,
713  enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
714 bool lexical_cast(const std::string &input, T &output) {
715  int val;
716  if(lexical_cast(input, val)) {
717  output = T(val);
718  return true;
719  }
720  return from_stream(input, output);
721 }
722 
724 template <
725  typename T,
726  enable_if_t<classify_object<T>::value == object_category::double_constructible, detail::enabler> = detail::dummy>
727 bool lexical_cast(const std::string &input, T &output) {
728  double 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 <typename T, enable_if_t<classify_object<T>::value == object_category::other, detail::enabler> = detail::dummy>
738 bool lexical_cast(const std::string &input, T &output) {
739  static_assert(is_istreamable<T>::value,
740  "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it "
741  "is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
742  return from_stream(input, output);
743 }
744 
746 template <
747  typename T,
748  typename XC,
749  enable_if_t<std::is_same<T, XC>::value && (classify_object<T>::value == object_category::string_assignable ||
752 bool lexical_assign(const std::string &input, T &output) {
753  return lexical_cast(input, output);
754 }
755 
757 template <typename T,
758  typename XC,
762 bool lexical_assign(const std::string &input, T &output) {
763  if(input.empty()) {
764  output = T{};
765  return true;
766  }
767  return lexical_cast(input, output);
768 }
769 
771 template <
772  typename T,
773  typename XC,
774  enable_if_t<!std::is_same<T, XC>::value && std::is_assignable<T &, XC &>::value, detail::enabler> = detail::dummy>
775 bool lexical_assign(const std::string &input, T &output) {
776  XC val{};
777  bool parse_result = (!input.empty()) ? lexical_cast<XC>(input, val) : true;
778  if(parse_result) {
779  output = val;
780  }
781  return parse_result;
782 }
783 
785 template <typename T,
786  typename XC,
787  enable_if_t<!std::is_same<T, XC>::value && !std::is_assignable<T &, XC &>::value &&
788  std::is_move_assignable<T>::value,
790 bool lexical_assign(const std::string &input, T &output) {
791  XC val{};
792  bool parse_result = input.empty() ? true : lexical_cast<XC>(input, val);
793  if(parse_result) {
794  output = T(val); // use () form of constructor to allow some implicit conversions
795  }
796  return parse_result;
797 }
799 template <
800  typename T,
801  typename XC,
802  enable_if_t<!is_tuple_like<T>::value && !is_tuple_like<XC>::value && !is_vector<T>::value && !is_vector<XC>::value,
804 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
805  return lexical_assign<T, XC>(strings[0], output);
806 }
807 
809 template <typename T,
810  typename XC,
812 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
813  typename std::tuple_element<0, XC>::type v1;
814  typename std::tuple_element<1, XC>::type v2;
815  bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);
816  if(strings.size() > 1) {
817  retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);
818  }
819  if(retval) {
820  output = T{v1, v2};
821  }
822  return retval;
823 }
824 
826 template <class T,
827  class XC,
828  enable_if_t<expected_count<T>::value == expected_max_vector_size &&
831 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
832  output.clear();
833  output.reserve(strings.size());
834  for(const auto &elem : strings) {
835 
836  output.emplace_back();
837  bool retval = lexical_assign<typename T::value_type, typename XC::value_type>(elem, output.back());
838  if(!retval) {
839  return false;
840  }
841  }
842  return (!output.empty());
843 }
844 
846 template <class T,
847  class XC,
848  enable_if_t<expected_count<T>::value == expected_max_vector_size &&
851 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
852  output.clear();
853  for(std::size_t ii = 0; ii < strings.size(); ii += 2) {
854 
855  typename std::tuple_element<0, typename XC::value_type>::type v1;
856  typename std::tuple_element<1, typename XC::value_type>::type v2;
857  bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[ii], v1);
858  if(strings.size() > ii + 1) {
859  retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[ii + 1], v2);
860  }
861  if(retval) {
862  output.emplace_back(v1, v2);
863  } else {
864  return false;
865  }
866  }
867  return (!output.empty());
868 }
869 
871 template <class T,
872  class XC,
874  (type_count<XC>::value == 1),
876 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
877  bool retval = true;
878  output.clear();
879  output.reserve(strings.size());
880  for(const auto &elem : strings) {
881 
882  output.emplace_back();
883  retval = retval && lexical_assign<typename T::value_type, XC>(elem, output.back());
884  }
885  return (!output.empty()) && retval;
886 }
887 // This one is last since it can call other lexical_conversion functions
889 template <typename T,
890  typename XC,
891  enable_if_t<!is_tuple_like<T>::value && !is_vector<T>::value && is_vector<XC>::value, detail::enabler> =
893 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
894 
895  if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) {
896  XC val;
897  auto retval = lexical_conversion<XC, XC>(strings, val);
898  output = T{val};
899  return retval;
900  }
901  output = T{};
902  return true;
903 }
904 
906 template <class T, class XC, std::size_t I>
907 inline typename std::enable_if<I >= type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &,
908  T &) {
909  return true;
910 }
912 template <class T, class XC, std::size_t I>
913  inline typename std::enable_if <
914  I<type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &strings, T &output) {
915  bool retval = true;
916  if(strings.size() > I) {
917  retval = retval && lexical_assign<typename std::tuple_element<I, T>::type,
918  typename std::conditional<is_tuple_like<XC>::value,
919  typename std::tuple_element<I, XC>::type,
920  XC>::type>(strings[I], std::get<I>(output));
921  }
922  retval = retval && tuple_conversion<T, XC, I + 1>(strings, output);
923  return retval;
924 }
925 
927 template <class T, class XC, enable_if_t<is_tuple_like<T>::value, detail::enabler> = detail::dummy>
928 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
929  static_assert(
931  "if the conversion type is defined as a tuple it must be the same size as the type you are converting to");
932  return tuple_conversion<T, XC, 0>(strings, output);
933 }
934 
936 template <class T,
937  class XC,
938  enable_if_t<expected_count<T>::value == expected_max_vector_size &&
941 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
942  bool retval = true;
943  output.clear();
944  std::vector<std::string> temp;
945  std::size_t ii = 0;
946  std::size_t icount = 0;
947  std::size_t xcm = type_count<XC>::value;
948  while(ii < strings.size()) {
949  temp.push_back(strings[ii]);
950  ++ii;
951  ++icount;
952  if(icount == xcm || temp.back().empty()) {
953  if(static_cast<int>(xcm) == expected_max_vector_size) {
954  temp.pop_back();
955  }
956  output.emplace_back();
957  retval = retval && lexical_conversion<typename T::value_type, typename XC::value_type>(temp, output.back());
958  temp.clear();
959  if(!retval) {
960  return false;
961  }
962  icount = 0;
963  }
964  }
965  return retval;
966 }
972 template <typename T,
973  enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
974 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
975  int64_t count{0};
976  for(auto &flag : flags) {
977  count += detail::to_flag_value(flag);
978  }
979  output = (count > 0) ? static_cast<T>(count) : T{0};
980 }
981 
987 template <typename T,
988  enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
989 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
990  int64_t count{0};
991  for(auto &flag : flags) {
992  count += detail::to_flag_value(flag);
993  }
994  output = static_cast<T>(count);
995 }
996 
997 } // namespace detail
998 } // namespace CLI
CLI::detail::object_category::string_constructible
@ string_constructible
CLI::detail::is_istreamable::value
static constexpr bool value
Definition: TypeTools.hpp:191
CLI::detail::classify_object
some type that is not otherwise recognized
Definition: TypeTools.hpp:350
CLI::detail::object_category::unsigned_integral
@ unsigned_integral
CLI::detail::tuple_name
std::size_t I std::enable_if< I==type_count< T >::value, std::string >::type tuple_name()
Definition: TypeTools.hpp:518
CLI::detail::pair_adaptor< T, conditional_t< false, void_t< typename T::value_type::first_type, typename T::value_type::second_type >, void > >::second
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:129
CLI::detail::pair_adaptor< T, conditional_t< false, void_t< typename T::value_type::first_type, typename T::value_type::second_type >, void > >::second_type
typename std::remove_const< typename value_type::second_type >::type second_type
Definition: TypeTools.hpp:122
CLI::is_copyable_ptr
Check to see if something is copyable pointer.
Definition: TypeTools.hpp:69
CLI::detail::object_category::boolean_value
@ boolean_value
CLI::detail::expected_count::value
static const int value
Definition: TypeTools.hpp:318
CLI::IsMemberType::type
T type
Definition: TypeTools.hpp:74
CLI::detail::enabler
enabler
Simple empty scoped class.
Definition: TypeTools.hpp:22
CLI::detail::expected_max_vector_size
constexpr int expected_max_vector_size
Definition: StringTools.hpp:36
CLI::is_vector
Check to see if something is a vector (fail check by default)
Definition: TypeTools.hpp:45
CLI::detail::element_value_type::type
typename element_type< T >::type::value_type type
Definition: TypeTools.hpp:95
CLI::detail::pair_adaptor< T, conditional_t< false, void_t< typename T::value_type::first_type, typename T::value_type::second_type >, void > >::first
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:125
CLI::detail::pair_adaptor::second_type
typename std::remove_const< value_type >::type second_type
Definition: TypeTools.hpp:101
CLI::is_copyable_ptr::value
static const bool value
Definition: TypeTools.hpp:70
CLI::detail::object_category::integral_value
@ integral_value
CLI::detail::uncommon_type::value
static constexpr bool value
Definition: TypeTools.hpp:413
CLI::detail::object_category::other
@ other
CLI::detail::object_category
object_category
Definition: TypeTools.hpp:331
CLI::detail::object_category::integer_constructible
@ integer_constructible
CLI::detail::element_type
not a pointer
Definition: TypeTools.hpp:87
CLI::enable_if_t
typename std::enable_if< B, T >::type enable_if_t
Definition: TypeTools.hpp:33
CLI::detail::is_ostreamable
Definition: TypeTools.hpp:173
CLI::detail::uncommon_type::type
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:412
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::detail::pair_adaptor< T, conditional_t< false, void_t< typename T::value_type::first_type, typename T::value_type::second_type >, void > >::value_type
typename T::value_type value_type
Definition: TypeTools.hpp:120
CLI::detail::classify_object::value
static constexpr object_category value
Definition: TypeTools.hpp:351
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::pair_adaptor< T, conditional_t< false, void_t< typename T::value_type::first_type, typename T::value_type::second_type >, void > >::first_type
typename std::remove_const< typename value_type::first_type >::type first_type
Definition: TypeTools.hpp:121
CLI::detail::is_direct_constructible
Definition: TypeTools.hpp:145
CLI::detail::lexical_cast
bool lexical_cast(const std::string &input, T &output)
Signed integers.
Definition: TypeTools.hpp:596
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::detail::object_category::floating_point
@ floating_point
CLI::detail::dummy
constexpr enabler dummy
An instance to use in EnableIf.
Definition: TypeTools.hpp:25
CLI::is_shared_ptr
Check to see if something is a shared pointer.
Definition: TypeTools.hpp:60
CLI::detail::pair_adaptor::second
static auto second(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:108
CLI::detail::element_value_type
Definition: TypeTools.hpp:95
CLI::make_void::type
void type
Definition: TypeTools.hpp:36
CLI::detail::lexical_conversion
bool lexical_conversion(const std::vector< std ::string > &strings, T &output)
Lexical conversion if there is only one element.
Definition: TypeTools.hpp:804
CLI::detail::tuple_conversion
bool ::type tuple_conversion(const std::vector< std::string > &, T &)
Definition: TypeTools.hpp:907
CLI::detail::to_lower
std::string to_lower(std::string str)
Return a lower case version of a string.
Definition: StringTools.hpp:196
CLI::detail::is_ostreamable::value
static constexpr bool value
Definition: TypeTools.hpp:180
CLI::is_bool
Check to see if something is bool (fail check by default)
Definition: TypeTools.hpp:54
CLI::detail::object_category::string_assignable
@ string_assignable
CLI::void_t
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:39
CLI::detail::element_type< T, typename std::enable_if< is_copyable_ptr< T >::value >::type >::type
typename std::pointer_traits< T >::element_type type
Definition: TypeTools.hpp:90
StringTools.hpp
CLI::detail::pair_adaptor::value_type
typename T::value_type value_type
Definition: TypeTools.hpp:99
CLI::detail::type_count::value
static const int value
Definition: TypeTools.hpp:297
CLI::detail::element_type::type
T type
Definition: TypeTools.hpp:87
CLI::detail::checked_to_string
auto checked_to_string(T &&value) -> decltype(to_string(std::forward< T >(value)))
special template overload
Definition: TypeTools.hpp:267
CLI::detail::is_istreamable
Check for input streamability.
Definition: TypeTools.hpp:184
CLI::detail::object_category::vector_value
@ vector_value
CLI::conditional_t
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:42
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::detail::is_tuple_like::value
static constexpr bool value
Definition: TypeTools.hpp:217
CLI::detail::type_name
constexpr const char * type_name()
Print name for enumeration types.
Definition: TypeTools.hpp:468
CLI::detail::to_flag_value
int64_t to_flag_value(std::string val)
Convert a flag into an integer value typically binary flags.
Definition: TypeTools.hpp:551
CLI::detail::expected_count
This will only trigger for actual void type.
Definition: TypeTools.hpp:318
CLI::detail::is_tuple_like
Definition: TypeTools.hpp:209
CLI::detail::object_category::number_constructible
@ number_constructible
CLI::detail::lexical_assign
bool lexical_assign(const std::string &input, T &output)
Assign a value through lexical cast operations.
Definition: TypeTools.hpp:752
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::IsMemberType
This can be specialized to override the type deduction for IsMember.
Definition: TypeTools.hpp:74
CLI::detail::from_stream
bool from_stream(const std::string &istring, T &obj)
Templated operation to get a value from a stream.
Definition: TypeTools.hpp:196
CLI::detail::type_count
This will only trigger for actual void type.
Definition: TypeTools.hpp:297
CLI::detail::uncommon_type
Definition: TypeTools.hpp:406
CLI::detail::object_category::tuple_value
@ tuple_value
CLI::detail::object_category::enumeration
@ enumeration
CLI::IsMemberType< const char * >::type
std::string type
Definition: TypeTools.hpp:77
CLI::make_void
A copy of std::void_t from C++17 (helper for C++11 and C++14)
Definition: TypeTools.hpp:36
CLI::detail::to_string
std::string to_string(T &&value)
Convert an object to a string (streaming must be supported for that type)
Definition: TypeTools.hpp:230
CLI::detail::object_category::double_constructible
@ double_constructible
CLI::detail::is_direct_constructible::value
static constexpr bool value
Definition: TypeTools.hpp:164