CLI11  2.3.2
TypeTools.hpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2023, 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 <cmath>
11 #include <cstdint>
12 #include <exception>
13 #include <limits>
14 #include <memory>
15 #include <string>
16 #include <type_traits>
17 #include <utility>
18 #include <vector>
19 // [CLI11:public_includes:end]
20 
21 #include "StringTools.hpp"
22 
23 namespace CLI {
24 // [CLI11:type_tools_hpp:verbatim]
25 
26 // Type tools
27 
28 // Utilities for type enabling
29 namespace detail {
30 // Based generally on https://rmf.io/cxx11/almost-static-if
32 enum class enabler {};
33 
35 constexpr enabler dummy = {};
36 } // namespace detail
37 
43 template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
44 
46 template <typename... Ts> struct make_void {
47  using type = void;
48 };
49 
51 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
52 
54 template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;
55 
57 template <typename T> struct is_bool : std::false_type {};
58 
60 template <> struct is_bool<bool> : std::true_type {};
61 
63 template <typename T> struct is_shared_ptr : std::false_type {};
64 
66 template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
67 
69 template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};
70 
72 template <typename T> struct is_copyable_ptr {
73  static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
74 };
75 
77 template <typename T> struct IsMemberType {
78  using type = T;
79 };
80 
82 template <> struct IsMemberType<const char *> {
83  using type = std::string;
84 };
85 
86 namespace detail {
87 
88 // These are utilities for IsMember and other transforming objects
89 
92 
94 template <typename T, typename Enable = void> struct element_type {
95  using type = T;
96 };
97 
98 template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
99  using type = typename std::pointer_traits<T>::element_type;
100 };
101 
104 template <typename T> struct element_value_type {
106 };
107 
109 template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
110  using value_type = typename T::value_type;
111  using first_type = typename std::remove_const<value_type>::type;
112  using second_type = typename std::remove_const<value_type>::type;
113 
115  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
116  return std::forward<Q>(pair_value);
117  }
119  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
120  return std::forward<Q>(pair_value);
121  }
122 };
123 
126 template <typename T>
128  T,
129  conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
130  : std::true_type {
131  using value_type = typename T::value_type;
132  using first_type = typename std::remove_const<typename value_type::first_type>::type;
133  using second_type = typename std::remove_const<typename value_type::second_type>::type;
134 
136  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
137  return std::get<0>(std::forward<Q>(pair_value));
138  }
140  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
141  return std::get<1>(std::forward<Q>(pair_value));
142  }
143 };
144 
145 // Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning
146 // in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in
147 // brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a
148 // little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out.
149 // But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be
150 // suppressed
151 #ifdef __GNUC__
152 #pragma GCC diagnostic push
153 #pragma GCC diagnostic ignored "-Wnarrowing"
154 #endif
155 // check for constructibility from a specific type and copy assignable used in the parse detection
156 template <typename T, typename C> class is_direct_constructible {
157  template <typename TT, typename CC>
158  static auto test(int, std::true_type) -> decltype(
159 // NVCC warns about narrowing conversions here
160 #ifdef __CUDACC__
161 #pragma diag_suppress 2361
162 #endif
163  TT{std::declval<CC>()}
164 #ifdef __CUDACC__
165 #pragma diag_default 2361
166 #endif
167  ,
168  std::is_move_assignable<TT>());
169 
170  template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
171 
172  template <typename, typename> static auto test(...) -> std::false_type;
173 
174  public:
175  static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
176 };
177 #ifdef __GNUC__
178 #pragma GCC diagnostic pop
179 #endif
180 
181 // Check for output streamability
182 // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
183 
184 template <typename T, typename S = std::ostringstream> class is_ostreamable {
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, typename S = std::istringstream> class is_istreamable {
196  template <typename TT, typename SS>
197  static auto test(int) -> decltype(std::declval<SS &>() >> std::declval<TT &>(), std::true_type());
198 
199  template <typename, typename> static auto test(...) -> std::false_type;
200 
201  public:
202  static constexpr bool value = decltype(test<T, S>(0))::value;
203 };
204 
206 template <typename T> class is_complex {
207  template <typename TT>
208  static auto test(int) -> decltype(std::declval<TT>().real(), std::declval<TT>().imag(), std::true_type());
209 
210  template <typename> static auto test(...) -> std::false_type;
211 
212  public:
213  static constexpr bool value = decltype(test<T>(0))::value;
214 };
215 
217 template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
218 bool from_stream(const std::string &istring, T &obj) {
219  std::istringstream is;
220  is.str(istring);
221  is >> obj;
222  return !is.fail() && !is.rdbuf()->in_avail();
223 }
224 
225 template <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> = detail::dummy>
226 bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
227  return false;
228 }
229 
230 // check to see if an object is a mutable container (fail by default)
231 template <typename T, typename _ = void> struct is_mutable_container : std::false_type {};
232 
236 template <typename T>
238  T,
239  conditional_t<false,
240  void_t<typename T::value_type,
241  decltype(std::declval<T>().end()),
242  decltype(std::declval<T>().clear()),
243  decltype(std::declval<T>().insert(std::declval<decltype(std::declval<T>().end())>(),
244  std::declval<const typename T::value_type &>()))>,
245  void>>
246  : public conditional_t<std::is_constructible<T, std::string>::value, std::false_type, std::true_type> {};
247 
248 // check to see if an object is a mutable container (fail by default)
249 template <typename T, typename _ = void> struct is_readable_container : std::false_type {};
250 
254 template <typename T>
256  T,
257  conditional_t<false, void_t<decltype(std::declval<T>().end()), decltype(std::declval<T>().begin())>, void>>
258  : public std::true_type {};
259 
260 // check to see if an object is a wrapper (fail by default)
261 template <typename T, typename _ = void> struct is_wrapper : std::false_type {};
262 
263 // check if an object is a wrapper (it has a value_type defined)
264 template <typename T>
265 struct is_wrapper<T, conditional_t<false, void_t<typename T::value_type>, void>> : public std::true_type {};
266 
267 // Check for tuple like types, as in classes with a tuple_size type trait
268 template <typename S> class is_tuple_like {
269  template <typename SS>
270  // static auto test(int)
271  // -> decltype(std::conditional<(std::tuple_size<SS>::value > 0), std::true_type, std::false_type>::type());
272  static auto test(int) -> decltype(std::tuple_size<typename std::decay<SS>::type>::value, std::true_type{});
273  template <typename> static auto test(...) -> std::false_type;
274 
275  public:
276  static constexpr bool value = decltype(test<S>(0))::value;
277 };
278 
280 template <typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
281 auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
282  return std::forward<T>(value);
283 }
284 
286 template <typename T,
287  enable_if_t<std::is_constructible<std::string, T>::value && !std::is_convertible<T, std::string>::value,
289 std::string to_string(const T &value) {
290  return std::string(value); // NOLINT(google-readability-casting)
291 }
292 
294 template <typename T,
295  enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
298 std::string to_string(T &&value) {
299  std::stringstream stream;
300  stream << value;
301  return stream.str();
302 }
303 
305 template <typename T,
307  !is_readable_container<typename std::remove_const<T>::type>::value,
309 std::string to_string(T &&) {
310  return {};
311 }
312 
314 template <typename T,
315  enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
316  is_readable_container<T>::value,
318 std::string to_string(T &&variable) {
319  auto cval = variable.begin();
320  auto end = variable.end();
321  if(cval == end) {
322  return {"{}"};
323  }
324  std::vector<std::string> defaults;
325  while(cval != end) {
326  defaults.emplace_back(CLI::detail::to_string(*cval));
327  ++cval;
328  }
329  return {"[" + detail::join(defaults) + "]"};
330 }
331 
333 template <typename T1,
334  typename T2,
335  typename T,
336  enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
337 auto checked_to_string(T &&value) -> decltype(to_string(std::forward<T>(value))) {
338  return to_string(std::forward<T>(value));
339 }
340 
342 template <typename T1,
343  typename T2,
344  typename T,
346 std::string checked_to_string(T &&) {
347  return std::string{};
348 }
350 template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
351 std::string value_string(const T &value) {
352  return std::to_string(value);
353 }
355 template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
356 std::string value_string(const T &value) {
357  return std::to_string(static_cast<typename std::underlying_type<T>::type>(value));
358 }
360 template <typename T,
361  enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
362 auto value_string(const T &value) -> decltype(to_string(value)) {
363  return to_string(value);
364 }
365 
367 template <typename T, typename def, typename Enable = void> struct wrapped_type {
368  using type = def;
369 };
370 
372 template <typename T, typename def> struct wrapped_type<T, def, typename std::enable_if<is_wrapper<T>::value>::type> {
373  using type = typename T::value_type;
374 };
375 
377 template <typename T, typename Enable = void> struct type_count_base {
378  static const int value{0};
379 };
380 
382 template <typename T>
384  typename std::enable_if<!is_tuple_like<T>::value && !is_mutable_container<T>::value &&
385  !std::is_void<T>::value>::type> {
386  static constexpr int value{1};
387 };
388 
390 template <typename T>
391 struct type_count_base<T, typename std::enable_if<is_tuple_like<T>::value && !is_mutable_container<T>::value>::type> {
392  static constexpr int value{std::tuple_size<T>::value};
393 };
394 
396 template <typename T> struct type_count_base<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
398 };
399 
401 
403 template <typename T> struct subtype_count;
404 
406 template <typename T> struct subtype_count_min;
407 
409 template <typename T, typename Enable = void> struct type_count {
410  static const int value{0};
411 };
412 
414 template <typename T>
415 struct type_count<T,
416  typename std::enable_if<!is_wrapper<T>::value && !is_tuple_like<T>::value && !is_complex<T>::value &&
417  !std::is_void<T>::value>::type> {
418  static constexpr int value{1};
419 };
420 
422 template <typename T> struct type_count<T, typename std::enable_if<is_complex<T>::value>::type> {
423  static constexpr int value{2};
424 };
425 
427 template <typename T> struct type_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
429 };
430 
432 template <typename T>
433 struct type_count<T,
434  typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value &&
435  !is_mutable_container<T>::value>::type> {
437 };
438 
440 template <typename T, std::size_t I>
441 constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size() {
442  return 0;
443 }
444 
446 template <typename T, std::size_t I>
447  constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size() {
448  return subtype_count<typename std::tuple_element<I, T>::type>::value + tuple_type_size<T, I + 1>();
449 }
450 
452 template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
453  static constexpr int value{tuple_type_size<T, 0>()};
454 };
455 
457 template <typename T> struct subtype_count {
458  static constexpr int value{is_mutable_container<T>::value ? expected_max_vector_size : type_count<T>::value};
459 };
460 
462 template <typename T, typename Enable = void> struct type_count_min {
463  static const int value{0};
464 };
465 
467 template <typename T>
468 struct type_count_min<
469  T,
470  typename std::enable_if<!is_mutable_container<T>::value && !is_tuple_like<T>::value && !is_wrapper<T>::value &&
471  !is_complex<T>::value && !std::is_void<T>::value>::type> {
472  static constexpr int value{type_count<T>::value};
473 };
474 
476 template <typename T> struct type_count_min<T, typename std::enable_if<is_complex<T>::value>::type> {
477  static constexpr int value{1};
478 };
479 
481 template <typename T>
482 struct type_count_min<
483  T,
484  typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value>::type> {
485  static constexpr int value{subtype_count_min<typename T::value_type>::value};
486 };
487 
489 template <typename T, std::size_t I>
490 constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size_min() {
491  return 0;
492 }
493 
495 template <typename T, std::size_t I>
496  constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size_min() {
497  return subtype_count_min<typename std::tuple_element<I, T>::type>::value + tuple_type_size_min<T, I + 1>();
498 }
499 
501 template <typename T> struct type_count_min<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
502  static constexpr int value{tuple_type_size_min<T, 0>()};
503 };
504 
506 template <typename T> struct subtype_count_min {
507  static constexpr int value{is_mutable_container<T>::value
509  : type_count_min<T>::value};
510 };
511 
513 template <typename T, typename Enable = void> struct expected_count {
514  static const int value{0};
515 };
516 
518 template <typename T>
519 struct expected_count<T,
520  typename std::enable_if<!is_mutable_container<T>::value && !is_wrapper<T>::value &&
521  !std::is_void<T>::value>::type> {
522  static constexpr int value{1};
523 };
525 template <typename T> struct expected_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
526  static constexpr int value{expected_max_vector_size};
527 };
528 
530 template <typename T>
531 struct expected_count<T, typename std::enable_if<!is_mutable_container<T>::value && is_wrapper<T>::value>::type> {
532  static constexpr int value{expected_count<typename T::value_type>::value};
533 };
534 
535 // Enumeration of the different supported categorizations of objects
536 enum class object_category : int {
537  char_value = 1,
538  integral_value = 2,
539  unsigned_integral = 4,
540  enumeration = 6,
541  boolean_value = 8,
542  floating_point = 10,
543  number_constructible = 12,
544  double_constructible = 14,
545  integer_constructible = 16,
546  // string like types
547  string_assignable = 23,
548  string_constructible = 24,
549  other = 45,
550  // special wrapper or container types
551  wrapper_value = 50,
552  complex_number = 60,
553  tuple_value = 70,
554  container_value = 80,
555 
556 };
557 
559 
561 template <typename T, typename Enable = void> struct classify_object {
562  static constexpr object_category value{object_category::other};
563 };
564 
566 template <typename T>
567 struct classify_object<
568  T,
569  typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && std::is_signed<T>::value &&
570  !is_bool<T>::value && !std::is_enum<T>::value>::type> {
571  static constexpr object_category value{object_category::integral_value};
572 };
573 
575 template <typename T>
576 struct classify_object<T,
577  typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value &&
578  !std::is_same<T, char>::value && !is_bool<T>::value>::type> {
579  static constexpr object_category value{object_category::unsigned_integral};
580 };
581 
583 template <typename T>
584 struct classify_object<T, typename std::enable_if<std::is_same<T, char>::value && !std::is_enum<T>::value>::type> {
585  static constexpr object_category value{object_category::char_value};
586 };
587 
589 template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
590  static constexpr object_category value{object_category::boolean_value};
591 };
592 
594 template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
595  static constexpr object_category value{object_category::floating_point};
596 };
597 
599 template <typename T>
600 struct classify_object<T,
601  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
602  std::is_assignable<T &, std::string>::value>::type> {
603  static constexpr object_category value{object_category::string_assignable};
604 };
605 
607 template <typename T>
608 struct classify_object<
609  T,
610  typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
611  !std::is_assignable<T &, std::string>::value && (type_count<T>::value == 1) &&
612  std::is_constructible<T, std::string>::value>::type> {
613  static constexpr object_category value{object_category::string_constructible};
614 };
615 
617 template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
618  static constexpr object_category value{object_category::enumeration};
619 };
620 
621 template <typename T> struct classify_object<T, typename std::enable_if<is_complex<T>::value>::type> {
622  static constexpr object_category value{object_category::complex_number};
623 };
624 
627 template <typename T> struct uncommon_type {
628  using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
629  !std::is_assignable<T &, std::string>::value &&
630  !std::is_constructible<T, std::string>::value && !is_complex<T>::value &&
631  !is_mutable_container<T>::value && !std::is_enum<T>::value,
632  std::true_type,
633  std::false_type>::type;
634  static constexpr bool value = type::value;
635 };
636 
638 template <typename T>
639 struct classify_object<T,
640  typename std::enable_if<(!is_mutable_container<T>::value && is_wrapper<T>::value &&
641  !is_tuple_like<T>::value && uncommon_type<T>::value)>::type> {
642  static constexpr object_category value{object_category::wrapper_value};
643 };
644 
646 template <typename T>
647 struct classify_object<T,
648  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
649  !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
650  is_direct_constructible<T, int>::value>::type> {
651  static constexpr object_category value{object_category::number_constructible};
652 };
653 
655 template <typename T>
656 struct classify_object<T,
657  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
658  !is_wrapper<T>::value && !is_direct_constructible<T, double>::value &&
659  is_direct_constructible<T, int>::value>::type> {
660  static constexpr object_category value{object_category::integer_constructible};
661 };
662 
664 template <typename T>
665 struct classify_object<T,
666  typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
667  !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
668  !is_direct_constructible<T, int>::value>::type> {
669  static constexpr object_category value{object_category::double_constructible};
670 };
671 
673 template <typename T>
674 struct classify_object<
675  T,
676  typename std::enable_if<is_tuple_like<T>::value &&
677  ((type_count<T>::value >= 2 && !is_wrapper<T>::value) ||
678  (uncommon_type<T>::value && !is_direct_constructible<T, double>::value &&
679  !is_direct_constructible<T, int>::value) ||
680  (uncommon_type<T>::value && type_count<T>::value >= 2))>::type> {
681  static constexpr object_category value{object_category::tuple_value};
682  // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be
683  // constructed from just the first element so tuples of <string, int,int> can be constructed from a string, which
684  // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2
685  // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out
686  // those cases that are caught by other object classifications
687 };
688 
690 template <typename T> struct classify_object<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
691  static constexpr object_category value{object_category::container_value};
692 };
693 
694 // Type name print
695 
699 
700 template <typename T,
701  enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
702 constexpr const char *type_name() {
703  return "CHAR";
704 }
705 
706 template <typename T,
707  enable_if_t<classify_object<T>::value == object_category::integral_value ||
708  classify_object<T>::value == object_category::integer_constructible,
710 constexpr const char *type_name() {
711  return "INT";
712 }
713 
714 template <typename T,
715  enable_if_t<classify_object<T>::value == object_category::unsigned_integral, detail::enabler> = detail::dummy>
716 constexpr const char *type_name() {
717  return "UINT";
718 }
719 
720 template <typename T,
721  enable_if_t<classify_object<T>::value == object_category::floating_point ||
722  classify_object<T>::value == object_category::number_constructible ||
723  classify_object<T>::value == object_category::double_constructible,
725 constexpr const char *type_name() {
726  return "FLOAT";
727 }
728 
730 template <typename T,
731  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
732 constexpr const char *type_name() {
733  return "ENUM";
734 }
735 
737 template <typename T,
738  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
739 constexpr const char *type_name() {
740  return "BOOLEAN";
741 }
742 
744 template <typename T,
745  enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
746 constexpr const char *type_name() {
747  return "COMPLEX";
748 }
749 
751 template <typename T,
752  enable_if_t<classify_object<T>::value >= object_category::string_assignable &&
753  classify_object<T>::value <= object_category::other,
755 constexpr const char *type_name() {
756  return "TEXT";
757 }
759 template <typename T,
760  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
762 std::string type_name(); // forward declaration
763 
765 template <typename T,
766  enable_if_t<classify_object<T>::value == object_category::container_value ||
767  classify_object<T>::value == object_category::wrapper_value,
769 std::string type_name(); // forward declaration
770 
772 template <typename T,
773  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value == 1,
775 inline std::string type_name() {
776  return type_name<typename std::decay<typename std::tuple_element<0, T>::type>::type>();
777 }
778 
780 template <typename T, std::size_t I>
781 inline typename std::enable_if<I == type_count_base<T>::value, std::string>::type tuple_name() {
782  return std::string{};
783 }
784 
786 template <typename T, std::size_t I>
787 inline typename std::enable_if<(I < type_count_base<T>::value), std::string>::type tuple_name() {
788  auto str = std::string{type_name<typename std::decay<typename std::tuple_element<I, T>::type>::type>()} + ',' +
789  tuple_name<T, I + 1>();
790  if(str.back() == ',')
791  str.pop_back();
792  return str;
793 }
794 
796 template <typename T,
797  enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
799 inline std::string type_name() {
800  auto tname = std::string(1, '[') + tuple_name<T, 0>();
801  tname.push_back(']');
802  return tname;
803 }
804 
806 template <typename T,
807  enable_if_t<classify_object<T>::value == object_category::container_value ||
808  classify_object<T>::value == object_category::wrapper_value,
810 inline std::string type_name() {
811  return type_name<typename T::value_type>();
812 }
813 
814 // Lexical cast
815 
817 template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
818 bool integral_conversion(const std::string &input, T &output) noexcept {
819  if(input.empty() || input.front() == '-') {
820  return false;
821  }
822  char *val = nullptr;
823  errno = 0;
824  std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);
825  if(errno == ERANGE) {
826  return false;
827  }
828  output = static_cast<T>(output_ll);
829  if(val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll) {
830  return true;
831  }
832  val = nullptr;
833  std::int64_t output_sll = std::strtoll(input.c_str(), &val, 0);
834  if(val == (input.c_str() + input.size())) {
835  output = (output_sll < 0) ? static_cast<T>(0) : static_cast<T>(output_sll);
836  return (static_cast<std::int64_t>(output) == output_sll);
837  }
838  return false;
839 }
840 
842 template <typename T, enable_if_t<std::is_signed<T>::value, detail::enabler> = detail::dummy>
843 bool integral_conversion(const std::string &input, T &output) noexcept {
844  if(input.empty()) {
845  return false;
846  }
847  char *val = nullptr;
848  errno = 0;
849  std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0);
850  if(errno == ERANGE) {
851  return false;
852  }
853  output = static_cast<T>(output_ll);
854  if(val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll) {
855  return true;
856  }
857  if(input == "true") {
858  // this is to deal with a few oddities with flags and wrapper int types
859  output = static_cast<T>(1);
860  return true;
861  }
862  return false;
863 }
864 
866 inline std::int64_t to_flag_value(std::string val) {
867  static const std::string trueString("true");
868  static const std::string falseString("false");
869  if(val == trueString) {
870  return 1;
871  }
872  if(val == falseString) {
873  return -1;
874  }
875  val = detail::to_lower(val);
876  std::int64_t ret = 0;
877  if(val.size() == 1) {
878  if(val[0] >= '1' && val[0] <= '9') {
879  return (static_cast<std::int64_t>(val[0]) - '0');
880  }
881  switch(val[0]) {
882  case '0':
883  case 'f':
884  case 'n':
885  case '-':
886  ret = -1;
887  break;
888  case 't':
889  case 'y':
890  case '+':
891  ret = 1;
892  break;
893  default:
894  throw std::invalid_argument("unrecognized character");
895  }
896  return ret;
897  }
898  if(val == trueString || val == "on" || val == "yes" || val == "enable") {
899  ret = 1;
900  } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
901  ret = -1;
902  } else {
903  ret = std::stoll(val);
904  }
905  return ret;
906 }
907 
909 template <typename T,
910  enable_if_t<classify_object<T>::value == object_category::integral_value ||
911  classify_object<T>::value == object_category::unsigned_integral,
913 bool lexical_cast(const std::string &input, T &output) {
914  return integral_conversion(input, output);
915 }
916 
918 template <typename T,
919  enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
920 bool lexical_cast(const std::string &input, T &output) {
921  if(input.size() == 1) {
922  output = static_cast<T>(input[0]);
923  return true;
924  }
925  return integral_conversion(input, output);
926 }
927 
929 template <typename T,
930  enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
931 bool lexical_cast(const std::string &input, T &output) {
932  try {
933  auto out = to_flag_value(input);
934  output = (out > 0);
935  return true;
936  } catch(const std::invalid_argument &) {
937  return false;
938  } catch(const std::out_of_range &) {
939  // 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
940  // valid all we care about the sign
941  output = (input[0] != '-');
942  return true;
943  }
944 }
945 
947 template <typename T,
948  enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
949 bool lexical_cast(const std::string &input, T &output) {
950  if(input.empty()) {
951  return false;
952  }
953  char *val = nullptr;
954  auto output_ld = std::strtold(input.c_str(), &val);
955  output = static_cast<T>(output_ld);
956  return val == (input.c_str() + input.size());
957 }
958 
960 template <typename T,
961  enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
962 bool lexical_cast(const std::string &input, T &output) {
963  using XC = typename wrapped_type<T, double>::type;
964  XC x{0.0}, y{0.0};
965  auto str1 = input;
966  bool worked = false;
967  auto nloc = str1.find_last_of("+-");
968  if(nloc != std::string::npos && nloc > 0) {
969  worked = lexical_cast(str1.substr(0, nloc), x);
970  str1 = str1.substr(nloc);
971  if(str1.back() == 'i' || str1.back() == 'j')
972  str1.pop_back();
973  worked = worked && lexical_cast(str1, y);
974  } else {
975  if(str1.back() == 'i' || str1.back() == 'j') {
976  str1.pop_back();
977  worked = lexical_cast(str1, y);
978  x = XC{0};
979  } else {
980  worked = lexical_cast(str1, x);
981  y = XC{0};
982  }
983  }
984  if(worked) {
985  output = T{x, y};
986  return worked;
987  }
988  return from_stream(input, output);
989 }
990 
992 template <typename T,
993  enable_if_t<classify_object<T>::value == object_category::string_assignable, detail::enabler> = detail::dummy>
994 bool lexical_cast(const std::string &input, T &output) {
995  output = input;
996  return true;
997 }
998 
1000 template <
1001  typename T,
1002  enable_if_t<classify_object<T>::value == object_category::string_constructible, detail::enabler> = detail::dummy>
1003 bool lexical_cast(const std::string &input, T &output) {
1004  output = T(input);
1005  return true;
1006 }
1007 
1009 template <typename T,
1010  enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
1011 bool lexical_cast(const std::string &input, T &output) {
1012  typename std::underlying_type<T>::type val;
1013  if(!integral_conversion(input, val)) {
1014  return false;
1015  }
1016  output = static_cast<T>(val);
1017  return true;
1018 }
1019 
1021 template <typename T,
1022  enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
1023  std::is_assignable<T &, typename T::value_type>::value,
1025 bool lexical_cast(const std::string &input, T &output) {
1026  typename T::value_type val;
1027  if(lexical_cast(input, val)) {
1028  output = val;
1029  return true;
1030  }
1031  return from_stream(input, output);
1032 }
1033 
1034 template <typename T,
1035  enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
1036  !std::is_assignable<T &, typename T::value_type>::value && std::is_assignable<T &, T>::value,
1038 bool lexical_cast(const std::string &input, T &output) {
1039  typename T::value_type val;
1040  if(lexical_cast(input, val)) {
1041  output = T{val};
1042  return true;
1043  }
1044  return from_stream(input, output);
1045 }
1046 
1048 template <
1049  typename T,
1050  enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
1051 bool lexical_cast(const std::string &input, T &output) {
1052  int val = 0;
1053  if(integral_conversion(input, val)) {
1054  output = T(val);
1055  return true;
1056  }
1057 
1058  double dval = 0.0;
1059  if(lexical_cast(input, dval)) {
1060  output = T{dval};
1061  return true;
1062  }
1063 
1064  return from_stream(input, output);
1065 }
1066 
1068 template <
1069  typename T,
1070  enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
1071 bool lexical_cast(const std::string &input, T &output) {
1072  int val = 0;
1073  if(integral_conversion(input, val)) {
1074  output = T(val);
1075  return true;
1076  }
1077  return from_stream(input, output);
1078 }
1079 
1081 template <
1082  typename T,
1083  enable_if_t<classify_object<T>::value == object_category::double_constructible, detail::enabler> = detail::dummy>
1084 bool lexical_cast(const std::string &input, T &output) {
1085  double val = 0.0;
1086  if(lexical_cast(input, val)) {
1087  output = T{val};
1088  return true;
1089  }
1090  return from_stream(input, output);
1091 }
1092 
1094 template <typename T,
1095  enable_if_t<classify_object<T>::value == object_category::other && std::is_assignable<T &, int>::value,
1097 bool lexical_cast(const std::string &input, T &output) {
1098  int val = 0;
1099  if(integral_conversion(input, val)) {
1100 #ifdef _MSC_VER
1101 #pragma warning(push)
1102 #pragma warning(disable : 4800)
1103 #endif
1104  // with Atomic<XX> this could produce a warning due to the conversion but if atomic gets here it is an old style
1105  // so will most likely still work
1106  output = val;
1107 #ifdef _MSC_VER
1108 #pragma warning(pop)
1109 #endif
1110  return true;
1111  }
1112  // LCOV_EXCL_START
1113  // This version of cast is only used for odd cases in an older compilers the fail over
1114  // from_stream is tested elsewhere an not relevant for coverage here
1115  return from_stream(input, output);
1116  // LCOV_EXCL_STOP
1117 }
1118 
1120 template <typename T,
1121  enable_if_t<classify_object<T>::value == object_category::other && !std::is_assignable<T &, int>::value,
1123 bool lexical_cast(const std::string &input, T &output) {
1124  static_assert(is_istreamable<T>::value,
1125  "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it "
1126  "is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
1127  return from_stream(input, output);
1128 }
1129 
1132 template <typename AssignTo,
1133  typename ConvertTo,
1134  enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&
1135  (classify_object<AssignTo>::value == object_category::string_assignable ||
1136  classify_object<AssignTo>::value == object_category::string_constructible),
1138 bool lexical_assign(const std::string &input, AssignTo &output) {
1139  return lexical_cast(input, output);
1140 }
1141 
1143 template <typename AssignTo,
1144  typename ConvertTo,
1145  enable_if_t<std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, AssignTo>::value &&
1146  classify_object<AssignTo>::value != object_category::string_assignable &&
1147  classify_object<AssignTo>::value != object_category::string_constructible,
1149 bool lexical_assign(const std::string &input, AssignTo &output) {
1150  if(input.empty()) {
1151  output = AssignTo{};
1152  return true;
1153  }
1154 
1155  return lexical_cast(input, output);
1156 }
1157 
1159 template <typename AssignTo,
1160  typename ConvertTo,
1161  enable_if_t<std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, AssignTo>::value &&
1162  classify_object<AssignTo>::value == object_category::wrapper_value,
1164 bool lexical_assign(const std::string &input, AssignTo &output) {
1165  if(input.empty()) {
1166  typename AssignTo::value_type emptyVal{};
1167  output = emptyVal;
1168  return true;
1169  }
1170  return lexical_cast(input, output);
1171 }
1172 
1175 template <typename AssignTo,
1176  typename ConvertTo,
1177  enable_if_t<std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, AssignTo>::value &&
1178  classify_object<AssignTo>::value != object_category::wrapper_value &&
1179  std::is_assignable<AssignTo &, int>::value,
1181 bool lexical_assign(const std::string &input, AssignTo &output) {
1182  if(input.empty()) {
1183  output = 0;
1184  return true;
1185  }
1186  int val = 0;
1187  if(lexical_cast(input, val)) {
1188  output = val;
1189  return true;
1190  }
1191  return false;
1192 }
1193 
1195 template <typename AssignTo,
1196  typename ConvertTo,
1197  enable_if_t<!std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, ConvertTo &>::value,
1199 bool lexical_assign(const std::string &input, AssignTo &output) {
1200  ConvertTo val{};
1201  bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true;
1202  if(parse_result) {
1203  output = val;
1204  }
1205  return parse_result;
1206 }
1207 
1209 template <
1210  typename AssignTo,
1211  typename ConvertTo,
1212  enable_if_t<!std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, ConvertTo &>::value &&
1213  std::is_move_assignable<AssignTo>::value,
1215 bool lexical_assign(const std::string &input, AssignTo &output) {
1216  ConvertTo val{};
1217  bool parse_result = input.empty() ? true : lexical_cast(input, val);
1218  if(parse_result) {
1219  output = AssignTo(val); // use () form of constructor to allow some implicit conversions
1220  }
1221  return parse_result;
1222 }
1223 
1225 template <typename AssignTo,
1226  typename ConvertTo,
1227  enable_if_t<classify_object<ConvertTo>::value <= object_category::other &&
1228  classify_object<AssignTo>::value <= object_category::wrapper_value,
1230 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1231  return lexical_assign<AssignTo, ConvertTo>(strings[0], output);
1232 }
1233 
1236 template <typename AssignTo,
1237  typename ConvertTo,
1238  enable_if_t<(type_count<AssignTo>::value <= 2) && expected_count<AssignTo>::value == 1 &&
1241 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1242  // the remove const is to handle pair types coming from a container
1243  typename std::remove_const<typename std::tuple_element<0, ConvertTo>::type>::type v1;
1244  typename std::tuple_element<1, ConvertTo>::type v2;
1245  bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);
1246  if(strings.size() > 1) {
1247  retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);
1248  }
1249  if(retval) {
1250  output = AssignTo{v1, v2};
1251  }
1252  return retval;
1253 }
1254 
1256 template <class AssignTo,
1257  class ConvertTo,
1258  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1261 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1262  output.erase(output.begin(), output.end());
1263  if(strings.size() == 1 && strings[0] == "{}") {
1264  return true;
1265  }
1266  bool skip_remaining = false;
1267  if(strings.size() == 2 && strings[0] == "{}" && is_separator(strings[1])) {
1268  skip_remaining = true;
1269  }
1270  for(const auto &elem : strings) {
1271  typename AssignTo::value_type out;
1272  bool retval = lexical_assign<typename AssignTo::value_type, typename ConvertTo::value_type>(elem, out);
1273  if(!retval) {
1274  return false;
1275  }
1276  output.insert(output.end(), std::move(out));
1277  if(skip_remaining) {
1278  break;
1279  }
1280  }
1281  return (!output.empty());
1282 }
1283 
1285 template <class AssignTo, class ConvertTo, enable_if_t<is_complex<ConvertTo>::value, detail::enabler> = detail::dummy>
1286 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
1287 
1288  if(strings.size() >= 2 && !strings[1].empty()) {
1289  using XC2 = typename wrapped_type<ConvertTo, double>::type;
1290  XC2 x{0.0}, y{0.0};
1291  auto str1 = strings[1];
1292  if(str1.back() == 'i' || str1.back() == 'j') {
1293  str1.pop_back();
1294  }
1295  auto worked = lexical_cast(strings[0], x) && lexical_cast(str1, y);
1296  if(worked) {
1297  output = ConvertTo{x, y};
1298  }
1299  return worked;
1300  }
1301  return lexical_assign<AssignTo, ConvertTo>(strings[0], output);
1302 }
1303 
1305 template <class AssignTo,
1306  class ConvertTo,
1307  enable_if_t<is_mutable_container<AssignTo>::value && (expected_count<ConvertTo>::value == 1) &&
1310 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1311  bool retval = true;
1312  output.clear();
1313  output.reserve(strings.size());
1314  for(const auto &elem : strings) {
1315 
1316  output.emplace_back();
1317  retval = retval && lexical_assign<typename AssignTo::value_type, ConvertTo>(elem, output.back());
1318  }
1319  return (!output.empty()) && retval;
1320 }
1321 
1322 // forward declaration
1323 
1325 template <class AssignTo,
1326  class ConvertTo,
1327  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1330 bool lexical_conversion(std::vector<std::string> strings, AssignTo &output);
1331 
1333 template <class AssignTo,
1334  class ConvertTo,
1335  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1340 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output);
1341 
1343 template <class AssignTo,
1344  class ConvertTo,
1345  enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
1349 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output); // forward declaration
1350 
1353 template <typename AssignTo,
1354  typename ConvertTo,
1355  enable_if_t<!is_tuple_like<AssignTo>::value && !is_mutable_container<AssignTo>::value &&
1356  classify_object<ConvertTo>::value != object_category::wrapper_value &&
1357  (is_mutable_container<ConvertTo>::value || type_count<ConvertTo>::value > 2),
1359 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1360 
1361  if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) {
1362  ConvertTo val;
1363  auto retval = lexical_conversion<ConvertTo, ConvertTo>(strings, val);
1364  output = AssignTo{val};
1365  return retval;
1366  }
1367  output = AssignTo{};
1368  return true;
1369 }
1370 
1372 template <class AssignTo, class ConvertTo, std::size_t I>
1373 inline typename std::enable_if<(I >= type_count_base<AssignTo>::value), bool>::type
1374 tuple_conversion(const std::vector<std::string> &, AssignTo &) {
1375  return true;
1376 }
1377 
1379 template <class AssignTo, class ConvertTo>
1380 inline typename std::enable_if<!is_mutable_container<ConvertTo>::value && type_count<ConvertTo>::value == 1, bool>::type
1381 tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
1382  auto retval = lexical_assign<AssignTo, ConvertTo>(strings[0], output);
1383  strings.erase(strings.begin());
1384  return retval;
1385 }
1386 
1388 template <class AssignTo, class ConvertTo>
1389 inline typename std::enable_if<!is_mutable_container<ConvertTo>::value && (type_count<ConvertTo>::value > 1) &&
1390  type_count<ConvertTo>::value == type_count_min<ConvertTo>::value,
1391  bool>::type
1392 tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
1393  auto retval = lexical_conversion<AssignTo, ConvertTo>(strings, output);
1394  strings.erase(strings.begin(), strings.begin() + type_count<ConvertTo>::value);
1395  return retval;
1396 }
1397 
1399 template <class AssignTo, class ConvertTo>
1400 inline typename std::enable_if<is_mutable_container<ConvertTo>::value ||
1401  type_count<ConvertTo>::value != type_count_min<ConvertTo>::value,
1402  bool>::type
1403 tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
1404 
1405  std::size_t index{subtype_count_min<ConvertTo>::value};
1406  const std::size_t mx_count{subtype_count<ConvertTo>::value};
1407  const std::size_t mx{(std::max)(mx_count, strings.size())};
1408 
1409  while(index < mx) {
1410  if(is_separator(strings[index])) {
1411  break;
1412  }
1413  ++index;
1414  }
1415  bool retval = lexical_conversion<AssignTo, ConvertTo>(
1416  std::vector<std::string>(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index)), output);
1417  strings.erase(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index) + 1);
1418  return retval;
1419 }
1420 
1422 template <class AssignTo, class ConvertTo, std::size_t I>
1423 inline typename std::enable_if<(I < type_count_base<AssignTo>::value), bool>::type
1424 tuple_conversion(std::vector<std::string> strings, AssignTo &output) {
1425  bool retval = true;
1426  using ConvertToElement = typename std::
1427  conditional<is_tuple_like<ConvertTo>::value, typename std::tuple_element<I, ConvertTo>::type, ConvertTo>::type;
1428  if(!strings.empty()) {
1429  retval = retval && tuple_type_conversion<typename std::tuple_element<I, AssignTo>::type, ConvertToElement>(
1430  strings, std::get<I>(output));
1431  }
1432  retval = retval && tuple_conversion<AssignTo, ConvertTo, I + 1>(std::move(strings), output);
1433  return retval;
1434 }
1435 
1437 template <class AssignTo,
1438  class ConvertTo,
1439  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1441  detail::enabler>>
1442 bool lexical_conversion(std::vector<std::string> strings, AssignTo &output) {
1443  output.clear();
1444  while(!strings.empty()) {
1445 
1446  typename std::remove_const<typename std::tuple_element<0, typename ConvertTo::value_type>::type>::type v1;
1447  typename std::tuple_element<1, typename ConvertTo::value_type>::type v2;
1448  bool retval = tuple_type_conversion<decltype(v1), decltype(v1)>(strings, v1);
1449  if(!strings.empty()) {
1450  retval = retval && tuple_type_conversion<decltype(v2), decltype(v2)>(strings, v2);
1451  }
1452  if(retval) {
1453  output.insert(output.end(), typename AssignTo::value_type{v1, v2});
1454  } else {
1455  return false;
1456  }
1457  }
1458  return (!output.empty());
1459 }
1460 
1462 template <class AssignTo,
1463  class ConvertTo,
1464  enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
1467  detail::enabler>>
1468 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1469  static_assert(
1471  "if the conversion type is defined as a tuple it must be the same size as the type you are converting to");
1472  return tuple_conversion<AssignTo, ConvertTo, 0>(strings, output);
1473 }
1474 
1476 template <class AssignTo,
1477  class ConvertTo,
1478  enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1482  detail::enabler>>
1483 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1484  bool retval = true;
1485  output.clear();
1486  std::vector<std::string> temp;
1487  std::size_t ii{0};
1488  std::size_t icount{0};
1489  std::size_t xcm{type_count<ConvertTo>::value};
1490  auto ii_max = strings.size();
1491  while(ii < ii_max) {
1492  temp.push_back(strings[ii]);
1493  ++ii;
1494  ++icount;
1495  if(icount == xcm || is_separator(temp.back()) || ii == ii_max) {
1496  if(static_cast<int>(xcm) > type_count_min<ConvertTo>::value && is_separator(temp.back())) {
1497  temp.pop_back();
1498  }
1499  typename AssignTo::value_type temp_out;
1500  retval = retval &&
1501  lexical_conversion<typename AssignTo::value_type, typename ConvertTo::value_type>(temp, temp_out);
1502  temp.clear();
1503  if(!retval) {
1504  return false;
1505  }
1506  output.insert(output.end(), std::move(temp_out));
1507  icount = 0;
1508  }
1509  }
1510  return retval;
1511 }
1512 
1514 template <typename AssignTo,
1515  class ConvertTo,
1516  enable_if_t<classify_object<ConvertTo>::value == object_category::wrapper_value &&
1517  std::is_assignable<ConvertTo &, ConvertTo>::value,
1519 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
1520  if(strings.empty() || strings.front().empty()) {
1521  output = ConvertTo{};
1522  return true;
1523  }
1524  typename ConvertTo::value_type val;
1525  if(lexical_conversion<typename ConvertTo::value_type, typename ConvertTo::value_type>(strings, val)) {
1526  output = ConvertTo{val};
1527  return true;
1528  }
1529  return false;
1530 }
1531 
1533 template <typename AssignTo,
1534  class ConvertTo,
1535  enable_if_t<classify_object<ConvertTo>::value == object_category::wrapper_value &&
1536  !std::is_assignable<AssignTo &, ConvertTo>::value,
1538 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
1539  using ConvertType = typename ConvertTo::value_type;
1540  if(strings.empty() || strings.front().empty()) {
1541  output = ConvertType{};
1542  return true;
1543  }
1544  ConvertType val;
1545  if(lexical_conversion<typename ConvertTo::value_type, typename ConvertTo::value_type>(strings, val)) {
1546  output = val;
1547  return true;
1548  }
1549  return false;
1550 }
1551 
1553 inline std::string sum_string_vector(const std::vector<std::string> &values) {
1554  double val{0.0};
1555  bool fail{false};
1556  std::string output;
1557  for(const auto &arg : values) {
1558  double tv{0.0};
1559  auto comp = lexical_cast(arg, tv);
1560  if(!comp) {
1561  try {
1562  tv = static_cast<double>(detail::to_flag_value(arg));
1563  } catch(const std::exception &) {
1564  fail = true;
1565  break;
1566  }
1567  }
1568  val += tv;
1569  }
1570  if(fail) {
1571  for(const auto &arg : values) {
1572  output.append(arg);
1573  }
1574  } else {
1575  if(val <= static_cast<double>((std::numeric_limits<std::int64_t>::min)()) ||
1576  val >= static_cast<double>((std::numeric_limits<std::int64_t>::max)()) ||
1577  std::ceil(val) == std::floor(val)) {
1578  output = detail::value_string(static_cast<int64_t>(val));
1579  } else {
1580  output = detail::value_string(val);
1581  }
1582  }
1583  return output;
1584 }
1585 
1586 } // namespace detail
1587 // [CLI11:type_tools_hpp:end]
1588 } // namespace CLI
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:54
void type
Definition: TypeTools.hpp:47
typename std::remove_const< typename value_type::second_type >::type second_type
Definition: TypeTools.hpp:133
static constexpr bool value
Definition: TypeTools.hpp:175
Definition: App.hpp:34
typename std::remove_const< value_type >::type second_type
Definition: TypeTools.hpp:112
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:109
Definition: TypeTools.hpp:104
enabler
Simple empty scoped class.
Definition: TypeTools.hpp:32
Check for complex.
Definition: TypeTools.hpp:206
A copy of std::void_t from C++17 (helper for C++11 and C++14)
Definition: TypeTools.hpp:46
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)||(uncommon_type< T >::value &&type_count< T >::value >=2))>::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() { auto 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:799
STL namespace.
static constexpr bool value
Definition: TypeTools.hpp:202
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:136
Check to see if something is bool (fail check by default)
Definition: TypeTools.hpp:57
bool is_separator(const std::string &str)
check if a string is a container segment separator (empty or "%%")
Definition: StringTools.hpp:163
typename std::remove_const< value_type >::type first_type
Definition: TypeTools.hpp:111
bool lexical_assign(const std::string &input, AssignTo &output)
Assign a value through lexical cast operations.
Definition: TypeTools.hpp:1138
bool lexical_cast(const std::string &input, T &output)
Integer conversion.
Definition: TypeTools.hpp:913
static auto second(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:119
constexpr enabler dummy
An instance to use in EnableIf.
Definition: TypeTools.hpp:35
template to get the underlying value type if it exists or use a default
Definition: TypeTools.hpp:367
def type
Definition: TypeTools.hpp:368
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:281
bool integral_conversion(const std::string &input, T &output) noexcept
Convert to an unsigned integral.
Definition: TypeTools.hpp:818
This can be specialized to override the type deduction for IsMember.
Definition: TypeTools.hpp:77
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:51
Definition: TypeTools.hpp:184
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:51
This will only trigger for actual void type.
Definition: TypeTools.hpp:377
typename std::remove_const< typename value_type::first_type >::type first_type
Definition: TypeTools.hpp:132
static bool const value
Definition: TypeTools.hpp:73
forward declare the subtype_count_min structure
Definition: TypeTools.hpp:406
std::int64_t to_flag_value(std::string val)
Convert a flag into an integer value typically binary flags.
Definition: TypeTools.hpp:866
static const int value
Definition: TypeTools.hpp:378
auto checked_to_string(T &&value) -> decltype(to_string(std::forward< T >(value)))
special template overload
Definition: TypeTools.hpp:337
Set of overloads to get the type size of an object.
Definition: TypeTools.hpp:403
typename std::enable_if< B, T >::type enable_if_t
Definition: TypeTools.hpp:43
Definition: TypeTools.hpp:231
static const int value
Definition: TypeTools.hpp:410
not a pointer
Definition: TypeTools.hpp:94
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:140
std::string to_lower(std::string str)
Return a lower case version of a string.
Definition: StringTools.hpp:174
static auto first(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:115
Definition: TypeTools.hpp:261
typename element_type< T >::type::value_type type
Definition: TypeTools.hpp:105
This will only trigger for actual void type.
Definition: TypeTools.hpp:409
Check to see if something is a shared pointer.
Definition: TypeTools.hpp:63
typename std::pointer_traits< T >::element_type type
Definition: TypeTools.hpp:99
constexpr std::enable_if< I==type_count_base< T >::value, int >::type tuple_type_size()
0 if the index > tuple size
Definition: TypeTools.hpp:441
static constexpr bool value
Definition: TypeTools.hpp:276
std::string value_string(const T &value)
get a string as a convertible value for arithmetic types
Definition: TypeTools.hpp:351
Definition: TypeTools.hpp:268
T type
Definition: TypeTools.hpp:78
static constexpr bool value
Definition: TypeTools.hpp:191
bool from_stream(const std::string &istring, T &obj)
Templated operation to get a value from a stream.
Definition: TypeTools.hpp:218
static constexpr bool value
Definition: TypeTools.hpp:213
Check to see if something is copyable pointer.
Definition: TypeTools.hpp:72
Check for input streamability.
Definition: TypeTools.hpp:195
Definition: TypeTools.hpp:156
typename T::value_type value_type
Definition: TypeTools.hpp:110
Definition: TypeTools.hpp:249
std::string type
Definition: TypeTools.hpp:83
std::string to_string(T &&value)
Convert an object to a string (streaming must be supported for that type)
Definition: TypeTools.hpp:298
constexpr int expected_max_vector_size
Definition: StringTools.hpp:45
T type
Definition: TypeTools.hpp:95