CLI11  2.1.0
TypeTools.hpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
2 // under NSF AWARD 1414736 and by the respective contributors.
3 // All rights reserved.
4 //
5 // SPDX-License-Identifier: BSD-3-Clause
6 
7 #pragma once
8 
9 // [CLI11:public_includes:set]
10 #include <cstdint>
11 #include <exception>
12 #include <memory>
13 #include <string>
14 #include <type_traits>
15 #include <utility>
16 #include <vector>
17 // [CLI11:public_includes:end]
18 
19 #include "StringTools.hpp"
20 
21 namespace CLI {
22 // [CLI11:type_tools_hpp:verbatim]
23 
24 // Type tools
25 
26 // Utilities for type enabling
27 namespace detail {
28 // Based generally on https://rmf.io/cxx11/almost-static-if
30 enum class enabler {};
31 
33 constexpr enabler dummy = {};
34 } // namespace detail
35 
41 template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
42 
44 template <typename... Ts> struct make_void { using type = void; };
45 
47 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
48 
50 template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;
51 
53 template <typename T> struct is_bool : std::false_type {};
54 
56 template <> struct is_bool<bool> : std::true_type {};
57 
59 template <typename T> struct is_shared_ptr : std::false_type {};
60 
62 template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
63 
65 template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};
66 
68 template <typename T> struct is_copyable_ptr {
69  static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
70 };
71 
73 template <typename T> struct IsMemberType { using type = T; };
74 
76 template <> struct IsMemberType<const char *> { using type = std::string; };
77 
78 namespace detail {
79 
80 // These are utilities for IsMember and other transforming objects
81 
84 
86 template <typename T, typename Enable = void> struct element_type { using type = T; };
87 
88 template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
89  using type = typename std::pointer_traits<T>::element_type;
90 };
91 
94 template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };
95 
97 template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
98  using value_type = typename T::value_type;
99  using first_type = typename std::remove_const<value_type>::type;
100  using second_type = typename std::remove_const<value_type>::type;
101 
103  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
104  return std::forward<Q>(pair_value);
105  }
107  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
108  return std::forward<Q>(pair_value);
109  }
110 };
111 
114 template <typename T>
116  T,
117  conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
118  : std::true_type {
119  using value_type = typename T::value_type;
120  using first_type = typename std::remove_const<typename value_type::first_type>::type;
121  using second_type = typename std::remove_const<typename value_type::second_type>::type;
122 
124  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
125  return std::get<0>(std::forward<Q>(pair_value));
126  }
128  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
129  return std::get<1>(std::forward<Q>(pair_value));
130  }
131 };
132 
133 // Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning
134 // in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in
135 // brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a
136 // little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out.
137 // But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be
138 // suppressed
139 #ifdef __GNUC__
140 #pragma GCC diagnostic push
141 #pragma GCC diagnostic ignored "-Wnarrowing"
142 #endif
143 // check for constructibility from a specific type and copy assignable used in the parse detection
144 template <typename T, typename C> class is_direct_constructible {
145  template <typename TT, typename CC>
146  static auto test(int, std::true_type) -> decltype(
147 // NVCC warns about narrowing conversions here
148 #ifdef __CUDACC__
149 #pragma diag_suppress 2361
150 #endif
151  TT { std::declval<CC>() }
152 #ifdef __CUDACC__
153 #pragma diag_default 2361
154 #endif
155  ,
156  std::is_move_assignable<TT>());
157 
158  template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
159 
160  template <typename, typename> static auto test(...) -> std::false_type;
161 
162  public:
163  static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
164 };
165 #ifdef __GNUC__
166 #pragma GCC diagnostic pop
167 #endif
168 
169 // Check for output streamability
170 // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
171 
172 template <typename T, typename S = std::ostringstream> class is_ostreamable {
173  template <typename TT, typename SS>
174  static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
175 
176  template <typename, typename> static auto test(...) -> std::false_type;
177 
178  public:
179  static constexpr bool value = decltype(test<T, S>(0))::value;
180 };
181 
183 template <typename T, typename S = std::istringstream> class is_istreamable {
184  template <typename TT, typename SS>
185  static auto test(int) -> decltype(std::declval<SS &>() >> std::declval<TT &>(), std::true_type());
186 
187  template <typename, typename> static auto test(...) -> std::false_type;
188 
189  public:
190  static constexpr bool value = decltype(test<T, S>(0))::value;
191 };
192 
194 template <typename T> class is_complex {
195  template <typename TT>
196  static auto test(int) -> decltype(std::declval<TT>().real(), std::declval<TT>().imag(), std::true_type());
197 
198  template <typename> static auto test(...) -> std::false_type;
199 
200  public:
201  static constexpr bool value = decltype(test<T>(0))::value;
202 };
203 
205 template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
206 bool from_stream(const std::string &istring, T &obj) {
207  std::istringstream is;
208  is.str(istring);
209  is >> obj;
210  return !is.fail() && !is.rdbuf()->in_avail();
211 }
212 
213 template <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> = detail::dummy>
214 bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
215  return false;
216 }
217 
218 // check to see if an object is a mutable container (fail by default)
219 template <typename T, typename _ = void> struct is_mutable_container : std::false_type {};
220 
224 template <typename T>
226  T,
227  conditional_t<false,
228  void_t<typename T::value_type,
229  decltype(std::declval<T>().end()),
230  decltype(std::declval<T>().clear()),
231  decltype(std::declval<T>().insert(std::declval<decltype(std::declval<T>().end())>(),
232  std::declval<const typename T::value_type &>()))>,
233  void>>
234  : public conditional_t<std::is_constructible<T, std::string>::value, std::false_type, std::true_type> {};
235 
236 // check to see if an object is a mutable container (fail by default)
237 template <typename T, typename _ = void> struct is_readable_container : std::false_type {};
238 
242 template <typename T>
244  T,
245  conditional_t<false, void_t<decltype(std::declval<T>().end()), decltype(std::declval<T>().begin())>, void>>
246  : public std::true_type {};
247 
248 // check to see if an object is a wrapper (fail by default)
249 template <typename T, typename _ = void> struct is_wrapper : std::false_type {};
250 
251 // check if an object is a wrapper (it has a value_type defined)
252 template <typename T>
253 struct is_wrapper<T, conditional_t<false, void_t<typename T::value_type>, void>> : public std::true_type {};
254 
255 // Check for tuple like types, as in classes with a tuple_size type trait
256 template <typename S> class is_tuple_like {
257  template <typename SS>
258  // static auto test(int)
259  // -> decltype(std::conditional<(std::tuple_size<SS>::value > 0), std::true_type, std::false_type>::type());
260  static auto test(int) -> decltype(std::tuple_size<typename std::decay<SS>::type>::value, std::true_type{});
261  template <typename> static auto test(...) -> std::false_type;
262 
263  public:
264  static constexpr bool value = decltype(test<S>(0))::value;
265 };
266 
268 template <typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
269 auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
270  return std::forward<T>(value);
271 }
272 
274 template <typename T,
275  enable_if_t<std::is_constructible<std::string, T>::value && !std::is_convertible<T, std::string>::value,
277 std::string to_string(const T &value) {
278  return std::string(value);
279 }
280 
282 template <typename T,
283  enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
286 std::string to_string(T &&value) {
287  std::stringstream stream;
288  stream << value;
289  return stream.str();
290 }
291 
293 template <typename T,
295  !is_readable_container<typename std::remove_const<T>::type>::value,
297 std::string to_string(T &&) {
298  return std::string{};
299 }
300 
302 template <typename T,
303  enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
304  is_readable_container<T>::value,
306 std::string to_string(T &&variable) {
307  std::vector<std::string> defaults;
308  auto cval = variable.begin();
309  auto end = variable.end();
310  while(cval != end) {
311  defaults.emplace_back(CLI::detail::to_string(*cval));
312  ++cval;
313  }
314  return std::string("[" + detail::join(defaults) + "]");
315 }
316 
318 template <typename T1,
319  typename T2,
320  typename T,
321  enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
322 auto checked_to_string(T &&value) -> decltype(to_string(std::forward<T>(value))) {
323  return to_string(std::forward<T>(value));
324 }
325 
327 template <typename T1,
328  typename T2,
329  typename T,
331 std::string checked_to_string(T &&) {
332  return std::string{};
333 }
335 template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
336 std::string value_string(const T &value) {
337  return std::to_string(value);
338 }
340 template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
341 std::string value_string(const T &value) {
342  return std::to_string(static_cast<typename std::underlying_type<T>::type>(value));
343 }
345 template <typename T,
346  enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
347 auto value_string(const T &value) -> decltype(to_string(value)) {
348  return to_string(value);
349 }
350 
352 template <typename T, typename def, typename Enable = void> struct wrapped_type { using type = def; };
353 
355 template <typename T, typename def> struct wrapped_type<T, def, typename std::enable_if<is_wrapper<T>::value>::type> {
356  using type = typename T::value_type;
357 };
358 
360 template <typename T, typename Enable = void> struct type_count_base { static const int value{0}; };
361 
363 template <typename T>
365  typename std::enable_if<!is_tuple_like<T>::value && !is_mutable_container<T>::value &&
366  !std::is_void<T>::value>::type> {
367  static constexpr int value{1};
368 };
369 
371 template <typename T>
372 struct type_count_base<T, typename std::enable_if<is_tuple_like<T>::value && !is_mutable_container<T>::value>::type> {
373  static constexpr int value{std::tuple_size<T>::value};
374 };
375 
377 template <typename T> struct type_count_base<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
379 };
380 
382 
384 template <typename T> struct subtype_count;
385 
387 template <typename T> struct subtype_count_min;
388 
390 template <typename T, typename Enable = void> struct type_count { static const int value{0}; };
391 
393 template <typename T>
394 struct type_count<T,
395  typename std::enable_if<!is_wrapper<T>::value && !is_tuple_like<T>::value && !is_complex<T>::value &&
396  !std::is_void<T>::value>::type> {
397  static constexpr int value{1};
398 };
399 
401 template <typename T> struct type_count<T, typename std::enable_if<is_complex<T>::value>::type> {
402  static constexpr int value{2};
403 };
404 
406 template <typename T> struct type_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
408 };
409 
411 template <typename T>
412 struct type_count<T,
413  typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value &&
414  !is_mutable_container<T>::value>::type> {
416 };
417 
419 template <typename T, std::size_t I>
420 constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size() {
421  return 0;
422 }
423 
425 template <typename T, std::size_t I>
426  constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size() {
427  return subtype_count<typename std::tuple_element<I, T>::type>::value + tuple_type_size<T, I + 1>();
428 }
429 
431 template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
432  static constexpr int value{tuple_type_size<T, 0>()};
433 };
434 
436 template <typename T> struct subtype_count {
437  static constexpr int value{is_mutable_container<T>::value ? expected_max_vector_size : type_count<T>::value};
438 };
439 
441 template <typename T, typename Enable = void> struct type_count_min { static const int value{0}; };
442 
444 template <typename T>
445 struct type_count_min<
446  T,
447  typename std::enable_if<!is_mutable_container<T>::value && !is_tuple_like<T>::value && !is_wrapper<T>::value &&
448  !is_complex<T>::value && !std::is_void<T>::value>::type> {
449  static constexpr int value{type_count<T>::value};
450 };
451 
453 template <typename T> struct type_count_min<T, typename std::enable_if<is_complex<T>::value>::type> {
454  static constexpr int value{1};
455 };
456 
458 template <typename T>
459 struct type_count_min<
460  T,
461  typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value>::type> {
462  static constexpr int value{subtype_count_min<typename T::value_type>::value};
463 };
464 
466 template <typename T, std::size_t I>
467 constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size_min() {
468  return 0;
469 }
470 
472 template <typename T, std::size_t I>
473  constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size_min() {
474  return subtype_count_min<typename std::tuple_element<I, T>::type>::value + tuple_type_size_min<T, I + 1>();
475 }
476 
478 template <typename T> struct type_count_min<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
479  static constexpr int value{tuple_type_size_min<T, 0>()};
480 };
481 
483 template <typename T> struct subtype_count_min {
484  static constexpr int value{is_mutable_container<T>::value
486  : type_count_min<T>::value};
487 };
488 
490 template <typename T, typename Enable = void> struct expected_count { static const int value{0}; };
491 
493 template <typename T>
494 struct expected_count<T,
495  typename std::enable_if<!is_mutable_container<T>::value && !is_wrapper<T>::value &&
496  !std::is_void<T>::value>::type> {
497  static constexpr int value{1};
498 };
500 template <typename T> struct expected_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
501  static constexpr int value{expected_max_vector_size};
502 };
503 
505 template <typename T>
506 struct expected_count<T, typename std::enable_if<!is_mutable_container<T>::value && is_wrapper<T>::value>::type> {
507  static constexpr int value{expected_count<typename T::value_type>::value};
508 };
509 
510 // Enumeration of the different supported categorizations of objects
511 enum class object_category : int {
512  char_value = 1,
513  integral_value = 2,
514  unsigned_integral = 4,
515  enumeration = 6,
516  boolean_value = 8,
517  floating_point = 10,
518  number_constructible = 12,
519  double_constructible = 14,
520  integer_constructible = 16,
521  // string like types
522  string_assignable = 23,
523  string_constructible = 24,
524  other = 45,
525  // special wrapper or container types
526  wrapper_value = 50,
527  complex_number = 60,
528  tuple_value = 70,
529  container_value = 80,
530 
531 };
532 
534 
536 template <typename T, typename Enable = void> struct classify_object {
537  static constexpr object_category value{object_category::other};
538 };
539 
541 template <typename T>
542 struct classify_object<
543  T,
544  typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && std::is_signed<T>::value &&
545  !is_bool<T>::value && !std::is_enum<T>::value>::type> {
546  static constexpr object_category value{object_category::integral_value};
547 };
548 
550 template <typename T>
551 struct classify_object<T,
552  typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value &&
553  !std::is_same<T, char>::value && !is_bool<T>::value>::type> {
554  static constexpr object_category value{object_category::unsigned_integral};
555 };
556 
558 template <typename T>
559 struct classify_object<T, typename std::enable_if<std::is_same<T, char>::value && !std::is_enum<T>::value>::type> {
560  static constexpr object_category value{object_category::char_value};
561 };
562 
564 template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
565  static constexpr object_category value{object_category::boolean_value};
566 };
567 
569 template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
570  static constexpr object_category value{object_category::floating_point};
571 };
572 
574 template <typename T>
575 struct classify_object<T,
576  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
577  std::is_assignable<T &, std::string>::value>::type> {
578  static constexpr object_category value{object_category::string_assignable};
579 };
580 
582 template <typename T>
583 struct classify_object<
584  T,
585  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
586  !std::is_assignable<T &, std::string>::value && (type_count<T>::value == 1) &&
587  std::is_constructible<T, std::string>::value>::type> {
588  static constexpr object_category value{object_category::string_constructible};
589 };
590 
592 template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
593  static constexpr object_category value{object_category::enumeration};
594 };
595 
596 template <typename T> struct classify_object<T, typename std::enable_if<is_complex<T>::value>::type> {
597  static constexpr object_category value{object_category::complex_number};
598 };
599 
602 template <typename T> struct uncommon_type {
603  using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
604  !std::is_assignable<T &, std::string>::value &&
605  !std::is_constructible<T, std::string>::value && !is_complex<T>::value &&
606  !is_mutable_container<T>::value && !std::is_enum<T>::value,
607  std::true_type,
608  std::false_type>::type;
609  static constexpr bool value = type::value;
610 };
611 
613 template <typename T>
614 struct classify_object<T,
615  typename std::enable_if<(!is_mutable_container<T>::value && is_wrapper<T>::value &&
616  !is_tuple_like<T>::value && uncommon_type<T>::value)>::type> {
617  static constexpr object_category value{object_category::wrapper_value};
618 };
619 
621 template <typename T>
622 struct classify_object<T,
623  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
624  !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
625  is_direct_constructible<T, int>::value>::type> {
626  static constexpr object_category value{object_category::number_constructible};
627 };
628 
630 template <typename T>
631 struct classify_object<T,
632  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
633  !is_wrapper<T>::value && !is_direct_constructible<T, double>::value &&
634  is_direct_constructible<T, int>::value>::type> {
635  static constexpr object_category value{object_category::integer_constructible};
636 };
637 
639 template <typename T>
640 struct classify_object<T,
641  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
642  !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
643  !is_direct_constructible<T, int>::value>::type> {
644  static constexpr object_category value{object_category::double_constructible};
645 };
646 
648 template <typename T>
649 struct classify_object<
650  T,
651  typename std::enable_if<is_tuple_like<T>::value &&
652  ((type_count<T>::value >= 2 && !is_wrapper<T>::value) ||
653  (uncommon_type<T>::value && !is_direct_constructible<T, double>::value &&
654  !is_direct_constructible<T, int>::value))>::type> {
655  static constexpr object_category value{object_category::tuple_value};
656  // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be
657  // constructed from just the first element so tuples of <string, int,int> can be constructed from a string, which
658  // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2
659  // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out
660  // those cases that are caught by other object classifications
661 };
662 
664 template <typename T> struct classify_object<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
665  static constexpr object_category value{object_category::container_value};
666 };
667 
668 // Type name print
669 
673 
674 template <typename T,
675  enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
676 constexpr const char *type_name() {
677  return "CHAR";
678 }
679 
680 template <typename T,
681  enable_if_t<classify_object<T>::value == object_category::integral_value ||
682  classify_object<T>::value == object_category::integer_constructible,
684 constexpr const char *type_name() {
685  return "INT";
686 }
687 
688 template <typename T,
689  enable_if_t<classify_object<T>::value == object_category::unsigned_integral, detail::enabler> = detail::dummy>
690 constexpr const char *type_name() {
691  return "UINT";
692 }
693 
694 template <typename T,
695  enable_if_t<classify_object<T>::value == object_category::floating_point ||
696  classify_object<T>::value == object_category::number_constructible ||
697  classify_object<T>::value == object_category::double_constructible,
699 constexpr const char *type_name() {
700  return "FLOAT";
701 }
702 
704 template <typename T,
705  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
706 constexpr const char *type_name() {
707  return "ENUM";
708 }
709 
711 template <typename T,
712  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
713 constexpr const char *type_name() {
714  return "BOOLEAN";
715 }
716 
718 template <typename T,
719  enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
720 constexpr const char *type_name() {
721  return "COMPLEX";
722 }
723 
725 template <typename T,
726  enable_if_t<classify_object<T>::value >= object_category::string_assignable &&
727  classify_object<T>::value <= object_category::other,
729 constexpr const char *type_name() {
730  return "TEXT";
731 }
733 template <typename T,
734  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
736 std::string type_name(); // forward declaration
737 
739 template <typename T,
740  enable_if_t<classify_object<T>::value == object_category::container_value ||
741  classify_object<T>::value == object_category::wrapper_value,
743 std::string type_name(); // forward declaration
744 
746 template <typename T,
747  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value == 1,
749 inline std::string type_name() {
750  return type_name<typename std::decay<typename std::tuple_element<0, T>::type>::type>();
751 }
752 
754 template <typename T, std::size_t I>
755 inline typename std::enable_if<I == type_count_base<T>::value, std::string>::type tuple_name() {
756  return std::string{};
757 }
758 
760 template <typename T, std::size_t I>
761 inline typename std::enable_if<(I < type_count_base<T>::value), std::string>::type tuple_name() {
762  std::string str = std::string(type_name<typename std::decay<typename std::tuple_element<I, T>::type>::type>()) +
763  ',' + tuple_name<T, I + 1>();
764  if(str.back() == ',')
765  str.pop_back();
766  return str;
767 }
768 
770 template <typename T,
771  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
773 inline std::string type_name() {
774  auto tname = std::string(1, '[') + tuple_name<T, 0>();
775  tname.push_back(']');
776  return tname;
777 }
778 
780 template <typename T,
781  enable_if_t<classify_object<T>::value == object_category::container_value ||
782  classify_object<T>::value == object_category::wrapper_value,
784 inline std::string type_name() {
785  return type_name<typename T::value_type>();
786 }
787 
788 // Lexical cast
789 
791 template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
792 bool integral_conversion(const std::string &input, T &output) noexcept {
793  if(input.empty()) {
794  return false;
795  }
796  char *val = nullptr;
797  std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);
798  output = static_cast<T>(output_ll);
799  return val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll;
800 }
801 
803 template <typename T, enable_if_t<std::is_signed<T>::value, detail::enabler> = detail::dummy>
804 bool integral_conversion(const std::string &input, T &output) noexcept {
805  if(input.empty()) {
806  return false;
807  }
808  char *val = nullptr;
809  std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0);
810  output = static_cast<T>(output_ll);
811  return val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll;
812 }
813 
815 inline std::int64_t to_flag_value(std::string val) {
816  static const std::string trueString("true");
817  static const std::string falseString("false");
818  if(val == trueString) {
819  return 1;
820  }
821  if(val == falseString) {
822  return -1;
823  }
824  val = detail::to_lower(val);
825  std::int64_t ret;
826  if(val.size() == 1) {
827  if(val[0] >= '1' && val[0] <= '9') {
828  return (static_cast<std::int64_t>(val[0]) - '0');
829  }
830  switch(val[0]) {
831  case '0':
832  case 'f':
833  case 'n':
834  case '-':
835  ret = -1;
836  break;
837  case 't':
838  case 'y':
839  case '+':
840  ret = 1;
841  break;
842  default:
843  throw std::invalid_argument("unrecognized character");
844  }
845  return ret;
846  }
847  if(val == trueString || val == "on" || val == "yes" || val == "enable") {
848  ret = 1;
849  } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
850  ret = -1;
851  } else {
852  ret = std::stoll(val);
853  }
854  return ret;
855 }
856 
858 template <typename T,
859  enable_if_t<classify_object<T>::value == object_category::integral_value ||
860  classify_object<T>::value == object_category::unsigned_integral,
862 bool lexical_cast(const std::string &input, T &output) {
863  return integral_conversion(input, output);
864 }
865 
867 template <typename T,
868  enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
869 bool lexical_cast(const std::string &input, T &output) {
870  if(input.size() == 1) {
871  output = static_cast<T>(input[0]);
872  return true;
873  }
874  return integral_conversion(input, output);
875 }
876 
878 template <typename T,
879  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
880 bool lexical_cast(const std::string &input, T &output) {
881  try {
882  auto out = to_flag_value(input);
883  output = (out > 0);
884  return true;
885  } catch(const std::invalid_argument &) {
886  return false;
887  } catch(const std::out_of_range &) {
888  // 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
889  // valid all we care about the sign
890  output = (input[0] != '-');
891  return true;
892  }
893 }
894 
896 template <typename T,
897  enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
898 bool lexical_cast(const std::string &input, T &output) {
899  if(input.empty()) {
900  return false;
901  }
902  char *val = nullptr;
903  auto output_ld = std::strtold(input.c_str(), &val);
904  output = static_cast<T>(output_ld);
905  return val == (input.c_str() + input.size());
906 }
907 
909 template <typename T,
910  enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
911 bool lexical_cast(const std::string &input, T &output) {
912  using XC = typename wrapped_type<T, double>::type;
913  XC x{0.0}, y{0.0};
914  auto str1 = input;
915  bool worked = false;
916  auto nloc = str1.find_last_of("+-");
917  if(nloc != std::string::npos && nloc > 0) {
918  worked = detail::lexical_cast(str1.substr(0, nloc), x);
919  str1 = str1.substr(nloc);
920  if(str1.back() == 'i' || str1.back() == 'j')
921  str1.pop_back();
922  worked = worked && detail::lexical_cast(str1, y);
923  } else {
924  if(str1.back() == 'i' || str1.back() == 'j') {
925  str1.pop_back();
926  worked = detail::lexical_cast(str1, y);
927  x = XC{0};
928  } else {
929  worked = detail::lexical_cast(str1, x);
930  y = XC{0};
931  }
932  }
933  if(worked) {
934  output = T{x, y};
935  return worked;
936  }
937  return from_stream(input, output);
938 }
939 
941 template <typename T,
942  enable_if_t<classify_object<T>::value == object_category::string_assignable, detail::enabler> = detail::dummy>
943 bool lexical_cast(const std::string &input, T &output) {
944  output = input;
945  return true;
946 }
947 
949 template <
950  typename T,
951  enable_if_t<classify_object<T>::value == object_category::string_constructible, detail::enabler> = detail::dummy>
952 bool lexical_cast(const std::string &input, T &output) {
953  output = T(input);
954  return true;
955 }
956 
958 template <typename T,
959  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
960 bool lexical_cast(const std::string &input, T &output) {
961  typename std::underlying_type<T>::type val;
962  if(!integral_conversion(input, val)) {
963  return false;
964  }
965  output = static_cast<T>(val);
966  return true;
967 }
968 
970 template <typename T,
971  enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
972  std::is_assignable<T &, typename T::value_type>::value,
974 bool lexical_cast(const std::string &input, T &output) {
975  typename T::value_type val;
976  if(lexical_cast(input, val)) {
977  output = val;
978  return true;
979  }
980  return from_stream(input, output);
981 }
982 
983 template <typename T,
984  enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
985  !std::is_assignable<T &, typename T::value_type>::value && std::is_assignable<T &, T>::value,
987 bool lexical_cast(const std::string &input, T &output) {
988  typename T::value_type val;
989  if(lexical_cast(input, val)) {
990  output = T{val};
991  return true;
992  }
993  return from_stream(input, output);
994 }
995 
997 template <
998  typename T,
999  enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
1000 bool lexical_cast(const std::string &input, T &output) {
1001  int val;
1002  if(integral_conversion(input, val)) {
1003  output = T(val);
1004  return true;
1005  } else {
1006  double dval;
1007  if(lexical_cast(input, dval)) {
1008  output = T{dval};
1009  return true;
1010  }
1011  }
1012  return from_stream(input, output);
1013 }
1014 
1016 template <
1017  typename T,
1018  enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
1019 bool lexical_cast(const std::string &input, T &output) {
1020  int val;
1021  if(integral_conversion(input, val)) {
1022  output = T(val);
1023  return true;
1024  }
1025  return from_stream(input, output);
1026 }
1027 
1029 template <
1030  typename T,
1031  enable_if_t<classify_object<T>::value == object_category::double_constructible, detail::enabler> = detail::dummy>
1032 bool lexical_cast(const std::string &input, T &output) {
1033  double val;
1034  if(lexical_cast(input, val)) {
1035  output = T{val};
1036  return true;
1037  }
1038  return from_stream(input, output);
1039 }
1040 
1042 template <typename T,
1043  enable_if_t<classify_object<T>::value == object_category::other && std::is_assignable<T &, int>::value,
1045 bool lexical_cast(const std::string &input, T &output) {
1046  int val;
1047  if(integral_conversion(input, val)) {
1048 #ifdef _MSC_VER
1049 #pragma warning(push)
1050 #pragma warning(disable : 4800)
1051 #endif
1052  // with Atomic<XX> this could produce a warning due to the conversion but if atomic gets here it is an old style
1053  // so will most likely still work
1054  output = val;
1055 #ifdef _MSC_VER
1056 #pragma warning(pop)
1057 #endif
1058  return true;
1059  }
1060  // LCOV_EXCL_START
1061  // This version of cast is only used for odd cases in an older compilers the fail over
1062  // from_stream is tested elsewhere an not relevant for coverage here
1063  return from_stream(input, output);
1064  // LCOV_EXCL_STOP
1065 }
1066 
1068 template <typename T,
1069  enable_if_t<classify_object<T>::value == object_category::other && !std::is_assignable<T &, int>::value,
1071 bool lexical_cast(const std::string &input, T &output) {
1072  static_assert(is_istreamable<T>::value,
1073  "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it "
1074  "is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
1075  return from_stream(input, output);
1076 }
1077 
1080 template <typename AssignTo,
1081  typename ConvertTo,
1082  enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&
1083  (classify_object<AssignTo>::value == object_category::string_assignable ||
1084  classify_object<AssignTo>::value == object_category::string_constructible),
1086 bool lexical_assign(const std::string &input, AssignTo &output) {
1087  return lexical_cast(input, output);
1088 }
1089 
1091 template <typename AssignTo,
1092  typename ConvertTo,
1093  enable_if_t<std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, AssignTo>::value &&
1094  classify_object<AssignTo>::value != object_category::string_assignable &&
1095  classify_object<AssignTo>::value != object_category::string_constructible,
1097 bool lexical_assign(const std::string &input, AssignTo &output) {
1098  if(input.empty()) {
1099  output = AssignTo{};
1100  return true;
1101  }
1102 
1103  return lexical_cast(input, output);
1104 }
1105 
1107 template <typename AssignTo,
1108  typename ConvertTo,
1109  enable_if_t<std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, AssignTo>::value &&
1110  classify_object<AssignTo>::value == object_category::wrapper_value,
1112 bool lexical_assign(const std::string &input, AssignTo &output) {
1113  if(input.empty()) {
1114  typename AssignTo::value_type emptyVal{};
1115  output = emptyVal;
1116  return true;
1117  }
1118  return lexical_cast(input, output);
1119 }
1120 
1123 template <typename AssignTo,
1124  typename ConvertTo,
1125  enable_if_t<std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, AssignTo>::value &&
1126  classify_object<AssignTo>::value != object_category::wrapper_value &&
1127  std::is_assignable<AssignTo &, int>::value,
1129 bool lexical_assign(const std::string &input, AssignTo &output) {
1130  if(input.empty()) {
1131  output = 0;
1132  return true;
1133  }
1134  int val;
1135  if(lexical_cast(input, val)) {
1136  output = val;
1137  return true;
1138  }
1139  return false;
1140 }
1141 
1143 template <typename AssignTo,
1144  typename ConvertTo,
1145  enable_if_t<!std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, ConvertTo &>::value,
1147 bool lexical_assign(const std::string &input, AssignTo &output) {
1148  ConvertTo val{};
1149  bool parse_result = (!input.empty()) ? lexical_cast<ConvertTo>(input, val) : true;
1150  if(parse_result) {
1151  output = val;
1152  }
1153  return parse_result;
1154 }
1155 
1157 template <
1158  typename AssignTo,
1159  typename ConvertTo,
1160  enable_if_t<!std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, ConvertTo &>::value &&
1161  std::is_move_assignable<AssignTo>::value,
1163 bool lexical_assign(const std::string &input, AssignTo &output) {
1164  ConvertTo val{};
1165  bool parse_result = input.empty() ? true : lexical_cast<ConvertTo>(input, val);
1166  if(parse_result) {
1167  output = AssignTo(val); // use () form of constructor to allow some implicit conversions
1168  }
1169  return parse_result;
1170 }
1171 
1173 template <typename AssignTo,
1174  typename ConvertTo,
1175  enable_if_t<classify_object<ConvertTo>::value <= object_category::other &&
1176  classify_object<AssignTo>::value <= object_category::wrapper_value,
1178 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1179  return lexical_assign<AssignTo, ConvertTo>(strings[0], output);
1180 }
1181 
1184 template <typename AssignTo,
1185  typename ConvertTo,
1186  enable_if_t<(type_count<AssignTo>::value <= 2) && expected_count<AssignTo>::value == 1 &&
1189 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1190  // the remove const is to handle pair types coming from a container
1191  typename std::remove_const<typename std::tuple_element<0, ConvertTo>::type>::type v1;
1192  typename std::tuple_element<1, ConvertTo>::type v2;
1193  bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);
1194  if(strings.size() > 1) {
1195  retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);
1196  }
1197  if(retval) {
1198  output = AssignTo{v1, v2};
1199  }
1200  return retval;
1201 }
1202 
1204 template <class AssignTo,
1205  class ConvertTo,
1206  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1209 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1210  output.erase(output.begin(), output.end());
1211  for(const auto &elem : strings) {
1212  typename AssignTo::value_type out;
1213  bool retval = lexical_assign<typename AssignTo::value_type, typename ConvertTo::value_type>(elem, out);
1214  if(!retval) {
1215  return false;
1216  }
1217  output.insert(output.end(), std::move(out));
1218  }
1219  return (!output.empty());
1220 }
1221 
1223 template <class AssignTo, class ConvertTo, enable_if_t<is_complex<ConvertTo>::value, detail::enabler> = detail::dummy>
1224 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
1225 
1226  if(strings.size() >= 2 && !strings[1].empty()) {
1227  using XC2 = typename wrapped_type<ConvertTo, double>::type;
1228  XC2 x{0.0}, y{0.0};
1229  auto str1 = strings[1];
1230  if(str1.back() == 'i' || str1.back() == 'j') {
1231  str1.pop_back();
1232  }
1233  auto worked = detail::lexical_cast(strings[0], x) && detail::lexical_cast(str1, y);
1234  if(worked) {
1235  output = ConvertTo{x, y};
1236  }
1237  return worked;
1238  } else {
1239  return lexical_assign<AssignTo, ConvertTo>(strings[0], output);
1240  }
1241 }
1242 
1244 template <class AssignTo,
1245  class ConvertTo,
1246  enable_if_t<is_mutable_container<AssignTo>::value && (expected_count<ConvertTo>::value == 1) &&
1249 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1250  bool retval = true;
1251  output.clear();
1252  output.reserve(strings.size());
1253  for(const auto &elem : strings) {
1254 
1255  output.emplace_back();
1256  retval = retval && lexical_assign<typename AssignTo::value_type, ConvertTo>(elem, output.back());
1257  }
1258  return (!output.empty()) && retval;
1259 }
1260 
1261 // forward declaration
1262 
1264 template <class AssignTo,
1265  class ConvertTo,
1266  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1269 bool lexical_conversion(std::vector<std::string> strings, AssignTo &output);
1270 
1272 template <class AssignTo,
1273  class ConvertTo,
1274  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1279 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output);
1280 
1282 template <class AssignTo,
1283  class ConvertTo,
1284  enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
1288 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output); // forward declaration
1289 
1292 template <typename AssignTo,
1293  typename ConvertTo,
1294  enable_if_t<!is_tuple_like<AssignTo>::value && !is_mutable_container<AssignTo>::value &&
1295  classify_object<ConvertTo>::value != object_category::wrapper_value &&
1296  (is_mutable_container<ConvertTo>::value || type_count<ConvertTo>::value > 2),
1298 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1299 
1300  if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) {
1301  ConvertTo val;
1302  auto retval = lexical_conversion<ConvertTo, ConvertTo>(strings, val);
1303  output = AssignTo{val};
1304  return retval;
1305  }
1306  output = AssignTo{};
1307  return true;
1308 }
1309 
1311 template <class AssignTo, class ConvertTo, std::size_t I>
1312 inline typename std::enable_if<(I >= type_count_base<AssignTo>::value), bool>::type
1313 tuple_conversion(const std::vector<std::string> &, AssignTo &) {
1314  return true;
1315 }
1316 
1318 template <class AssignTo, class ConvertTo>
1319 inline typename std::enable_if<!is_mutable_container<ConvertTo>::value && type_count<ConvertTo>::value == 1, bool>::type
1320 tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
1321  auto retval = lexical_assign<AssignTo, ConvertTo>(strings[0], output);
1322  strings.erase(strings.begin());
1323  return retval;
1324 }
1325 
1327 template <class AssignTo, class ConvertTo>
1328 inline typename std::enable_if<!is_mutable_container<ConvertTo>::value && (type_count<ConvertTo>::value > 1) &&
1329  type_count<ConvertTo>::value == type_count_min<ConvertTo>::value,
1330  bool>::type
1331 tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
1332  auto retval = lexical_conversion<AssignTo, ConvertTo>(strings, output);
1333  strings.erase(strings.begin(), strings.begin() + type_count<ConvertTo>::value);
1334  return retval;
1335 }
1336 
1338 template <class AssignTo, class ConvertTo>
1339 inline typename std::enable_if<is_mutable_container<ConvertTo>::value ||
1340  type_count<ConvertTo>::value != type_count_min<ConvertTo>::value,
1341  bool>::type
1342 tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
1343 
1344  std::size_t index{subtype_count_min<ConvertTo>::value};
1345  const std::size_t mx_count{subtype_count<ConvertTo>::value};
1346  const std::size_t mx{(std::max)(mx_count, strings.size())};
1347 
1348  while(index < mx) {
1349  if(is_separator(strings[index])) {
1350  break;
1351  }
1352  ++index;
1353  }
1354  bool retval = lexical_conversion<AssignTo, ConvertTo>(
1355  std::vector<std::string>(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index)), output);
1356  strings.erase(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index) + 1);
1357  return retval;
1358 }
1359 
1361 template <class AssignTo, class ConvertTo, std::size_t I>
1362 inline typename std::enable_if<(I < type_count_base<AssignTo>::value), bool>::type
1363 tuple_conversion(std::vector<std::string> strings, AssignTo &output) {
1364  bool retval = true;
1365  using ConvertToElement = typename std::
1366  conditional<is_tuple_like<ConvertTo>::value, typename std::tuple_element<I, ConvertTo>::type, ConvertTo>::type;
1367  if(!strings.empty()) {
1368  retval = retval && tuple_type_conversion<typename std::tuple_element<I, AssignTo>::type, ConvertToElement>(
1369  strings, std::get<I>(output));
1370  }
1371  retval = retval && tuple_conversion<AssignTo, ConvertTo, I + 1>(std::move(strings), output);
1372  return retval;
1373 }
1374 
1376 template <class AssignTo,
1377  class ConvertTo,
1378  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1380  detail::enabler>>
1381 bool lexical_conversion(std::vector<std::string> strings, AssignTo &output) {
1382  output.clear();
1383  while(!strings.empty()) {
1384 
1385  typename std::remove_const<typename std::tuple_element<0, typename ConvertTo::value_type>::type>::type v1;
1386  typename std::tuple_element<1, typename ConvertTo::value_type>::type v2;
1387  bool retval = tuple_type_conversion<decltype(v1), decltype(v1)>(strings, v1);
1388  if(!strings.empty()) {
1389  retval = retval && tuple_type_conversion<decltype(v2), decltype(v2)>(strings, v2);
1390  }
1391  if(retval) {
1392  output.insert(output.end(), typename AssignTo::value_type{v1, v2});
1393  } else {
1394  return false;
1395  }
1396  }
1397  return (!output.empty());
1398 }
1399 
1401 template <class AssignTo,
1402  class ConvertTo,
1403  enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
1406  detail::enabler>>
1407 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1408  static_assert(
1410  "if the conversion type is defined as a tuple it must be the same size as the type you are converting to");
1411  return tuple_conversion<AssignTo, ConvertTo, 0>(strings, output);
1412 }
1413 
1415 template <class AssignTo,
1416  class ConvertTo,
1417  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1421  detail::enabler>>
1422 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1423  bool retval = true;
1424  output.clear();
1425  std::vector<std::string> temp;
1426  std::size_t ii{0};
1427  std::size_t icount{0};
1428  std::size_t xcm{type_count<ConvertTo>::value};
1429  auto ii_max = strings.size();
1430  while(ii < ii_max) {
1431  temp.push_back(strings[ii]);
1432  ++ii;
1433  ++icount;
1434  if(icount == xcm || is_separator(temp.back()) || ii == ii_max) {
1435  if(static_cast<int>(xcm) > type_count_min<ConvertTo>::value && is_separator(temp.back())) {
1436  temp.pop_back();
1437  }
1438  typename AssignTo::value_type temp_out;
1439  retval = retval &&
1440  lexical_conversion<typename AssignTo::value_type, typename ConvertTo::value_type>(temp, temp_out);
1441  temp.clear();
1442  if(!retval) {
1443  return false;
1444  }
1445  output.insert(output.end(), std::move(temp_out));
1446  icount = 0;
1447  }
1448  }
1449  return retval;
1450 }
1451 
1453 template <typename AssignTo,
1454  class ConvertTo,
1455  enable_if_t<classify_object<ConvertTo>::value == object_category::wrapper_value &&
1456  std::is_assignable<ConvertTo &, ConvertTo>::value,
1458 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
1459  if(strings.empty() || strings.front().empty()) {
1460  output = ConvertTo{};
1461  return true;
1462  }
1463  typename ConvertTo::value_type val;
1464  if(lexical_conversion<typename ConvertTo::value_type, typename ConvertTo::value_type>(strings, val)) {
1465  output = ConvertTo{val};
1466  return true;
1467  }
1468  return false;
1469 }
1470 
1472 template <typename AssignTo,
1473  class ConvertTo,
1474  enable_if_t<classify_object<ConvertTo>::value == object_category::wrapper_value &&
1475  !std::is_assignable<AssignTo &, ConvertTo>::value,
1477 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
1478  using ConvertType = typename ConvertTo::value_type;
1479  if(strings.empty() || strings.front().empty()) {
1480  output = ConvertType{};
1481  return true;
1482  }
1483  ConvertType val;
1484  if(lexical_conversion<typename ConvertTo::value_type, typename ConvertTo::value_type>(strings, val)) {
1485  output = val;
1486  return true;
1487  }
1488  return false;
1489 }
1490 
1496 template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
1497 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
1498  std::int64_t count{0};
1499  for(auto &flag : flags) {
1500  count += detail::to_flag_value(flag);
1501  }
1502  output = (count > 0) ? static_cast<T>(count) : T{0};
1503 }
1504 
1510 template <typename T, enable_if_t<std::is_signed<T>::value, detail::enabler> = detail::dummy>
1511 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
1512  std::int64_t count{0};
1513  for(auto &flag : flags) {
1514  count += detail::to_flag_value(flag);
1515  }
1516  output = static_cast<T>(count);
1517 }
1518 
1519 #ifdef _MSC_VER
1520 #pragma warning(push)
1521 #pragma warning(disable : 4800)
1522 #endif
1523 // with Atomic<XX> this could produce a warning due to the conversion but if atomic gets here it is an old style so will
1524 // most likely still work
1525 
1531 template <typename T,
1532  enable_if_t<!std::is_signed<T>::value && !std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
1533 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
1534  std::int64_t count{0};
1535  for(auto &flag : flags) {
1536  count += detail::to_flag_value(flag);
1537  }
1538  std::string out = detail::to_string(count);
1539  lexical_cast(out, output);
1540 }
1541 
1542 #ifdef _MSC_VER
1543 #pragma warning(pop)
1544 #endif
1545 
1546 } // namespace detail
1547 // [CLI11:type_tools_hpp:end]
1548 } // namespace CLI
Check for complex.
Definition: TypeTools.hpp:194
static constexpr bool value
Definition: TypeTools.hpp:201
Definition: TypeTools.hpp:144
static constexpr bool value
Definition: TypeTools.hpp:163
Check for input streamability.
Definition: TypeTools.hpp:183
static constexpr bool value
Definition: TypeTools.hpp:190
Definition: TypeTools.hpp:172
static constexpr bool value
Definition: TypeTools.hpp:179
Definition: TypeTools.hpp:256
static constexpr bool value
Definition: TypeTools.hpp:264
constexpr enabler dummy
An instance to use in EnableIf.
Definition: TypeTools.hpp:33
auto to_string(T &&value) -> decltype(std::forward< T >(value))
Convert an object to a string (directly forward if this can become a string)
Definition: TypeTools.hpp:269
auto checked_to_string(T &&value) -> decltype(to_string(std::forward< T >(value)))
special template overload
Definition: TypeTools.hpp:322
bool is_separator(const std::string &str)
check if a string is a container segment separator (empty or "%%")
Definition: StringTools.hpp:248
bool from_stream(const std::string &istring, T &obj)
Templated operation to get a value from a stream.
Definition: TypeTools.hpp:206
constexpr std::enable_if< I< type_count_base< T >::value, int >::type tuple_type_size() { return subtype_count< typename std::tuple_element< I, T >::type >::value+tuple_type_size< T, I+1 >);}template< typename T > struct type_count< T, typename std::enable_if< is_tuple_like< T >::value >::type > { static constexpr int value{tuple_type_size< T, 0 >)};};template< typename T > struct subtype_count { static constexpr int value{is_mutable_container< T >::value ? expected_max_vector_size :type_count< T >::value};};template< typename T, typename Enable=void > struct type_count_min { static const int value{0};};template< typename T >struct type_count_min< T, typename std::enable_if<!is_mutable_container< T >::value &&!is_tuple_like< T >::value &&!is_wrapper< T >::value &&!is_complex< T >::value &&!std::is_void< T >::value >::type > { static constexpr int value{type_count< T >::value};};template< typename T > struct type_count_min< T, typename std::enable_if< is_complex< T >::value >::type > { static constexpr int value{1};};template< typename T >struct type_count_min< T, typename std::enable_if< is_wrapper< T >::value &&!is_complex< T >::value &&!is_tuple_like< T >::value >::type > { static constexpr int value{subtype_count_min< typename T::value_type >::value};};template< typename T, std::size_t I >constexpr typename std::enable_if< I==type_count_base< T >::value, int >::type tuple_type_size_min() { return 0;}template< typename T, std::size_t I > constexpr typename std::enable_if< I< type_count_base< T >::value, int >::type tuple_type_size_min() { return subtype_count_min< typename std::tuple_element< I, T >::type >::value+tuple_type_size_min< T, I+1 >);}template< typename T > struct type_count_min< T, typename std::enable_if< is_tuple_like< T >::value >::type > { static constexpr int value{tuple_type_size_min< T, 0 >)};};template< typename T > struct subtype_count_min { static constexpr int value{is_mutable_container< T >::value ?((type_count< T >::value< expected_max_vector_size) ? type_count< T >::value :0) :type_count_min< T >::value};};template< typename T, typename Enable=void > struct expected_count { static const int value{0};};template< typename T >struct expected_count< T, typename std::enable_if<!is_mutable_container< T >::value &&!is_wrapper< T >::value &&!std::is_void< T >::value >::type > { static constexpr int value{1};};template< typename T > struct expected_count< T, typename std::enable_if< is_mutable_container< T >::value >::type > { static constexpr int value{expected_max_vector_size};};template< typename T >struct expected_count< T, typename std::enable_if<!is_mutable_container< T >::value &&is_wrapper< T >::value >::type > { static constexpr int value{expected_count< typename T::value_type >::value};};enum class object_category :int { char_value=1, integral_value=2, unsigned_integral=4, enumeration=6, boolean_value=8, floating_point=10, number_constructible=12, double_constructible=14, integer_constructible=16, string_assignable=23, string_constructible=24, other=45, wrapper_value=50, complex_number=60, tuple_value=70, container_value=80,};template< typename T, typename Enable=void > struct classify_object { static constexpr object_category value{object_category::other};};template< typename T >struct classify_object< T, typename std::enable_if< std::is_integral< T >::value &&!std::is_same< T, char >::value &&std::is_signed< T >::value &&!is_bool< T >::value &&!std::is_enum< T >::value >::type > { static constexpr object_category value{object_category::integral_value};};template< typename T >struct classify_object< T, typename std::enable_if< std::is_integral< T >::value &&std::is_unsigned< T >::value &&!std::is_same< T, char >::value &&!is_bool< T >::value >::type > { static constexpr object_category value{object_category::unsigned_integral};};template< typename T >struct classify_object< T, typename std::enable_if< std::is_same< T, char >::value &&!std::is_enum< T >::value >::type > { static constexpr object_category value{object_category::char_value};};template< typename T > struct classify_object< T, typename std::enable_if< is_bool< T >::value >::type > { static constexpr object_category value{object_category::boolean_value};};template< typename T > struct classify_object< T, typename std::enable_if< std::is_floating_point< T >::value >::type > { static constexpr object_category value{object_category::floating_point};};template< typename T >struct classify_object< T, typename std::enable_if<!std::is_floating_point< T >::value &&!std::is_integral< T >::value &&std::is_assignable< T &, std::string >::value >::type > { static constexpr object_category value{object_category::string_assignable};};template< typename T >struct classify_object< T, typename std::enable_if<!std::is_floating_point< T >::value &&!std::is_integral< T >::value &&!std::is_assignable< T &, std::string >::value &&(type_count< T >::value==1) &&std::is_constructible< T, std::string >::value >::type > { static constexpr object_category value{object_category::string_constructible};};template< typename T > struct classify_object< T, typename std::enable_if< std::is_enum< T >::value >::type > { static constexpr object_category value{object_category::enumeration};};template< typename T > struct classify_object< T, typename std::enable_if< is_complex< T >::value >::type > { static constexpr object_category value{object_category::complex_number};};template< typename T > struct uncommon_type { using 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_complex< T >::value &&!is_mutable_container< T >::value &&!std::is_enum< T >::value, std::true_type, std::false_type >::type;static constexpr bool value=type::value;};template< typename T >struct classify_object< T, typename std::enable_if<(!is_mutable_container< T >::value &&is_wrapper< T >::value &&!is_tuple_like< T >::value &&uncommon_type< T >::value)>::type > { static constexpr object_category value{object_category::wrapper_value};};template< typename T >struct classify_object< T, typename std::enable_if< uncommon_type< T >::value &&type_count< T >::value==1 &&!is_wrapper< T >::value &&is_direct_constructible< T, double >::value &&is_direct_constructible< T, int >::value >::type > { static constexpr object_category value{object_category::number_constructible};};template< typename T >struct classify_object< T, typename std::enable_if< uncommon_type< T >::value &&type_count< T >::value==1 &&!is_wrapper< T >::value &&!is_direct_constructible< T, double >::value &&is_direct_constructible< T, int >::value >::type > { static constexpr object_category value{object_category::integer_constructible};};template< typename T >struct classify_object< T, typename std::enable_if< uncommon_type< T >::value &&type_count< T >::value==1 &&!is_wrapper< T >::value &&is_direct_constructible< T, double >::value &&!is_direct_constructible< T, int >::value >::type > { static constexpr object_category value{object_category::double_constructible};};template< typename T >struct classify_object< T, typename std::enable_if< is_tuple_like< T >::value &&((type_count< T >::value >=2 &&!is_wrapper< T >::value)||(uncommon_type< T >::value &&!is_direct_constructible< T, double >::value &&!is_direct_constructible< T, int >::value))>::type > { static constexpr object_category value{object_category::tuple_value};};template< typename T > struct classify_object< T, typename std::enable_if< is_mutable_container< T >::value >::type > { static constexpr object_category value{object_category::container_value};};template< typename T, enable_if_t< classify_object< T >::value==object_category::char_value, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "CHAR";}template< typename T, enable_if_t< classify_object< T >::value==object_category::integral_value||classify_object< T >::value==object_category::integer_constructible, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "INT";}template< typename T, enable_if_t< classify_object< T >::value==object_category::unsigned_integral, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "UINT";}template< typename T, enable_if_t< classify_object< T >::value==object_category::floating_point||classify_object< T >::value==object_category::number_constructible||classify_object< T >::value==object_category::double_constructible, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "FLOAT";}template< typename T, enable_if_t< classify_object< T >::value==object_category::enumeration, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "ENUM";}template< typename T, enable_if_t< classify_object< T >::value==object_category::boolean_value, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "BOOLEAN";}template< typename T, enable_if_t< classify_object< T >::value==object_category::complex_number, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "COMPLEX";}template< typename T, enable_if_t< classify_object< T >::value >=object_category::string_assignable &&classify_object< T >::value<=object_category::other, detail::enabler >=detail::dummy >constexpr const char *type_name() { return "TEXT";}template< typename T, enable_if_t< classify_object< T >::value==object_category::tuple_value &&type_count_base< T >::value >=2, detail::enabler >=detail::dummy >std::string type_name();template< typename T, enable_if_t< classify_object< T >::value==object_category::container_value||classify_object< T >::value==object_category::wrapper_value, detail::enabler >=detail::dummy >std::string type_name();template< typename T, enable_if_t< classify_object< T >::value==object_category::tuple_value &&type_count_base< T >::value==1, detail::enabler >=detail::dummy >inline std::string type_name() { return type_name< typename std::decay< typename std::tuple_element< 0, T >::type >::type >);}template< typename T, std::size_t I >inline typename std::enable_if< I==type_count_base< T >::value, std::string >::type tuple_name() { return std::string{};}template< typename T, std::size_t I >inline typename std::enable_if<(I< type_count_base< T >::value), std::string >::type tuple_name() { std::string str=std::string(type_name< typename std::decay< typename std::tuple_element< I, T >::type >::type >))+','+tuple_name< T, I+1 >);if(str.back()==',') str.pop_back();return str;}template< typename T, enable_if_t< classify_object< T >::value==object_category::tuple_value &&type_count_base< T >::value >=2, detail::enabler > > std::string type_name()
Recursively generate the tuple type name.
Definition: TypeTools.hpp:773
constexpr int expected_max_vector_size
Definition: StringTools.hpp:43
std::string value_string(const T &value)
get a string as a convertible value for arithmetic types
Definition: TypeTools.hpp:336
std::string to_string(T &&value)
Convert an object to a string (streaming must be supported for that type)
Definition: TypeTools.hpp:286
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:63
bool lexical_assign(const std::string &input, AssignTo &output)
Assign a value through lexical cast operations.
Definition: TypeTools.hpp:1086
std::string to_lower(std::string str)
Return a lower case version of a string.
Definition: StringTools.hpp:259
enabler
Simple empty scoped class.
Definition: TypeTools.hpp:30
bool lexical_cast(const std::string &input, T &output)
Integer conversion.
Definition: TypeTools.hpp:862
std::int64_t to_flag_value(std::string val)
Convert a flag into an integer value typically binary flags.
Definition: TypeTools.hpp:815
constexpr std::enable_if< I==type_count_base< T >::value, int >::type tuple_type_size()
0 if the index > tuple size
Definition: TypeTools.hpp:420
bool integral_conversion(const std::string &input, T &output) noexcept
Convert to an unsigned integral.
Definition: TypeTools.hpp:792
Definition: App.hpp:34
typename std::enable_if< B, T >::type enable_if_t
Definition: TypeTools.hpp:41
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:50
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:47
std::string type
Definition: TypeTools.hpp:76
This can be specialized to override the type deduction for IsMember.
Definition: TypeTools.hpp:73
T type
Definition: TypeTools.hpp:73
typename std::pointer_traits< T >::element_type type
Definition: TypeTools.hpp:89
not a pointer
Definition: TypeTools.hpp:86
T type
Definition: TypeTools.hpp:86
Definition: TypeTools.hpp:94
typename element_type< T >::type::value_type type
Definition: TypeTools.hpp:94
Definition: TypeTools.hpp:219
Definition: TypeTools.hpp:237
Definition: TypeTools.hpp:249
typename std::remove_const< typename value_type::first_type >::type first_type
Definition: TypeTools.hpp:120
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:124
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:128
typename std::remove_const< typename value_type::second_type >::type second_type
Definition: TypeTools.hpp:121
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:97
typename T::value_type value_type
Definition: TypeTools.hpp:98
typename std::remove_const< value_type >::type second_type
Definition: TypeTools.hpp:100
static auto second(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:107
typename std::remove_const< value_type >::type first_type
Definition: TypeTools.hpp:99
static auto first(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:103
forward declare the subtype_count_min structure
Definition: TypeTools.hpp:387
Set of overloads to get the type size of an object.
Definition: TypeTools.hpp:384
This will only trigger for actual void type.
Definition: TypeTools.hpp:360
static const int value
Definition: TypeTools.hpp:360
This will only trigger for actual void type.
Definition: TypeTools.hpp:390
static const int value
Definition: TypeTools.hpp:390
template to get the underlying value type if it exists or use a default
Definition: TypeTools.hpp:352
def type
Definition: TypeTools.hpp:352
Check to see if something is bool (fail check by default)
Definition: TypeTools.hpp:53
Check to see if something is copyable pointer.
Definition: TypeTools.hpp:68
static bool const value
Definition: TypeTools.hpp:69
Check to see if something is a shared pointer.
Definition: TypeTools.hpp:59
A copy of std::void_t from C++17 (helper for C++11 and C++14)
Definition: TypeTools.hpp:44
void type
Definition: TypeTools.hpp:44