Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-09-28 07:02:19

0001 // Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
0002 // under NSF AWARD 1414736 and by the respective contributors.
0003 // All rights reserved.
0004 //
0005 // SPDX-License-Identifier: BSD-3-Clause
0006 
0007 #pragma once
0008 
0009 // [CLI11:public_includes:set]
0010 #include <cstdint>
0011 #include <exception>
0012 #include <memory>
0013 #include <string>
0014 #include <type_traits>
0015 #include <utility>
0016 #include <vector>
0017 // [CLI11:public_includes:end]
0018 
0019 #include "StringTools.hpp"
0020 
0021 namespace CLI {
0022 // [CLI11:type_tools_hpp:verbatim]
0023 
0024 // Type tools
0025 
0026 // Utilities for type enabling
0027 namespace detail {
0028 // Based generally on https://rmf.io/cxx11/almost-static-if
0029 /// Simple empty scoped class
0030 enum class enabler {};
0031 
0032 /// An instance to use in EnableIf
0033 constexpr enabler dummy = {};
0034 }  // namespace detail
0035 
0036 /// A copy of enable_if_t from C++14, compatible with C++11.
0037 ///
0038 /// We could check to see if C++14 is being used, but it does not hurt to redefine this
0039 /// (even Google does this: https://github.com/google/skia/blob/master/include/private/SkTLogic.h)
0040 /// It is not in the std namespace anyway, so no harm done.
0041 template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
0042 
0043 /// A copy of std::void_t from C++17 (helper for C++11 and C++14)
0044 template <typename... Ts> struct make_void { using type = void; };
0045 
0046 /// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine
0047 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
0048 
0049 /// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine
0050 template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;
0051 
0052 /// Check to see if something is bool (fail check by default)
0053 template <typename T> struct is_bool : std::false_type {};
0054 
0055 /// Check to see if something is bool (true if actually a bool)
0056 template <> struct is_bool<bool> : std::true_type {};
0057 
0058 /// Check to see if something is a shared pointer
0059 template <typename T> struct is_shared_ptr : std::false_type {};
0060 
0061 /// Check to see if something is a shared pointer (True if really a shared pointer)
0062 template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
0063 
0064 /// Check to see if something is a shared pointer (True if really a shared pointer)
0065 template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};
0066 
0067 /// Check to see if something is copyable pointer
0068 template <typename T> struct is_copyable_ptr {
0069     static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
0070 };
0071 
0072 /// This can be specialized to override the type deduction for IsMember.
0073 template <typename T> struct IsMemberType { using type = T; };
0074 
0075 /// The main custom type needed here is const char * should be a string.
0076 template <> struct IsMemberType<const char *> { using type = std::string; };
0077 
0078 namespace detail {
0079 
0080 // These are utilities for IsMember and other transforming objects
0081 
0082 /// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that
0083 /// pointer_traits<T> be valid.
0084 
0085 /// not a pointer
0086 template <typename T, typename Enable = void> struct element_type { using type = T; };
0087 
0088 template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
0089     using type = typename std::pointer_traits<T>::element_type;
0090 };
0091 
0092 /// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of
0093 /// the container
0094 template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };
0095 
0096 /// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing.
0097 template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
0098     using value_type = typename T::value_type;
0099     using first_type = typename std::remove_const<value_type>::type;
0100     using second_type = typename std::remove_const<value_type>::type;
0101 
0102     /// Get the first value (really just the underlying value)
0103     template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
0104         return std::forward<Q>(pair_value);
0105     }
0106     /// Get the second value (really just the underlying value)
0107     template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
0108         return std::forward<Q>(pair_value);
0109     }
0110 };
0111 
0112 /// Adaptor for map-like structure (true version, must have key_type and mapped_type).
0113 /// This wraps a mapped container in a few utilities access it in a general way.
0114 template <typename T>
0115 struct pair_adaptor<
0116     T,
0117     conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
0118     : std::true_type {
0119     using value_type = typename T::value_type;
0120     using first_type = typename std::remove_const<typename value_type::first_type>::type;
0121     using second_type = typename std::remove_const<typename value_type::second_type>::type;
0122 
0123     /// Get the first value (really just the underlying value)
0124     template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
0125         return std::get<0>(std::forward<Q>(pair_value));
0126     }
0127     /// Get the second value (really just the underlying value)
0128     template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
0129         return std::get<1>(std::forward<Q>(pair_value));
0130     }
0131 };
0132 
0133 // Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning
0134 // in the unevaluated context even if the function that was using this wasn't used.  The standard says narrowing in
0135 // brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts.  It is a
0136 // little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out.
0137 // But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be
0138 // suppressed
0139 #ifdef __GNUC__
0140 #pragma GCC diagnostic push
0141 #pragma GCC diagnostic ignored "-Wnarrowing"
0142 #endif
0143 // check for constructibility from a specific type and copy assignable used in the parse detection
0144 template <typename T, typename C> class is_direct_constructible {
0145     template <typename TT, typename CC>
0146     static auto test(int, std::true_type) -> decltype(
0147 // NVCC warns about narrowing conversions here
0148 #ifdef __CUDACC__
0149 #pragma diag_suppress 2361
0150 #endif
0151         TT { std::declval<CC>() }
0152 #ifdef __CUDACC__
0153 #pragma diag_default 2361
0154 #endif
0155         ,
0156         std::is_move_assignable<TT>());
0157 
0158     template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
0159 
0160     template <typename, typename> static auto test(...) -> std::false_type;
0161 
0162   public:
0163     static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
0164 };
0165 #ifdef __GNUC__
0166 #pragma GCC diagnostic pop
0167 #endif
0168 
0169 // Check for output streamability
0170 // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
0171 
0172 template <typename T, typename S = std::ostringstream> class is_ostreamable {
0173     template <typename TT, typename SS>
0174     static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
0175 
0176     template <typename, typename> static auto test(...) -> std::false_type;
0177 
0178   public:
0179     static constexpr bool value = decltype(test<T, S>(0))::value;
0180 };
0181 
0182 /// Check for input streamability
0183 template <typename T, typename S = std::istringstream> class is_istreamable {
0184     template <typename TT, typename SS>
0185     static auto test(int) -> decltype(std::declval<SS &>() >> std::declval<TT &>(), std::true_type());
0186 
0187     template <typename, typename> static auto test(...) -> std::false_type;
0188 
0189   public:
0190     static constexpr bool value = decltype(test<T, S>(0))::value;
0191 };
0192 
0193 /// Check for complex
0194 template <typename T> class is_complex {
0195     template <typename TT>
0196     static auto test(int) -> decltype(std::declval<TT>().real(), std::declval<TT>().imag(), std::true_type());
0197 
0198     template <typename> static auto test(...) -> std::false_type;
0199 
0200   public:
0201     static constexpr bool value = decltype(test<T>(0))::value;
0202 };
0203 
0204 /// Templated operation to get a value from a stream
0205 template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
0206 bool from_stream(const std::string &istring, T &obj) {
0207     std::istringstream is;
0208     is.str(istring);
0209     is >> obj;
0210     return !is.fail() && !is.rdbuf()->in_avail();
0211 }
0212 
0213 template <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> = detail::dummy>
0214 bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
0215     return false;
0216 }
0217 
0218 // check to see if an object is a mutable container (fail by default)
0219 template <typename T, typename _ = void> struct is_mutable_container : std::false_type {};
0220 
0221 /// type trait to test if a type is a mutable container meaning it has a value_type, it has an iterator, a clear, and
0222 /// end methods and an insert function.  And for our purposes we exclude std::string and types that can be constructed
0223 /// from a std::string
0224 template <typename T>
0225 struct is_mutable_container<
0226     T,
0227     conditional_t<false,
0228                   void_t<typename T::value_type,
0229                          decltype(std::declval<T>().end()),
0230                          decltype(std::declval<T>().clear()),
0231                          decltype(std::declval<T>().insert(std::declval<decltype(std::declval<T>().end())>(),
0232                                                            std::declval<const typename T::value_type &>()))>,
0233                   void>>
0234     : public conditional_t<std::is_constructible<T, std::string>::value, std::false_type, std::true_type> {};
0235 
0236 // check to see if an object is a mutable container (fail by default)
0237 template <typename T, typename _ = void> struct is_readable_container : std::false_type {};
0238 
0239 /// type trait to test if a type is a container meaning it has a value_type, it has an iterator, a clear, and an end
0240 /// methods and an insert function.  And for our purposes we exclude std::string and types that can be constructed from
0241 /// a std::string
0242 template <typename T>
0243 struct is_readable_container<
0244     T,
0245     conditional_t<false, void_t<decltype(std::declval<T>().end()), decltype(std::declval<T>().begin())>, void>>
0246     : public std::true_type {};
0247 
0248 // check to see if an object is a wrapper (fail by default)
0249 template <typename T, typename _ = void> struct is_wrapper : std::false_type {};
0250 
0251 // check if an object is a wrapper (it has a value_type defined)
0252 template <typename T>
0253 struct is_wrapper<T, conditional_t<false, void_t<typename T::value_type>, void>> : public std::true_type {};
0254 
0255 // Check for tuple like types, as in classes with a tuple_size type trait
0256 template <typename S> class is_tuple_like {
0257     template <typename SS>
0258     // static auto test(int)
0259     //     -> decltype(std::conditional<(std::tuple_size<SS>::value > 0), std::true_type, std::false_type>::type());
0260     static auto test(int) -> decltype(std::tuple_size<typename std::decay<SS>::type>::value, std::true_type{});
0261     template <typename> static auto test(...) -> std::false_type;
0262 
0263   public:
0264     static constexpr bool value = decltype(test<S>(0))::value;
0265 };
0266 
0267 /// Convert an object to a string (directly forward if this can become a string)
0268 template <typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
0269 auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
0270     return std::forward<T>(value);
0271 }
0272 
0273 /// Construct a string from the object
0274 template <typename T,
0275           enable_if_t<std::is_constructible<std::string, T>::value && !std::is_convertible<T, std::string>::value,
0276                       detail::enabler> = detail::dummy>
0277 std::string to_string(const T &value) {
0278     return std::string(value);
0279 }
0280 
0281 /// Convert an object to a string (streaming must be supported for that type)
0282 template <typename T,
0283           enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
0284                           is_ostreamable<T>::value,
0285                       detail::enabler> = detail::dummy>
0286 std::string to_string(T &&value) {
0287     std::stringstream stream;
0288     stream << value;
0289     return stream.str();
0290 }
0291 
0292 /// If conversion is not supported, return an empty string (streaming is not supported for that type)
0293 template <typename T,
0294           enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
0295                           !is_readable_container<typename std::remove_const<T>::type>::value,
0296                       detail::enabler> = detail::dummy>
0297 std::string to_string(T &&) {
0298     return std::string{};
0299 }
0300 
0301 /// convert a readable container to a string
0302 template <typename T,
0303           enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
0304                           is_readable_container<T>::value,
0305                       detail::enabler> = detail::dummy>
0306 std::string to_string(T &&variable) {
0307     std::vector<std::string> defaults;
0308     auto cval = variable.begin();
0309     auto end = variable.end();
0310     while(cval != end) {
0311         defaults.emplace_back(CLI::detail::to_string(*cval));
0312         ++cval;
0313     }
0314     return std::string("[" + detail::join(defaults) + "]");
0315 }
0316 
0317 /// special template overload
0318 template <typename T1,
0319           typename T2,
0320           typename T,
0321           enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
0322 auto checked_to_string(T &&value) -> decltype(to_string(std::forward<T>(value))) {
0323     return to_string(std::forward<T>(value));
0324 }
0325 
0326 /// special template overload
0327 template <typename T1,
0328           typename T2,
0329           typename T,
0330           enable_if_t<!std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
0331 std::string checked_to_string(T &&) {
0332     return std::string{};
0333 }
0334 /// get a string as a convertible value for arithmetic types
0335 template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
0336 std::string value_string(const T &value) {
0337     return std::to_string(value);
0338 }
0339 /// get a string as a convertible value for enumerations
0340 template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
0341 std::string value_string(const T &value) {
0342     return std::to_string(static_cast<typename std::underlying_type<T>::type>(value));
0343 }
0344 /// for other types just use the regular to_string function
0345 template <typename T,
0346           enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
0347 auto value_string(const T &value) -> decltype(to_string(value)) {
0348     return to_string(value);
0349 }
0350 
0351 /// template to get the underlying value type if it exists or use a default
0352 template <typename T, typename def, typename Enable = void> struct wrapped_type { using type = def; };
0353 
0354 /// Type size for regular object types that do not look like a tuple
0355 template <typename T, typename def> struct wrapped_type<T, def, typename std::enable_if<is_wrapper<T>::value>::type> {
0356     using type = typename T::value_type;
0357 };
0358 
0359 /// This will only trigger for actual void type
0360 template <typename T, typename Enable = void> struct type_count_base { static const int value{0}; };
0361 
0362 /// Type size for regular object types that do not look like a tuple
0363 template <typename T>
0364 struct type_count_base<T,
0365                        typename std::enable_if<!is_tuple_like<T>::value && !is_mutable_container<T>::value &&
0366                                                !std::is_void<T>::value>::type> {
0367     static constexpr int value{1};
0368 };
0369 
0370 /// the base tuple size
0371 template <typename T>
0372 struct type_count_base<T, typename std::enable_if<is_tuple_like<T>::value && !is_mutable_container<T>::value>::type> {
0373     static constexpr int value{std::tuple_size<T>::value};
0374 };
0375 
0376 /// Type count base for containers is the type_count_base of the individual element
0377 template <typename T> struct type_count_base<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
0378     static constexpr int value{type_count_base<typename T::value_type>::value};
0379 };
0380 
0381 /// Set of overloads to get the type size of an object
0382 
0383 /// forward declare the subtype_count structure
0384 template <typename T> struct subtype_count;
0385 
0386 /// forward declare the subtype_count_min structure
0387 template <typename T> struct subtype_count_min;
0388 
0389 /// This will only trigger for actual void type
0390 template <typename T, typename Enable = void> struct type_count { static const int value{0}; };
0391 
0392 /// Type size for regular object types that do not look like a tuple
0393 template <typename T>
0394 struct type_count<T,
0395                   typename std::enable_if<!is_wrapper<T>::value && !is_tuple_like<T>::value && !is_complex<T>::value &&
0396                                           !std::is_void<T>::value>::type> {
0397     static constexpr int value{1};
0398 };
0399 
0400 /// Type size for complex since it sometimes looks like a wrapper
0401 template <typename T> struct type_count<T, typename std::enable_if<is_complex<T>::value>::type> {
0402     static constexpr int value{2};
0403 };
0404 
0405 /// Type size of types that are wrappers,except complex and tuples(which can also be wrappers sometimes)
0406 template <typename T> struct type_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
0407     static constexpr int value{subtype_count<typename T::value_type>::value};
0408 };
0409 
0410 /// Type size of types that are wrappers,except containers complex and tuples(which can also be wrappers sometimes)
0411 template <typename T>
0412 struct type_count<T,
0413                   typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value &&
0414                                           !is_mutable_container<T>::value>::type> {
0415     static constexpr int value{type_count<typename T::value_type>::value};
0416 };
0417 
0418 /// 0 if the index > tuple size
0419 template <typename T, std::size_t I>
0420 constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size() {
0421     return 0;
0422 }
0423 
0424 /// Recursively generate the tuple type name
0425 template <typename T, std::size_t I>
0426     constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size() {
0427     return subtype_count<typename std::tuple_element<I, T>::type>::value + tuple_type_size<T, I + 1>();
0428 }
0429 
0430 /// Get the type size of the sum of type sizes for all the individual tuple types
0431 template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
0432     static constexpr int value{tuple_type_size<T, 0>()};
0433 };
0434 
0435 /// definition of subtype count
0436 template <typename T> struct subtype_count {
0437     static constexpr int value{is_mutable_container<T>::value ? expected_max_vector_size : type_count<T>::value};
0438 };
0439 
0440 /// This will only trigger for actual void type
0441 template <typename T, typename Enable = void> struct type_count_min { static const int value{0}; };
0442 
0443 /// Type size for regular object types that do not look like a tuple
0444 template <typename T>
0445 struct type_count_min<
0446     T,
0447     typename std::enable_if<!is_mutable_container<T>::value && !is_tuple_like<T>::value && !is_wrapper<T>::value &&
0448                             !is_complex<T>::value && !std::is_void<T>::value>::type> {
0449     static constexpr int value{type_count<T>::value};
0450 };
0451 
0452 /// Type size for complex since it sometimes looks like a wrapper
0453 template <typename T> struct type_count_min<T, typename std::enable_if<is_complex<T>::value>::type> {
0454     static constexpr int value{1};
0455 };
0456 
0457 /// Type size min of types that are wrappers,except complex and tuples(which can also be wrappers sometimes)
0458 template <typename T>
0459 struct type_count_min<
0460     T,
0461     typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value>::type> {
0462     static constexpr int value{subtype_count_min<typename T::value_type>::value};
0463 };
0464 
0465 /// 0 if the index > tuple size
0466 template <typename T, std::size_t I>
0467 constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size_min() {
0468     return 0;
0469 }
0470 
0471 /// Recursively generate the tuple type name
0472 template <typename T, std::size_t I>
0473     constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size_min() {
0474     return subtype_count_min<typename std::tuple_element<I, T>::type>::value + tuple_type_size_min<T, I + 1>();
0475 }
0476 
0477 /// Get the type size of the sum of type sizes for all the individual tuple types
0478 template <typename T> struct type_count_min<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
0479     static constexpr int value{tuple_type_size_min<T, 0>()};
0480 };
0481 
0482 /// definition of subtype count
0483 template <typename T> struct subtype_count_min {
0484     static constexpr int value{is_mutable_container<T>::value
0485                                    ? ((type_count<T>::value < expected_max_vector_size) ? type_count<T>::value : 0)
0486                                    : type_count_min<T>::value};
0487 };
0488 
0489 /// This will only trigger for actual void type
0490 template <typename T, typename Enable = void> struct expected_count { static const int value{0}; };
0491 
0492 /// For most types the number of expected items is 1
0493 template <typename T>
0494 struct expected_count<T,
0495                       typename std::enable_if<!is_mutable_container<T>::value && !is_wrapper<T>::value &&
0496                                               !std::is_void<T>::value>::type> {
0497     static constexpr int value{1};
0498 };
0499 /// number of expected items in a vector
0500 template <typename T> struct expected_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
0501     static constexpr int value{expected_max_vector_size};
0502 };
0503 
0504 /// number of expected items in a vector
0505 template <typename T>
0506 struct expected_count<T, typename std::enable_if<!is_mutable_container<T>::value && is_wrapper<T>::value>::type> {
0507     static constexpr int value{expected_count<typename T::value_type>::value};
0508 };
0509 
0510 // Enumeration of the different supported categorizations of objects
0511 enum class object_category : int {
0512     char_value = 1,
0513     integral_value = 2,
0514     unsigned_integral = 4,
0515     enumeration = 6,
0516     boolean_value = 8,
0517     floating_point = 10,
0518     number_constructible = 12,
0519     double_constructible = 14,
0520     integer_constructible = 16,
0521     // string like types
0522     string_assignable = 23,
0523     string_constructible = 24,
0524     other = 45,
0525     // special wrapper or container types
0526     wrapper_value = 50,
0527     complex_number = 60,
0528     tuple_value = 70,
0529     container_value = 80,
0530 
0531 };
0532 
0533 /// Set of overloads to classify an object according to type
0534 
0535 /// some type that is not otherwise recognized
0536 template <typename T, typename Enable = void> struct classify_object {
0537     static constexpr object_category value{object_category::other};
0538 };
0539 
0540 /// Signed integers
0541 template <typename T>
0542 struct classify_object<
0543     T,
0544     typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && std::is_signed<T>::value &&
0545                             !is_bool<T>::value && !std::is_enum<T>::value>::type> {
0546     static constexpr object_category value{object_category::integral_value};
0547 };
0548 
0549 /// Unsigned integers
0550 template <typename T>
0551 struct classify_object<T,
0552                        typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value &&
0553                                                !std::is_same<T, char>::value && !is_bool<T>::value>::type> {
0554     static constexpr object_category value{object_category::unsigned_integral};
0555 };
0556 
0557 /// single character values
0558 template <typename T>
0559 struct classify_object<T, typename std::enable_if<std::is_same<T, char>::value && !std::is_enum<T>::value>::type> {
0560     static constexpr object_category value{object_category::char_value};
0561 };
0562 
0563 /// Boolean values
0564 template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
0565     static constexpr object_category value{object_category::boolean_value};
0566 };
0567 
0568 /// Floats
0569 template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
0570     static constexpr object_category value{object_category::floating_point};
0571 };
0572 
0573 /// String and similar direct assignment
0574 template <typename T>
0575 struct classify_object<T,
0576                        typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
0577                                                std::is_assignable<T &, std::string>::value>::type> {
0578     static constexpr object_category value{object_category::string_assignable};
0579 };
0580 
0581 /// String and similar constructible and copy assignment
0582 template <typename T>
0583 struct classify_object<
0584     T,
0585     typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
0586                             !std::is_assignable<T &, std::string>::value && (type_count<T>::value == 1) &&
0587                             std::is_constructible<T, std::string>::value>::type> {
0588     static constexpr object_category value{object_category::string_constructible};
0589 };
0590 
0591 /// Enumerations
0592 template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
0593     static constexpr object_category value{object_category::enumeration};
0594 };
0595 
0596 template <typename T> struct classify_object<T, typename std::enable_if<is_complex<T>::value>::type> {
0597     static constexpr object_category value{object_category::complex_number};
0598 };
0599 
0600 /// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point,
0601 /// vectors, and enumerations
0602 template <typename T> struct uncommon_type {
0603     using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
0604                                                !std::is_assignable<T &, std::string>::value &&
0605                                                !std::is_constructible<T, std::string>::value && !is_complex<T>::value &&
0606                                                !is_mutable_container<T>::value && !std::is_enum<T>::value,
0607                                            std::true_type,
0608                                            std::false_type>::type;
0609     static constexpr bool value = type::value;
0610 };
0611 
0612 /// wrapper type
0613 template <typename T>
0614 struct classify_object<T,
0615                        typename std::enable_if<(!is_mutable_container<T>::value && is_wrapper<T>::value &&
0616                                                 !is_tuple_like<T>::value && uncommon_type<T>::value)>::type> {
0617     static constexpr object_category value{object_category::wrapper_value};
0618 };
0619 
0620 /// Assignable from double or int
0621 template <typename T>
0622 struct classify_object<T,
0623                        typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
0624                                                !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
0625                                                is_direct_constructible<T, int>::value>::type> {
0626     static constexpr object_category value{object_category::number_constructible};
0627 };
0628 
0629 /// Assignable from int
0630 template <typename T>
0631 struct classify_object<T,
0632                        typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
0633                                                !is_wrapper<T>::value && !is_direct_constructible<T, double>::value &&
0634                                                is_direct_constructible<T, int>::value>::type> {
0635     static constexpr object_category value{object_category::integer_constructible};
0636 };
0637 
0638 /// Assignable from double
0639 template <typename T>
0640 struct classify_object<T,
0641                        typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
0642                                                !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
0643                                                !is_direct_constructible<T, int>::value>::type> {
0644     static constexpr object_category value{object_category::double_constructible};
0645 };
0646 
0647 /// Tuple type
0648 template <typename T>
0649 struct classify_object<
0650     T,
0651     typename std::enable_if<is_tuple_like<T>::value &&
0652                             ((type_count<T>::value >= 2 && !is_wrapper<T>::value) ||
0653                              (uncommon_type<T>::value && !is_direct_constructible<T, double>::value &&
0654                               !is_direct_constructible<T, int>::value))>::type> {
0655     static constexpr object_category value{object_category::tuple_value};
0656     // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be
0657     // constructed from just the first element so tuples of <string, int,int> can be constructed from a string, which
0658     // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2
0659     // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out
0660     // those cases that are caught by other object classifications
0661 };
0662 
0663 /// container type
0664 template <typename T> struct classify_object<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
0665     static constexpr object_category value{object_category::container_value};
0666 };
0667 
0668 // Type name print
0669 
0670 /// Was going to be based on
0671 ///  http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template
0672 /// But this is cleaner and works better in this case
0673 
0674 template <typename T,
0675           enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
0676 constexpr const char *type_name() {
0677     return "CHAR";
0678 }
0679 
0680 template <typename T,
0681           enable_if_t<classify_object<T>::value == object_category::integral_value ||
0682                           classify_object<T>::value == object_category::integer_constructible,
0683                       detail::enabler> = detail::dummy>
0684 constexpr const char *type_name() {
0685     return "INT";
0686 }
0687 
0688 template <typename T,
0689           enable_if_t<classify_object<T>::value == object_category::unsigned_integral, detail::enabler> = detail::dummy>
0690 constexpr const char *type_name() {
0691     return "UINT";
0692 }
0693 
0694 template <typename T,
0695           enable_if_t<classify_object<T>::value == object_category::floating_point ||
0696                           classify_object<T>::value == object_category::number_constructible ||
0697                           classify_object<T>::value == object_category::double_constructible,
0698                       detail::enabler> = detail::dummy>
0699 constexpr const char *type_name() {
0700     return "FLOAT";
0701 }
0702 
0703 /// Print name for enumeration types
0704 template <typename T,
0705           enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
0706 constexpr const char *type_name() {
0707     return "ENUM";
0708 }
0709 
0710 /// Print name for enumeration types
0711 template <typename T,
0712           enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
0713 constexpr const char *type_name() {
0714     return "BOOLEAN";
0715 }
0716 
0717 /// Print name for enumeration types
0718 template <typename T,
0719           enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
0720 constexpr const char *type_name() {
0721     return "COMPLEX";
0722 }
0723 
0724 /// Print for all other types
0725 template <typename T,
0726           enable_if_t<classify_object<T>::value >= object_category::string_assignable &&
0727                           classify_object<T>::value <= object_category::other,
0728                       detail::enabler> = detail::dummy>
0729 constexpr const char *type_name() {
0730     return "TEXT";
0731 }
0732 /// typename for tuple value
0733 template <typename T,
0734           enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
0735                       detail::enabler> = detail::dummy>
0736 std::string type_name();  // forward declaration
0737 
0738 /// Generate type name for a wrapper or container value
0739 template <typename T,
0740           enable_if_t<classify_object<T>::value == object_category::container_value ||
0741                           classify_object<T>::value == object_category::wrapper_value,
0742                       detail::enabler> = detail::dummy>
0743 std::string type_name();  // forward declaration
0744 
0745 /// Print name for single element tuple types
0746 template <typename T,
0747           enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value == 1,
0748                       detail::enabler> = detail::dummy>
0749 inline std::string type_name() {
0750     return type_name<typename std::decay<typename std::tuple_element<0, T>::type>::type>();
0751 }
0752 
0753 /// Empty string if the index > tuple size
0754 template <typename T, std::size_t I>
0755 inline typename std::enable_if<I == type_count_base<T>::value, std::string>::type tuple_name() {
0756     return std::string{};
0757 }
0758 
0759 /// Recursively generate the tuple type name
0760 template <typename T, std::size_t I>
0761 inline typename std::enable_if<(I < type_count_base<T>::value), std::string>::type tuple_name() {
0762     std::string str = std::string(type_name<typename std::decay<typename std::tuple_element<I, T>::type>::type>()) +
0763                       ',' + tuple_name<T, I + 1>();
0764     if(str.back() == ',')
0765         str.pop_back();
0766     return str;
0767 }
0768 
0769 /// Print type name for tuples with 2 or more elements
0770 template <typename T,
0771           enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
0772                       detail::enabler>>
0773 inline std::string type_name() {
0774     auto tname = std::string(1, '[') + tuple_name<T, 0>();
0775     tname.push_back(']');
0776     return tname;
0777 }
0778 
0779 /// get the type name for a type that has a value_type member
0780 template <typename T,
0781           enable_if_t<classify_object<T>::value == object_category::container_value ||
0782                           classify_object<T>::value == object_category::wrapper_value,
0783                       detail::enabler>>
0784 inline std::string type_name() {
0785     return type_name<typename T::value_type>();
0786 }
0787 
0788 // Lexical cast
0789 
0790 /// Convert to an unsigned integral
0791 template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
0792 bool integral_conversion(const std::string &input, T &output) noexcept {
0793     if(input.empty()) {
0794         return false;
0795     }
0796     char *val = nullptr;
0797     std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);
0798     output = static_cast<T>(output_ll);
0799     return val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll;
0800 }
0801 
0802 /// Convert to a signed integral
0803 template <typename T, enable_if_t<std::is_signed<T>::value, detail::enabler> = detail::dummy>
0804 bool integral_conversion(const std::string &input, T &output) noexcept {
0805     if(input.empty()) {
0806         return false;
0807     }
0808     char *val = nullptr;
0809     std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0);
0810     output = static_cast<T>(output_ll);
0811     return val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll;
0812 }
0813 
0814 /// Convert a flag into an integer value  typically binary flags
0815 inline std::int64_t to_flag_value(std::string val) {
0816     static const std::string trueString("true");
0817     static const std::string falseString("false");
0818     if(val == trueString) {
0819         return 1;
0820     }
0821     if(val == falseString) {
0822         return -1;
0823     }
0824     val = detail::to_lower(val);
0825     std::int64_t ret;
0826     if(val.size() == 1) {
0827         if(val[0] >= '1' && val[0] <= '9') {
0828             return (static_cast<std::int64_t>(val[0]) - '0');
0829         }
0830         switch(val[0]) {
0831         case '0':
0832         case 'f':
0833         case 'n':
0834         case '-':
0835             ret = -1;
0836             break;
0837         case 't':
0838         case 'y':
0839         case '+':
0840             ret = 1;
0841             break;
0842         default:
0843             throw std::invalid_argument("unrecognized character");
0844         }
0845         return ret;
0846     }
0847     if(val == trueString || val == "on" || val == "yes" || val == "enable") {
0848         ret = 1;
0849     } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
0850         ret = -1;
0851     } else {
0852         ret = std::stoll(val);
0853     }
0854     return ret;
0855 }
0856 
0857 /// Integer conversion
0858 template <typename T,
0859           enable_if_t<classify_object<T>::value == object_category::integral_value ||
0860                           classify_object<T>::value == object_category::unsigned_integral,
0861                       detail::enabler> = detail::dummy>
0862 bool lexical_cast(const std::string &input, T &output) {
0863     return integral_conversion(input, output);
0864 }
0865 
0866 /// char values
0867 template <typename T,
0868           enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
0869 bool lexical_cast(const std::string &input, T &output) {
0870     if(input.size() == 1) {
0871         output = static_cast<T>(input[0]);
0872         return true;
0873     }
0874     return integral_conversion(input, output);
0875 }
0876 
0877 /// Boolean values
0878 template <typename T,
0879           enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
0880 bool lexical_cast(const std::string &input, T &output) {
0881     try {
0882         auto out = to_flag_value(input);
0883         output = (out > 0);
0884         return true;
0885     } catch(const std::invalid_argument &) {
0886         return false;
0887     } catch(const std::out_of_range &) {
0888         // 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
0889         // valid all we care about the sign
0890         output = (input[0] != '-');
0891         return true;
0892     }
0893 }
0894 
0895 /// Floats
0896 template <typename T,
0897           enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
0898 bool lexical_cast(const std::string &input, T &output) {
0899     if(input.empty()) {
0900         return false;
0901     }
0902     char *val = nullptr;
0903     auto output_ld = std::strtold(input.c_str(), &val);
0904     output = static_cast<T>(output_ld);
0905     return val == (input.c_str() + input.size());
0906 }
0907 
0908 /// complex
0909 template <typename T,
0910           enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
0911 bool lexical_cast(const std::string &input, T &output) {
0912     using XC = typename wrapped_type<T, double>::type;
0913     XC x{0.0}, y{0.0};
0914     auto str1 = input;
0915     bool worked = false;
0916     auto nloc = str1.find_last_of("+-");
0917     if(nloc != std::string::npos && nloc > 0) {
0918         worked = detail::lexical_cast(str1.substr(0, nloc), x);
0919         str1 = str1.substr(nloc);
0920         if(str1.back() == 'i' || str1.back() == 'j')
0921             str1.pop_back();
0922         worked = worked && detail::lexical_cast(str1, y);
0923     } else {
0924         if(str1.back() == 'i' || str1.back() == 'j') {
0925             str1.pop_back();
0926             worked = detail::lexical_cast(str1, y);
0927             x = XC{0};
0928         } else {
0929             worked = detail::lexical_cast(str1, x);
0930             y = XC{0};
0931         }
0932     }
0933     if(worked) {
0934         output = T{x, y};
0935         return worked;
0936     }
0937     return from_stream(input, output);
0938 }
0939 
0940 /// String and similar direct assignment
0941 template <typename T,
0942           enable_if_t<classify_object<T>::value == object_category::string_assignable, detail::enabler> = detail::dummy>
0943 bool lexical_cast(const std::string &input, T &output) {
0944     output = input;
0945     return true;
0946 }
0947 
0948 /// String and similar constructible and copy assignment
0949 template <
0950     typename T,
0951     enable_if_t<classify_object<T>::value == object_category::string_constructible, detail::enabler> = detail::dummy>
0952 bool lexical_cast(const std::string &input, T &output) {
0953     output = T(input);
0954     return true;
0955 }
0956 
0957 /// Enumerations
0958 template <typename T,
0959           enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
0960 bool lexical_cast(const std::string &input, T &output) {
0961     typename std::underlying_type<T>::type val;
0962     if(!integral_conversion(input, val)) {
0963         return false;
0964     }
0965     output = static_cast<T>(val);
0966     return true;
0967 }
0968 
0969 /// wrapper types
0970 template <typename T,
0971           enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
0972                           std::is_assignable<T &, typename T::value_type>::value,
0973                       detail::enabler> = detail::dummy>
0974 bool lexical_cast(const std::string &input, T &output) {
0975     typename T::value_type val;
0976     if(lexical_cast(input, val)) {
0977         output = val;
0978         return true;
0979     }
0980     return from_stream(input, output);
0981 }
0982 
0983 template <typename T,
0984           enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
0985                           !std::is_assignable<T &, typename T::value_type>::value && std::is_assignable<T &, T>::value,
0986                       detail::enabler> = detail::dummy>
0987 bool lexical_cast(const std::string &input, T &output) {
0988     typename T::value_type val;
0989     if(lexical_cast(input, val)) {
0990         output = T{val};
0991         return true;
0992     }
0993     return from_stream(input, output);
0994 }
0995 
0996 /// Assignable from double or int
0997 template <
0998     typename T,
0999     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 
1015 /// Assignable from int
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 
1028 /// Assignable from double
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 
1041 /// Non-string convertible from an int
1042 template <typename T,
1043           enable_if_t<classify_object<T>::value == object_category::other && std::is_assignable<T &, int>::value,
1044                       detail::enabler> = detail::dummy>
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 
1067 /// Non-string parsable by a stream
1068 template <typename T,
1069           enable_if_t<classify_object<T>::value == object_category::other && !std::is_assignable<T &, int>::value,
1070                       detail::enabler> = detail::dummy>
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 
1078 /// Assign a value through lexical cast operations
1079 /// Strings can be empty so we need to do a little different
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),
1085                       detail::enabler> = detail::dummy>
1086 bool lexical_assign(const std::string &input, AssignTo &output) {
1087     return lexical_cast(input, output);
1088 }
1089 
1090 /// Assign a value through lexical cast operations
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,
1096                       detail::enabler> = detail::dummy>
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 
1106 /// Assign a value through lexical cast operations
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,
1111                       detail::enabler> = detail::dummy>
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 
1121 /// Assign a value through lexical cast operations for int compatible values
1122 /// mainly for atomic operations on some compilers
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,
1128                       detail::enabler> = detail::dummy>
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 
1142 /// Assign a value converted from a string in lexical cast to the output value directly
1143 template <typename AssignTo,
1144           typename ConvertTo,
1145           enable_if_t<!std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, ConvertTo &>::value,
1146                       detail::enabler> = detail::dummy>
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 
1156 /// Assign a value from a lexical cast through constructing a value and move assigning it
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,
1162                 detail::enabler> = detail::dummy>
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 
1172 /// primary lexical conversion operation, 1 string to 1 type of some kind
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,
1177                       detail::enabler> = detail::dummy>
1178 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1179     return lexical_assign<AssignTo, ConvertTo>(strings[0], output);
1180 }
1181 
1182 /// Lexical conversion if there is only one element but the conversion type is for two, then call a two element
1183 /// constructor
1184 template <typename AssignTo,
1185           typename ConvertTo,
1186           enable_if_t<(type_count<AssignTo>::value <= 2) && expected_count<AssignTo>::value == 1 &&
1187                           is_tuple_like<ConvertTo>::value && type_count_base<ConvertTo>::value == 2,
1188                       detail::enabler> = detail::dummy>
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 
1203 /// Lexical conversion of a container types of single elements
1204 template <class AssignTo,
1205           class ConvertTo,
1206           enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1207                           type_count<ConvertTo>::value == 1,
1208                       detail::enabler> = detail::dummy>
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 
1222 /// Lexical conversion for complex types
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 
1243 /// Conversion to a vector type using a particular single type as the conversion type
1244 template <class AssignTo,
1245           class ConvertTo,
1246           enable_if_t<is_mutable_container<AssignTo>::value && (expected_count<ConvertTo>::value == 1) &&
1247                           (type_count<ConvertTo>::value == 1),
1248                       detail::enabler> = detail::dummy>
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 
1263 /// Lexical conversion of a container types with conversion type of two elements
1264 template <class AssignTo,
1265           class ConvertTo,
1266           enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1267                           type_count_base<ConvertTo>::value == 2,
1268                       detail::enabler> = detail::dummy>
1269 bool lexical_conversion(std::vector<std::string> strings, AssignTo &output);
1270 
1271 /// Lexical conversion of a vector types with type_size >2 forward declaration
1272 template <class AssignTo,
1273           class ConvertTo,
1274           enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1275                           type_count_base<ConvertTo>::value != 2 &&
1276                           ((type_count<ConvertTo>::value > 2) ||
1277                            (type_count<ConvertTo>::value > type_count_base<ConvertTo>::value)),
1278                       detail::enabler> = detail::dummy>
1279 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output);
1280 
1281 /// Conversion for tuples
1282 template <class AssignTo,
1283           class ConvertTo,
1284           enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
1285                           (type_count_base<ConvertTo>::value != type_count<ConvertTo>::value ||
1286                            type_count<ConvertTo>::value > 2),
1287                       detail::enabler> = detail::dummy>
1288 bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output);  // forward declaration
1289 
1290 /// Conversion for operations where the assigned type is some class but the conversion is a mutable container or large
1291 /// tuple
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),
1297                       detail::enabler> = detail::dummy>
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 
1310 /// function template for converting tuples if the static Index is greater than the tuple size
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 
1317 /// Conversion of a tuple element where the type size ==1 and not a mutable container
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 
1326 /// Conversion of a tuple element where the type size !=1 but the size is fixed and not a mutable container
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 
1337 /// Conversion of a tuple element where the type is a mutable container or a type with different min and max type sizes
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 
1360 /// Tuple conversion operation
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 
1375 /// Lexical conversion of a container types with tuple elements of size 2
1376 template <class AssignTo,
1377           class ConvertTo,
1378           enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1379                           type_count_base<ConvertTo>::value == 2,
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 
1400 /// lexical conversion of tuples with type count>2 or tuples of types of some element with a type size>=2
1401 template <class AssignTo,
1402           class ConvertTo,
1403           enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
1404                           (type_count_base<ConvertTo>::value != type_count<ConvertTo>::value ||
1405                            type_count<ConvertTo>::value > 2),
1406                       detail::enabler>>
1407 bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
1408     static_assert(
1409         !is_tuple_like<ConvertTo>::value || type_count_base<AssignTo>::value == type_count_base<ConvertTo>::value,
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 
1414 /// Lexical conversion of a vector types for everything but tuples of two elements and types of size 1
1415 template <class AssignTo,
1416           class ConvertTo,
1417           enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
1418                           type_count_base<ConvertTo>::value != 2 &&
1419                           ((type_count<ConvertTo>::value > 2) ||
1420                            (type_count<ConvertTo>::value > type_count_base<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 
1452 /// conversion for wrapper types
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,
1457                       detail::enabler> = detail::dummy>
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 
1471 /// conversion for wrapper types
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,
1476                       detail::enabler> = detail::dummy>
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 
1491 /// Sum a vector of flag representations
1492 /// The flag vector produces a series of strings in a vector,  simple true is represented by a "1",  simple false is
1493 /// by
1494 /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most
1495 /// common true and false strings then uses stoll to convert the rest for summing
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 
1505 /// Sum a vector of flag representations
1506 /// The flag vector produces a series of strings in a vector,  simple true is represented by a "1",  simple false is
1507 /// by
1508 /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most
1509 /// common true and false strings then uses stoll to convert the rest for summing
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 
1526 /// Sum a vector of flag representations
1527 /// The flag vector produces a series of strings in a vector,  simple true is represented by a "1",  simple false is
1528 /// by
1529 /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most
1530 /// common true and false strings then uses stoll to convert the rest for summing
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