Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:45

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