Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:27:05

0001 /*
0002   __ _ _ __ __ _ _ __   __ _ _ __ ___  ___
0003  / _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++
0004 | (_| | | | (_| | |_) | (_| | |  \__ \  __/ http://github.com/p-ranav/argparse
0005  \__,_|_|  \__, | .__/ \__,_|_|  |___/\___|
0006            |___/|_|
0007 
0008 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
0009 SPDX-License-Identifier: MIT
0010 Copyright (c) 2019-2022 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
0011 and other contributors.
0012 
0013 Permission is hereby  granted, free of charge, to any  person obtaining a copy
0014 of this software and associated  documentation files (the "Software"), to deal
0015 in the Software  without restriction, including without  limitation the rights
0016 to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
0017 copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
0018 furnished to do so, subject to the following conditions:
0019 
0020 The above copyright notice and this permission notice shall be included in all
0021 copies or substantial portions of the Software.
0022 
0023 THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
0024 IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
0025 FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
0026 AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
0027 LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0028 OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
0029 SOFTWARE.
0030 */
0031 #pragma once
0032 
0033 #ifndef ARGPARSE_MODULE_USE_STD_MODULE
0034 #include <algorithm>
0035 #include <any>
0036 #include <array>
0037 #include <cerrno>
0038 #include <charconv>
0039 #include <cstdlib>
0040 #include <functional>
0041 #include <iomanip>
0042 #include <iostream>
0043 #include <iterator>
0044 #include <limits>
0045 #include <list>
0046 #include <map>
0047 #include <numeric>
0048 #include <optional>
0049 #include <sstream>
0050 #include <stdexcept>
0051 #include <string>
0052 #include <string_view>
0053 #include <tuple>
0054 #include <type_traits>
0055 #include <utility>
0056 #include <variant>
0057 #include <vector>
0058 #endif
0059 
0060 namespace argparse {
0061 
0062 namespace details { // namespace for helper methods
0063 
0064 template <typename T, typename = void>
0065 struct HasContainerTraits : std::false_type {};
0066 
0067 template <> struct HasContainerTraits<std::string> : std::false_type {};
0068 
0069 template <> struct HasContainerTraits<std::string_view> : std::false_type {};
0070 
0071 template <typename T>
0072 struct HasContainerTraits<
0073     T, std::void_t<typename T::value_type, decltype(std::declval<T>().begin()),
0074                    decltype(std::declval<T>().end()),
0075                    decltype(std::declval<T>().size())>> : std::true_type {};
0076 
0077 template <typename T>
0078 inline constexpr bool IsContainer = HasContainerTraits<T>::value;
0079 
0080 template <typename T, typename = void>
0081 struct HasStreamableTraits : std::false_type {};
0082 
0083 template <typename T>
0084 struct HasStreamableTraits<
0085     T,
0086     std::void_t<decltype(std::declval<std::ostream &>() << std::declval<T>())>>
0087     : std::true_type {};
0088 
0089 template <typename T>
0090 inline constexpr bool IsStreamable = HasStreamableTraits<T>::value;
0091 
0092 constexpr std::size_t repr_max_container_size = 5;
0093 
0094 template <typename T> std::string repr(T const &val) {
0095   if constexpr (std::is_same_v<T, bool>) {
0096     return val ? "true" : "false";
0097   } else if constexpr (std::is_convertible_v<T, std::string_view>) {
0098     return '"' + std::string{std::string_view{val}} + '"';
0099   } else if constexpr (IsContainer<T>) {
0100     std::stringstream out;
0101     out << "{";
0102     const auto size = val.size();
0103     if (size > 1) {
0104       out << repr(*val.begin());
0105       std::for_each(
0106           std::next(val.begin()),
0107           std::next(
0108               val.begin(),
0109               static_cast<typename T::iterator::difference_type>(
0110                   std::min<std::size_t>(size, repr_max_container_size) - 1)),
0111           [&out](const auto &v) { out << " " << repr(v); });
0112       if (size <= repr_max_container_size) {
0113         out << " ";
0114       } else {
0115         out << "...";
0116       }
0117     }
0118     if (size > 0) {
0119       out << repr(*std::prev(val.end()));
0120     }
0121     out << "}";
0122     return out.str();
0123   } else if constexpr (IsStreamable<T>) {
0124     std::stringstream out;
0125     out << val;
0126     return out.str();
0127   } else {
0128     return "<not representable>";
0129   }
0130 }
0131 
0132 namespace {
0133 
0134 template <typename T> constexpr bool standard_signed_integer = false;
0135 template <> constexpr bool standard_signed_integer<signed char> = true;
0136 template <> constexpr bool standard_signed_integer<short int> = true;
0137 template <> constexpr bool standard_signed_integer<int> = true;
0138 template <> constexpr bool standard_signed_integer<long int> = true;
0139 template <> constexpr bool standard_signed_integer<long long int> = true;
0140 
0141 template <typename T> constexpr bool standard_unsigned_integer = false;
0142 template <> constexpr bool standard_unsigned_integer<unsigned char> = true;
0143 template <> constexpr bool standard_unsigned_integer<unsigned short int> = true;
0144 template <> constexpr bool standard_unsigned_integer<unsigned int> = true;
0145 template <> constexpr bool standard_unsigned_integer<unsigned long int> = true;
0146 template <>
0147 constexpr bool standard_unsigned_integer<unsigned long long int> = true;
0148 
0149 } // namespace
0150 
0151 constexpr int radix_2 = 2;
0152 constexpr int radix_8 = 8;
0153 constexpr int radix_10 = 10;
0154 constexpr int radix_16 = 16;
0155 
0156 template <typename T>
0157 constexpr bool standard_integer =
0158     standard_signed_integer<T> || standard_unsigned_integer<T>;
0159 
0160 template <class F, class Tuple, class Extra, std::size_t... I>
0161 constexpr decltype(auto)
0162 apply_plus_one_impl(F &&f, Tuple &&t, Extra &&x,
0163                     std::index_sequence<I...> /*unused*/) {
0164   return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...,
0165                      std::forward<Extra>(x));
0166 }
0167 
0168 template <class F, class Tuple, class Extra>
0169 constexpr decltype(auto) apply_plus_one(F &&f, Tuple &&t, Extra &&x) {
0170   return details::apply_plus_one_impl(
0171       std::forward<F>(f), std::forward<Tuple>(t), std::forward<Extra>(x),
0172       std::make_index_sequence<
0173           std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
0174 }
0175 
0176 constexpr auto pointer_range(std::string_view s) noexcept {
0177   return std::tuple(s.data(), s.data() + s.size());
0178 }
0179 
0180 template <class CharT, class Traits>
0181 constexpr bool starts_with(std::basic_string_view<CharT, Traits> prefix,
0182                            std::basic_string_view<CharT, Traits> s) noexcept {
0183   return s.substr(0, prefix.size()) == prefix;
0184 }
0185 
0186 enum class chars_format {
0187   scientific = 0xf1,
0188   fixed = 0xf2,
0189   hex = 0xf4,
0190   binary = 0xf8,
0191   general = fixed | scientific
0192 };
0193 
0194 struct ConsumeBinaryPrefixResult {
0195   bool is_binary;
0196   std::string_view rest;
0197 };
0198 
0199 constexpr auto consume_binary_prefix(std::string_view s)
0200     -> ConsumeBinaryPrefixResult {
0201   if (starts_with(std::string_view{"0b"}, s) ||
0202       starts_with(std::string_view{"0B"}, s)) {
0203     s.remove_prefix(2);
0204     return {true, s};
0205   }
0206   return {false, s};
0207 }
0208 
0209 struct ConsumeHexPrefixResult {
0210   bool is_hexadecimal;
0211   std::string_view rest;
0212 };
0213 
0214 using namespace std::literals;
0215 
0216 constexpr auto consume_hex_prefix(std::string_view s)
0217     -> ConsumeHexPrefixResult {
0218   if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
0219     s.remove_prefix(2);
0220     return {true, s};
0221   }
0222   return {false, s};
0223 }
0224 
0225 template <class T, auto Param>
0226 inline auto do_from_chars(std::string_view s) -> T {
0227   T x;
0228   auto [first, last] = pointer_range(s);
0229   auto [ptr, ec] = std::from_chars(first, last, x, Param);
0230   if (ec == std::errc()) {
0231     if (ptr == last) {
0232       return x;
0233     }
0234     throw std::invalid_argument{"pattern '" + std::string(s) +
0235                                 "' does not match to the end"};
0236   }
0237   if (ec == std::errc::invalid_argument) {
0238     throw std::invalid_argument{"pattern '" + std::string(s) + "' not found"};
0239   }
0240   if (ec == std::errc::result_out_of_range) {
0241     throw std::range_error{"'" + std::string(s) + "' not representable"};
0242   }
0243   return x; // unreachable
0244 }
0245 
0246 template <class T, auto Param = 0> struct parse_number {
0247   auto operator()(std::string_view s) -> T {
0248     return do_from_chars<T, Param>(s);
0249   }
0250 };
0251 
0252 template <class T> struct parse_number<T, radix_2> {
0253   auto operator()(std::string_view s) -> T {
0254     if (auto [ok, rest] = consume_binary_prefix(s); ok) {
0255       return do_from_chars<T, radix_2>(rest);
0256     }
0257     throw std::invalid_argument{"pattern not found"};
0258   }
0259 };
0260 
0261 template <class T> struct parse_number<T, radix_16> {
0262   auto operator()(std::string_view s) -> T {
0263     if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
0264       if (auto [ok, rest] = consume_hex_prefix(s); ok) {
0265         try {
0266           return do_from_chars<T, radix_16>(rest);
0267         } catch (const std::invalid_argument &err) {
0268           throw std::invalid_argument("Failed to parse '" + std::string(s) +
0269                                       "' as hexadecimal: " + err.what());
0270         } catch (const std::range_error &err) {
0271           throw std::range_error("Failed to parse '" + std::string(s) +
0272                                  "' as hexadecimal: " + err.what());
0273         }
0274       }
0275     } else {
0276       // Allow passing hex numbers without prefix
0277       // Shape 'x' already has to be specified
0278       try {
0279         return do_from_chars<T, radix_16>(s);
0280       } catch (const std::invalid_argument &err) {
0281         throw std::invalid_argument("Failed to parse '" + std::string(s) +
0282                                     "' as hexadecimal: " + err.what());
0283       } catch (const std::range_error &err) {
0284         throw std::range_error("Failed to parse '" + std::string(s) +
0285                                "' as hexadecimal: " + err.what());
0286       }
0287     }
0288 
0289     throw std::invalid_argument{"pattern '" + std::string(s) +
0290                                 "' not identified as hexadecimal"};
0291   }
0292 };
0293 
0294 template <class T> struct parse_number<T> {
0295   auto operator()(std::string_view s) -> T {
0296     auto [ok, rest] = consume_hex_prefix(s);
0297     if (ok) {
0298       try {
0299         return do_from_chars<T, radix_16>(rest);
0300       } catch (const std::invalid_argument &err) {
0301         throw std::invalid_argument("Failed to parse '" + std::string(s) +
0302                                     "' as hexadecimal: " + err.what());
0303       } catch (const std::range_error &err) {
0304         throw std::range_error("Failed to parse '" + std::string(s) +
0305                                "' as hexadecimal: " + err.what());
0306       }
0307     }
0308 
0309     auto [ok_binary, rest_binary] = consume_binary_prefix(s);
0310     if (ok_binary) {
0311       try {
0312         return do_from_chars<T, radix_2>(rest_binary);
0313       } catch (const std::invalid_argument &err) {
0314         throw std::invalid_argument("Failed to parse '" + std::string(s) +
0315                                     "' as binary: " + err.what());
0316       } catch (const std::range_error &err) {
0317         throw std::range_error("Failed to parse '" + std::string(s) +
0318                                "' as binary: " + err.what());
0319       }
0320     }
0321 
0322     if (starts_with("0"sv, s)) {
0323       try {
0324         return do_from_chars<T, radix_8>(rest);
0325       } catch (const std::invalid_argument &err) {
0326         throw std::invalid_argument("Failed to parse '" + std::string(s) +
0327                                     "' as octal: " + err.what());
0328       } catch (const std::range_error &err) {
0329         throw std::range_error("Failed to parse '" + std::string(s) +
0330                                "' as octal: " + err.what());
0331       }
0332     }
0333 
0334     try {
0335       return do_from_chars<T, radix_10>(rest);
0336     } catch (const std::invalid_argument &err) {
0337       throw std::invalid_argument("Failed to parse '" + std::string(s) +
0338                                   "' as decimal integer: " + err.what());
0339     } catch (const std::range_error &err) {
0340       throw std::range_error("Failed to parse '" + std::string(s) +
0341                              "' as decimal integer: " + err.what());
0342     }
0343   }
0344 };
0345 
0346 namespace {
0347 
0348 template <class T> inline const auto generic_strtod = nullptr;
0349 template <> inline const auto generic_strtod<float> = strtof;
0350 template <> inline const auto generic_strtod<double> = strtod;
0351 template <> inline const auto generic_strtod<long double> = strtold;
0352 
0353 } // namespace
0354 
0355 template <class T> inline auto do_strtod(std::string const &s) -> T {
0356   if (isspace(static_cast<unsigned char>(s[0])) || s[0] == '+') {
0357     throw std::invalid_argument{"pattern '" + s + "' not found"};
0358   }
0359 
0360   auto [first, last] = pointer_range(s);
0361   char *ptr;
0362 
0363   errno = 0;
0364   auto x = generic_strtod<T>(first, &ptr);
0365   if (errno == 0) {
0366     if (ptr == last) {
0367       return x;
0368     }
0369     throw std::invalid_argument{"pattern '" + s +
0370                                 "' does not match to the end"};
0371   }
0372   if (errno == ERANGE) {
0373     throw std::range_error{"'" + s + "' not representable"};
0374   }
0375   return x; // unreachable
0376 }
0377 
0378 template <class T> struct parse_number<T, chars_format::general> {
0379   auto operator()(std::string const &s) -> T {
0380     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
0381       throw std::invalid_argument{
0382           "chars_format::general does not parse hexfloat"};
0383     }
0384     if (auto r = consume_binary_prefix(s); r.is_binary) {
0385       throw std::invalid_argument{
0386           "chars_format::general does not parse binfloat"};
0387     }
0388 
0389     try {
0390       return do_strtod<T>(s);
0391     } catch (const std::invalid_argument &err) {
0392       throw std::invalid_argument("Failed to parse '" + s +
0393                                   "' as number: " + err.what());
0394     } catch (const std::range_error &err) {
0395       throw std::range_error("Failed to parse '" + s +
0396                              "' as number: " + err.what());
0397     }
0398   }
0399 };
0400 
0401 template <class T> struct parse_number<T, chars_format::hex> {
0402   auto operator()(std::string const &s) -> T {
0403     if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) {
0404       throw std::invalid_argument{"chars_format::hex parses hexfloat"};
0405     }
0406     if (auto r = consume_binary_prefix(s); r.is_binary) {
0407       throw std::invalid_argument{"chars_format::hex does not parse binfloat"};
0408     }
0409 
0410     try {
0411       return do_strtod<T>(s);
0412     } catch (const std::invalid_argument &err) {
0413       throw std::invalid_argument("Failed to parse '" + s +
0414                                   "' as hexadecimal: " + err.what());
0415     } catch (const std::range_error &err) {
0416       throw std::range_error("Failed to parse '" + s +
0417                              "' as hexadecimal: " + err.what());
0418     }
0419   }
0420 };
0421 
0422 template <class T> struct parse_number<T, chars_format::binary> {
0423   auto operator()(std::string const &s) -> T {
0424     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
0425       throw std::invalid_argument{
0426           "chars_format::binary does not parse hexfloat"};
0427     }
0428     if (auto r = consume_binary_prefix(s); !r.is_binary) {
0429       throw std::invalid_argument{"chars_format::binary parses binfloat"};
0430     }
0431 
0432     return do_strtod<T>(s);
0433   }
0434 };
0435 
0436 template <class T> struct parse_number<T, chars_format::scientific> {
0437   auto operator()(std::string const &s) -> T {
0438     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
0439       throw std::invalid_argument{
0440           "chars_format::scientific does not parse hexfloat"};
0441     }
0442     if (auto r = consume_binary_prefix(s); r.is_binary) {
0443       throw std::invalid_argument{
0444           "chars_format::scientific does not parse binfloat"};
0445     }
0446     if (s.find_first_of("eE") == std::string::npos) {
0447       throw std::invalid_argument{
0448           "chars_format::scientific requires exponent part"};
0449     }
0450 
0451     try {
0452       return do_strtod<T>(s);
0453     } catch (const std::invalid_argument &err) {
0454       throw std::invalid_argument("Failed to parse '" + s +
0455                                   "' as scientific notation: " + err.what());
0456     } catch (const std::range_error &err) {
0457       throw std::range_error("Failed to parse '" + s +
0458                              "' as scientific notation: " + err.what());
0459     }
0460   }
0461 };
0462 
0463 template <class T> struct parse_number<T, chars_format::fixed> {
0464   auto operator()(std::string const &s) -> T {
0465     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
0466       throw std::invalid_argument{
0467           "chars_format::fixed does not parse hexfloat"};
0468     }
0469     if (auto r = consume_binary_prefix(s); r.is_binary) {
0470       throw std::invalid_argument{
0471           "chars_format::fixed does not parse binfloat"};
0472     }
0473     if (s.find_first_of("eE") != std::string::npos) {
0474       throw std::invalid_argument{
0475           "chars_format::fixed does not parse exponent part"};
0476     }
0477 
0478     try {
0479       return do_strtod<T>(s);
0480     } catch (const std::invalid_argument &err) {
0481       throw std::invalid_argument("Failed to parse '" + s +
0482                                   "' as fixed notation: " + err.what());
0483     } catch (const std::range_error &err) {
0484       throw std::range_error("Failed to parse '" + s +
0485                              "' as fixed notation: " + err.what());
0486     }
0487   }
0488 };
0489 
0490 template <typename StrIt>
0491 std::string join(StrIt first, StrIt last, const std::string &separator) {
0492   if (first == last) {
0493     return "";
0494   }
0495   std::stringstream value;
0496   value << *first;
0497   ++first;
0498   while (first != last) {
0499     value << separator << *first;
0500     ++first;
0501   }
0502   return value.str();
0503 }
0504 
0505 template <typename T> struct can_invoke_to_string {
0506   template <typename U>
0507   static auto test(int)
0508       -> decltype(std::to_string(std::declval<U>()), std::true_type{});
0509 
0510   template <typename U> static auto test(...) -> std::false_type;
0511 
0512   static constexpr bool value = decltype(test<T>(0))::value;
0513 };
0514 
0515 template <typename T> struct IsChoiceTypeSupported {
0516   using CleanType = typename std::decay<T>::type;
0517   static const bool value = std::is_integral<CleanType>::value ||
0518                             std::is_same<CleanType, std::string>::value ||
0519                             std::is_same<CleanType, std::string_view>::value ||
0520                             std::is_same<CleanType, const char *>::value;
0521 };
0522 
0523 template <typename StringType>
0524 std::size_t get_levenshtein_distance(const StringType &s1,
0525                                      const StringType &s2) {
0526   std::vector<std::vector<std::size_t>> dp(
0527       s1.size() + 1, std::vector<std::size_t>(s2.size() + 1, 0));
0528 
0529   for (std::size_t i = 0; i <= s1.size(); ++i) {
0530     for (std::size_t j = 0; j <= s2.size(); ++j) {
0531       if (i == 0) {
0532         dp[i][j] = j;
0533       } else if (j == 0) {
0534         dp[i][j] = i;
0535       } else if (s1[i - 1] == s2[j - 1]) {
0536         dp[i][j] = dp[i - 1][j - 1];
0537       } else {
0538         dp[i][j] = 1 + std::min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
0539       }
0540     }
0541   }
0542 
0543   return dp[s1.size()][s2.size()];
0544 }
0545 
0546 template <typename ValueType>
0547 std::string_view
0548 get_most_similar_string(const std::map<std::string_view, ValueType> &map,
0549                         const std::string_view input) {
0550   std::string_view most_similar{};
0551   std::size_t min_distance = std::numeric_limits<std::size_t>::max();
0552 
0553   for (const auto &entry : map) {
0554     std::size_t distance = get_levenshtein_distance(entry.first, input);
0555     if (distance < min_distance) {
0556       min_distance = distance;
0557       most_similar = entry.first;
0558     }
0559   }
0560 
0561   return most_similar;
0562 }
0563 
0564 } // namespace details
0565 
0566 enum class nargs_pattern { optional, any, at_least_one };
0567 
0568 enum class default_arguments : unsigned int {
0569   none = 0,
0570   help = 1,
0571   version = 2,
0572   all = help | version,
0573 };
0574 
0575 inline default_arguments operator&(const default_arguments &a,
0576                                    const default_arguments &b) {
0577   return static_cast<default_arguments>(
0578       static_cast<std::underlying_type<default_arguments>::type>(a) &
0579       static_cast<std::underlying_type<default_arguments>::type>(b));
0580 }
0581 
0582 class ArgumentParser;
0583 
0584 class Argument {
0585   friend class ArgumentParser;
0586   friend auto operator<<(std::ostream &stream, const ArgumentParser &parser)
0587       -> std::ostream &;
0588 
0589   template <std::size_t N, std::size_t... I>
0590   explicit Argument(std::string_view prefix_chars,
0591                     std::array<std::string_view, N> &&a,
0592                     std::index_sequence<I...> /*unused*/)
0593       : m_accepts_optional_like_value(false),
0594         m_is_optional((is_optional(a[I], prefix_chars) || ...)),
0595         m_is_required(false), m_is_repeatable(false), m_is_used(false),
0596         m_prefix_chars(prefix_chars) {
0597     ((void)m_names.emplace_back(a[I]), ...);
0598     std::sort(
0599         m_names.begin(), m_names.end(), [](const auto &lhs, const auto &rhs) {
0600           return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size();
0601         });
0602   }
0603 
0604 public:
0605   template <std::size_t N>
0606   explicit Argument(std::string_view prefix_chars,
0607                     std::array<std::string_view, N> &&a)
0608       : Argument(prefix_chars, std::move(a), std::make_index_sequence<N>{}) {}
0609 
0610   Argument &help(std::string help_text) {
0611     m_help = std::move(help_text);
0612     return *this;
0613   }
0614 
0615   Argument &metavar(std::string metavar) {
0616     m_metavar = std::move(metavar);
0617     return *this;
0618   }
0619 
0620   template <typename T> Argument &default_value(T &&value) {
0621     m_num_args_range = NArgsRange{0, m_num_args_range.get_max()};
0622     m_default_value_repr = details::repr(value);
0623 
0624     if constexpr (std::is_convertible_v<T, std::string_view>) {
0625       m_default_value_str = std::string{std::string_view{value}};
0626     } else if constexpr (details::can_invoke_to_string<T>::value) {
0627       m_default_value_str = std::to_string(value);
0628     }
0629 
0630     m_default_value = std::forward<T>(value);
0631     return *this;
0632   }
0633 
0634   Argument &default_value(const char *value) {
0635     return default_value(std::string(value));
0636   }
0637 
0638   Argument &required() {
0639     m_is_required = true;
0640     return *this;
0641   }
0642 
0643   Argument &implicit_value(std::any value) {
0644     m_implicit_value = std::move(value);
0645     m_num_args_range = NArgsRange{0, 0};
0646     return *this;
0647   }
0648 
0649   // This is shorthand for:
0650   //   program.add_argument("foo")
0651   //     .default_value(false)
0652   //     .implicit_value(true)
0653   Argument &flag() {
0654     default_value(false);
0655     implicit_value(true);
0656     return *this;
0657   }
0658 
0659   template <class F, class... Args>
0660   auto action(F &&callable, Args &&... bound_args)
0661       -> std::enable_if_t<std::is_invocable_v<F, Args..., std::string const>,
0662                           Argument &> {
0663     using action_type = std::conditional_t<
0664         std::is_void_v<std::invoke_result_t<F, Args..., std::string const>>,
0665         void_action, valued_action>;
0666     if constexpr (sizeof...(Args) == 0) {
0667       m_action.emplace<action_type>(std::forward<F>(callable));
0668     } else {
0669       m_action.emplace<action_type>(
0670           [f = std::forward<F>(callable),
0671            tup = std::make_tuple(std::forward<Args>(bound_args)...)](
0672               std::string const &opt) mutable {
0673             return details::apply_plus_one(f, tup, opt);
0674           });
0675     }
0676     return *this;
0677   }
0678 
0679   auto &append() {
0680     m_is_repeatable = true;
0681     return *this;
0682   }
0683 
0684   template <char Shape, typename T>
0685   auto scan() -> std::enable_if_t<std::is_arithmetic_v<T>, Argument &> {
0686     static_assert(!(std::is_const_v<T> || std::is_volatile_v<T>),
0687                   "T should not be cv-qualified");
0688     auto is_one_of = [](char c, auto... x) constexpr {
0689       return ((c == x) || ...);
0690     };
0691 
0692     if constexpr (is_one_of(Shape, 'd') && details::standard_integer<T>) {
0693       action(details::parse_number<T, details::radix_10>());
0694     } else if constexpr (is_one_of(Shape, 'i') &&
0695                          details::standard_integer<T>) {
0696       action(details::parse_number<T>());
0697     } else if constexpr (is_one_of(Shape, 'u') &&
0698                          details::standard_unsigned_integer<T>) {
0699       action(details::parse_number<T, details::radix_10>());
0700     } else if constexpr (is_one_of(Shape, 'b') &&
0701                          details::standard_unsigned_integer<T>) {
0702       action(details::parse_number<T, details::radix_2>());
0703     } else if constexpr (is_one_of(Shape, 'o') &&
0704                          details::standard_unsigned_integer<T>) {
0705       action(details::parse_number<T, details::radix_8>());
0706     } else if constexpr (is_one_of(Shape, 'x', 'X') &&
0707                          details::standard_unsigned_integer<T>) {
0708       action(details::parse_number<T, details::radix_16>());
0709     } else if constexpr (is_one_of(Shape, 'a', 'A') &&
0710                          std::is_floating_point_v<T>) {
0711       action(details::parse_number<T, details::chars_format::hex>());
0712     } else if constexpr (is_one_of(Shape, 'e', 'E') &&
0713                          std::is_floating_point_v<T>) {
0714       action(details::parse_number<T, details::chars_format::scientific>());
0715     } else if constexpr (is_one_of(Shape, 'f', 'F') &&
0716                          std::is_floating_point_v<T>) {
0717       action(details::parse_number<T, details::chars_format::fixed>());
0718     } else if constexpr (is_one_of(Shape, 'g', 'G') &&
0719                          std::is_floating_point_v<T>) {
0720       action(details::parse_number<T, details::chars_format::general>());
0721     } else {
0722       static_assert(alignof(T) == 0, "No scan specification for T");
0723     }
0724 
0725     return *this;
0726   }
0727 
0728   Argument &nargs(std::size_t num_args) {
0729     m_num_args_range = NArgsRange{num_args, num_args};
0730     return *this;
0731   }
0732 
0733   Argument &nargs(std::size_t num_args_min, std::size_t num_args_max) {
0734     m_num_args_range = NArgsRange{num_args_min, num_args_max};
0735     return *this;
0736   }
0737 
0738   Argument &nargs(nargs_pattern pattern) {
0739     switch (pattern) {
0740     case nargs_pattern::optional:
0741       m_num_args_range = NArgsRange{0, 1};
0742       break;
0743     case nargs_pattern::any:
0744       m_num_args_range =
0745           NArgsRange{0, (std::numeric_limits<std::size_t>::max)()};
0746       break;
0747     case nargs_pattern::at_least_one:
0748       m_num_args_range =
0749           NArgsRange{1, (std::numeric_limits<std::size_t>::max)()};
0750       break;
0751     }
0752     return *this;
0753   }
0754 
0755   Argument &remaining() {
0756     m_accepts_optional_like_value = true;
0757     return nargs(nargs_pattern::any);
0758   }
0759 
0760   template <typename T> void add_choice(T &&choice) {
0761     static_assert(details::IsChoiceTypeSupported<T>::value,
0762                   "Only string or integer type supported for choice");
0763     static_assert(std::is_convertible_v<T, std::string_view> ||
0764                       details::can_invoke_to_string<T>::value,
0765                   "Choice is not convertible to string_type");
0766     if (!m_choices.has_value()) {
0767       m_choices = std::vector<std::string>{};
0768     }
0769 
0770     if constexpr (std::is_convertible_v<T, std::string_view>) {
0771       m_choices.value().push_back(
0772           std::string{std::string_view{std::forward<T>(choice)}});
0773     } else if constexpr (details::can_invoke_to_string<T>::value) {
0774       m_choices.value().push_back(std::to_string(std::forward<T>(choice)));
0775     }
0776   }
0777 
0778   Argument &choices() {
0779     if (!m_choices.has_value()) {
0780       throw std::runtime_error("Zero choices provided");
0781     }
0782     return *this;
0783   }
0784 
0785   template <typename T, typename... U>
0786   Argument &choices(T &&first, U &&... rest) {
0787     add_choice(std::forward<T>(first));
0788     choices(std::forward<U>(rest)...);
0789     return *this;
0790   }
0791 
0792   void find_default_value_in_choices_or_throw() const {
0793 
0794     const auto &choices = m_choices.value();
0795 
0796     if (m_default_value.has_value()) {
0797       if (std::find(choices.begin(), choices.end(), m_default_value_str) ==
0798           choices.end()) {
0799         // provided arg not in list of allowed choices
0800         // report error
0801 
0802         std::string choices_as_csv =
0803             std::accumulate(choices.begin(), choices.end(), std::string(),
0804                             [](const std::string &a, const std::string &b) {
0805                               return a + (a.empty() ? "" : ", ") + b;
0806                             });
0807 
0808         throw std::runtime_error(
0809             std::string{"Invalid default value "} + m_default_value_repr +
0810             " - allowed options: {" + choices_as_csv + "}");
0811       }
0812     }
0813   }
0814 
0815   template <typename Iterator>
0816   void find_value_in_choices_or_throw(Iterator it) const {
0817 
0818     const auto &choices = m_choices.value();
0819 
0820     if (std::find(choices.begin(), choices.end(), *it) == choices.end()) {
0821       // provided arg not in list of allowed choices
0822       // report error
0823 
0824       std::string choices_as_csv =
0825           std::accumulate(choices.begin(), choices.end(), std::string(),
0826                           [](const std::string &a, const std::string &b) {
0827                             return a + (a.empty() ? "" : ", ") + b;
0828                           });
0829 
0830       throw std::runtime_error(std::string{"Invalid argument "} +
0831                                details::repr(*it) + " - allowed options: {" +
0832                                choices_as_csv + "}");
0833     }
0834   }
0835 
0836   template <typename Iterator>
0837   Iterator consume(Iterator start, Iterator end,
0838                    std::string_view used_name = {}) {
0839     if (!m_is_repeatable && m_is_used) {
0840       throw std::runtime_error("Duplicate argument");
0841     }
0842     m_is_used = true;
0843     m_used_name = used_name;
0844 
0845     if (m_choices.has_value()) {
0846       // Check each value in (start, end) and make sure
0847       // it is in the list of allowed choices/options
0848       std::size_t i = 0;
0849       auto max_number_of_args = m_num_args_range.get_max();
0850       for (auto it = start; it != end; ++it) {
0851         if (i == max_number_of_args) {
0852           break;
0853         }
0854         find_value_in_choices_or_throw(it);
0855         i += 1;
0856       }
0857     }
0858 
0859     const auto num_args_max = m_num_args_range.get_max();
0860     const auto num_args_min = m_num_args_range.get_min();
0861     std::size_t dist = 0;
0862     if (num_args_max == 0) {
0863       m_values.emplace_back(m_implicit_value);
0864       std::visit([](const auto &f) { f({}); }, m_action);
0865       return start;
0866     }
0867     if ((dist = static_cast<std::size_t>(std::distance(start, end))) >=
0868         num_args_min) {
0869       if (num_args_max < dist) {
0870         end = std::next(start, static_cast<typename Iterator::difference_type>(
0871                                    num_args_max));
0872       }
0873       if (!m_accepts_optional_like_value) {
0874         end = std::find_if(
0875             start, end,
0876             std::bind(is_optional, std::placeholders::_1, m_prefix_chars));
0877         dist = static_cast<std::size_t>(std::distance(start, end));
0878         if (dist < num_args_min) {
0879           throw std::runtime_error("Too few arguments");
0880         }
0881       }
0882 
0883       struct ActionApply {
0884         void operator()(valued_action &f) {
0885           std::transform(first, last, std::back_inserter(self.m_values), f);
0886         }
0887 
0888         void operator()(void_action &f) {
0889           std::for_each(first, last, f);
0890           if (!self.m_default_value.has_value()) {
0891             if (!self.m_accepts_optional_like_value) {
0892               self.m_values.resize(
0893                   static_cast<std::size_t>(std::distance(first, last)));
0894             }
0895           }
0896         }
0897 
0898         Iterator first, last;
0899         Argument &self;
0900       };
0901       std::visit(ActionApply{start, end, *this}, m_action);
0902       return end;
0903     }
0904     if (m_default_value.has_value()) {
0905       return start;
0906     }
0907     throw std::runtime_error("Too few arguments for '" +
0908                              std::string(m_used_name) + "'.");
0909   }
0910 
0911   /*
0912    * @throws std::runtime_error if argument values are not valid
0913    */
0914   void validate() const {
0915     if (m_is_optional) {
0916       // TODO: check if an implicit value was programmed for this argument
0917       if (!m_is_used && !m_default_value.has_value() && m_is_required) {
0918         throw_required_arg_not_used_error();
0919       }
0920       if (m_is_used && m_is_required && m_values.empty()) {
0921         throw_required_arg_no_value_provided_error();
0922       }
0923     } else {
0924       if (!m_num_args_range.contains(m_values.size()) &&
0925           !m_default_value.has_value()) {
0926         throw_nargs_range_validation_error();
0927       }
0928     }
0929 
0930     if (m_choices.has_value()) {
0931       // Make sure the default value (if provided)
0932       // is in the list of choices
0933       find_default_value_in_choices_or_throw();
0934     }
0935   }
0936 
0937   std::string get_names_csv(char separator = ',') const {
0938     return std::accumulate(
0939         m_names.begin(), m_names.end(), std::string{""},
0940         [&](const std::string &result, const std::string &name) {
0941           return result.empty() ? name : result + separator + name;
0942         });
0943   }
0944 
0945   std::string get_usage_full() const {
0946     std::stringstream usage;
0947 
0948     usage << get_names_csv('/');
0949     const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
0950     if (m_num_args_range.get_max() > 0) {
0951       usage << " " << metavar;
0952       if (m_num_args_range.get_max() > 1) {
0953         usage << "...";
0954       }
0955     }
0956     return usage.str();
0957   }
0958 
0959   std::string get_inline_usage() const {
0960     std::stringstream usage;
0961     // Find the longest variant to show in the usage string
0962     std::string longest_name = m_names.front();
0963     for (const auto &s : m_names) {
0964       if (s.size() > longest_name.size()) {
0965         longest_name = s;
0966       }
0967     }
0968     if (!m_is_required) {
0969       usage << "[";
0970     }
0971     usage << longest_name;
0972     const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
0973     if (m_num_args_range.get_max() > 0) {
0974       usage << " " << metavar;
0975       if (m_num_args_range.get_max() > 1) {
0976         usage << "...";
0977       }
0978     }
0979     if (!m_is_required) {
0980       usage << "]";
0981     }
0982     return usage.str();
0983   }
0984 
0985   std::size_t get_arguments_length() const {
0986 
0987     std::size_t names_size = std::accumulate(
0988         std::begin(m_names), std::end(m_names), std::size_t(0),
0989         [](const auto &sum, const auto &s) { return sum + s.size(); });
0990 
0991     if (is_positional(m_names.front(), m_prefix_chars)) {
0992       // A set metavar means this replaces the names
0993       if (!m_metavar.empty()) {
0994         // Indent and metavar
0995         return 2 + m_metavar.size();
0996       }
0997 
0998       // Indent and space-separated
0999       return 2 + names_size + (m_names.size() - 1);
1000     }
1001     // Is an option - include both names _and_ metavar
1002     // size = text + (", " between names)
1003     std::size_t size = names_size + 2 * (m_names.size() - 1);
1004     if (!m_metavar.empty() && m_num_args_range == NArgsRange{1, 1}) {
1005       size += m_metavar.size() + 1;
1006     }
1007     return size + 2; // indent
1008   }
1009 
1010   friend std::ostream &operator<<(std::ostream &stream,
1011                                   const Argument &argument) {
1012     std::stringstream name_stream;
1013     name_stream << "  "; // indent
1014     if (argument.is_positional(argument.m_names.front(),
1015                                argument.m_prefix_chars)) {
1016       if (!argument.m_metavar.empty()) {
1017         name_stream << argument.m_metavar;
1018       } else {
1019         name_stream << details::join(argument.m_names.begin(),
1020                                      argument.m_names.end(), " ");
1021       }
1022     } else {
1023       name_stream << details::join(argument.m_names.begin(),
1024                                    argument.m_names.end(), ", ");
1025       // If we have a metavar, and one narg - print the metavar
1026       if (!argument.m_metavar.empty() &&
1027           argument.m_num_args_range == NArgsRange{1, 1}) {
1028         name_stream << " " << argument.m_metavar;
1029       }
1030     }
1031 
1032     // align multiline help message
1033     auto stream_width = stream.width();
1034     auto name_padding = std::string(name_stream.str().size(), ' ');
1035     auto pos = std::string::size_type{};
1036     auto prev = std::string::size_type{};
1037     auto first_line = true;
1038     auto hspace = "  "; // minimal space between name and help message
1039     stream << name_stream.str();
1040     std::string_view help_view(argument.m_help);
1041     while ((pos = argument.m_help.find('\n', prev)) != std::string::npos) {
1042       auto line = help_view.substr(prev, pos - prev + 1);
1043       if (first_line) {
1044         stream << hspace << line;
1045         first_line = false;
1046       } else {
1047         stream.width(stream_width);
1048         stream << name_padding << hspace << line;
1049       }
1050       prev += pos - prev + 1;
1051     }
1052     if (first_line) {
1053       stream << hspace << argument.m_help;
1054     } else {
1055       auto leftover = help_view.substr(prev, argument.m_help.size() - prev);
1056       if (!leftover.empty()) {
1057         stream.width(stream_width);
1058         stream << name_padding << hspace << leftover;
1059       }
1060     }
1061 
1062     // print nargs spec
1063     if (!argument.m_help.empty()) {
1064       stream << " ";
1065     }
1066     stream << argument.m_num_args_range;
1067 
1068     if (argument.m_default_value.has_value() &&
1069         argument.m_num_args_range != NArgsRange{0, 0}) {
1070       stream << "[default: " << argument.m_default_value_repr << "]";
1071     } else if (argument.m_is_required) {
1072       stream << "[required]";
1073     }
1074     stream << "\n";
1075     return stream;
1076   }
1077 
1078   template <typename T> bool operator!=(const T &rhs) const {
1079     return !(*this == rhs);
1080   }
1081 
1082   /*
1083    * Compare to an argument value of known type
1084    * @throws std::logic_error in case of incompatible types
1085    */
1086   template <typename T> bool operator==(const T &rhs) const {
1087     if constexpr (!details::IsContainer<T>) {
1088       return get<T>() == rhs;
1089     } else {
1090       using ValueType = typename T::value_type;
1091       auto lhs = get<T>();
1092       return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
1093                         std::end(rhs), [](const auto &a, const auto &b) {
1094                           return std::any_cast<const ValueType &>(a) == b;
1095                         });
1096     }
1097   }
1098 
1099 private:
1100   class NArgsRange {
1101     std::size_t m_min;
1102     std::size_t m_max;
1103 
1104   public:
1105     NArgsRange(std::size_t minimum, std::size_t maximum)
1106         : m_min(minimum), m_max(maximum) {
1107       if (minimum > maximum) {
1108         throw std::logic_error("Range of number of arguments is invalid");
1109       }
1110     }
1111 
1112     bool contains(std::size_t value) const {
1113       return value >= m_min && value <= m_max;
1114     }
1115 
1116     bool is_exact() const { return m_min == m_max; }
1117 
1118     bool is_right_bounded() const {
1119       return m_max < (std::numeric_limits<std::size_t>::max)();
1120     }
1121 
1122     std::size_t get_min() const { return m_min; }
1123 
1124     std::size_t get_max() const { return m_max; }
1125 
1126     // Print help message
1127     friend auto operator<<(std::ostream &stream, const NArgsRange &range)
1128         -> std::ostream & {
1129       if (range.m_min == range.m_max) {
1130         if (range.m_min != 0 && range.m_min != 1) {
1131           stream << "[nargs: " << range.m_min << "] ";
1132         }
1133       } else {
1134         if (range.m_max == (std::numeric_limits<std::size_t>::max)()) {
1135           stream << "[nargs: " << range.m_min << " or more] ";
1136         } else {
1137           stream << "[nargs=" << range.m_min << ".." << range.m_max << "] ";
1138         }
1139       }
1140       return stream;
1141     }
1142 
1143     bool operator==(const NArgsRange &rhs) const {
1144       return rhs.m_min == m_min && rhs.m_max == m_max;
1145     }
1146 
1147     bool operator!=(const NArgsRange &rhs) const { return !(*this == rhs); }
1148   };
1149 
1150   void throw_nargs_range_validation_error() const {
1151     std::stringstream stream;
1152     if (!m_used_name.empty()) {
1153       stream << m_used_name << ": ";
1154     } else {
1155       stream << m_names.front() << ": ";
1156     }
1157     if (m_num_args_range.is_exact()) {
1158       stream << m_num_args_range.get_min();
1159     } else if (m_num_args_range.is_right_bounded()) {
1160       stream << m_num_args_range.get_min() << " to "
1161              << m_num_args_range.get_max();
1162     } else {
1163       stream << m_num_args_range.get_min() << " or more";
1164     }
1165     stream << " argument(s) expected. " << m_values.size() << " provided.";
1166     throw std::runtime_error(stream.str());
1167   }
1168 
1169   void throw_required_arg_not_used_error() const {
1170     std::stringstream stream;
1171     stream << m_names.front() << ": required.";
1172     throw std::runtime_error(stream.str());
1173   }
1174 
1175   void throw_required_arg_no_value_provided_error() const {
1176     std::stringstream stream;
1177     stream << m_used_name << ": no value provided.";
1178     throw std::runtime_error(stream.str());
1179   }
1180 
1181   static constexpr int eof = std::char_traits<char>::eof();
1182 
1183   static auto lookahead(std::string_view s) -> int {
1184     if (s.empty()) {
1185       return eof;
1186     }
1187     return static_cast<int>(static_cast<unsigned char>(s[0]));
1188   }
1189 
1190   /*
1191    * decimal-literal:
1192    *    '0'
1193    *    nonzero-digit digit-sequence_opt
1194    *    integer-part fractional-part
1195    *    fractional-part
1196    *    integer-part '.' exponent-part_opt
1197    *    integer-part exponent-part
1198    *
1199    * integer-part:
1200    *    digit-sequence
1201    *
1202    * fractional-part:
1203    *    '.' post-decimal-point
1204    *
1205    * post-decimal-point:
1206    *    digit-sequence exponent-part_opt
1207    *
1208    * exponent-part:
1209    *    'e' post-e
1210    *    'E' post-e
1211    *
1212    * post-e:
1213    *    sign_opt digit-sequence
1214    *
1215    * sign: one of
1216    *    '+' '-'
1217    */
1218   static bool is_decimal_literal(std::string_view s) {
1219     auto is_digit = [](auto c) constexpr {
1220       switch (c) {
1221       case '0':
1222       case '1':
1223       case '2':
1224       case '3':
1225       case '4':
1226       case '5':
1227       case '6':
1228       case '7':
1229       case '8':
1230       case '9':
1231         return true;
1232       default:
1233         return false;
1234       }
1235     };
1236 
1237     // precondition: we have consumed or will consume at least one digit
1238     auto consume_digits = [=](std::string_view sd) {
1239       // NOLINTNEXTLINE(readability-qualified-auto)
1240       auto it = std::find_if_not(std::begin(sd), std::end(sd), is_digit);
1241       return sd.substr(static_cast<std::size_t>(it - std::begin(sd)));
1242     };
1243 
1244     switch (lookahead(s)) {
1245     case '0': {
1246       s.remove_prefix(1);
1247       if (s.empty()) {
1248         return true;
1249       }
1250       goto integer_part;
1251     }
1252     case '1':
1253     case '2':
1254     case '3':
1255     case '4':
1256     case '5':
1257     case '6':
1258     case '7':
1259     case '8':
1260     case '9': {
1261       s = consume_digits(s);
1262       if (s.empty()) {
1263         return true;
1264       }
1265       goto integer_part_consumed;
1266     }
1267     case '.': {
1268       s.remove_prefix(1);
1269       goto post_decimal_point;
1270     }
1271     default:
1272       return false;
1273     }
1274 
1275   integer_part:
1276     s = consume_digits(s);
1277   integer_part_consumed:
1278     switch (lookahead(s)) {
1279     case '.': {
1280       s.remove_prefix(1);
1281       if (is_digit(lookahead(s))) {
1282         goto post_decimal_point;
1283       } else {
1284         goto exponent_part_opt;
1285       }
1286     }
1287     case 'e':
1288     case 'E': {
1289       s.remove_prefix(1);
1290       goto post_e;
1291     }
1292     default:
1293       return false;
1294     }
1295 
1296   post_decimal_point:
1297     if (is_digit(lookahead(s))) {
1298       s = consume_digits(s);
1299       goto exponent_part_opt;
1300     }
1301     return false;
1302 
1303   exponent_part_opt:
1304     switch (lookahead(s)) {
1305     case eof:
1306       return true;
1307     case 'e':
1308     case 'E': {
1309       s.remove_prefix(1);
1310       goto post_e;
1311     }
1312     default:
1313       return false;
1314     }
1315 
1316   post_e:
1317     switch (lookahead(s)) {
1318     case '-':
1319     case '+':
1320       s.remove_prefix(1);
1321     }
1322     if (is_digit(lookahead(s))) {
1323       s = consume_digits(s);
1324       return s.empty();
1325     }
1326     return false;
1327   }
1328 
1329   static bool is_optional(std::string_view name,
1330                           std::string_view prefix_chars) {
1331     return !is_positional(name, prefix_chars);
1332   }
1333 
1334   /*
1335    * positional:
1336    *    _empty_
1337    *    '-'
1338    *    '-' decimal-literal
1339    *    !'-' anything
1340    */
1341   static bool is_positional(std::string_view name,
1342                             std::string_view prefix_chars) {
1343     auto first = lookahead(name);
1344 
1345     if (first == eof) {
1346       return true;
1347     } else if (prefix_chars.find(static_cast<char>(first)) !=
1348                std::string_view::npos) {
1349       name.remove_prefix(1);
1350       if (name.empty()) {
1351         return true;
1352       }
1353       return is_decimal_literal(name);
1354     }
1355     return true;
1356   }
1357 
1358   /*
1359    * Get argument value given a type
1360    * @throws std::logic_error in case of incompatible types
1361    */
1362   template <typename T> T get() const {
1363     if (!m_values.empty()) {
1364       if constexpr (details::IsContainer<T>) {
1365         return any_cast_container<T>(m_values);
1366       } else {
1367         return std::any_cast<T>(m_values.front());
1368       }
1369     }
1370     if (m_default_value.has_value()) {
1371       return std::any_cast<T>(m_default_value);
1372     }
1373     if constexpr (details::IsContainer<T>) {
1374       if (!m_accepts_optional_like_value) {
1375         return any_cast_container<T>(m_values);
1376       }
1377     }
1378 
1379     throw std::logic_error("No value provided for '" + m_names.back() + "'.");
1380   }
1381 
1382   /*
1383    * Get argument value given a type.
1384    * @pre The object has no default value.
1385    * @returns The stored value if any, std::nullopt otherwise.
1386    */
1387   template <typename T> auto present() const -> std::optional<T> {
1388     if (m_default_value.has_value()) {
1389       throw std::logic_error("Argument with default value always presents");
1390     }
1391     if (m_values.empty()) {
1392       return std::nullopt;
1393     }
1394     if constexpr (details::IsContainer<T>) {
1395       return any_cast_container<T>(m_values);
1396     }
1397     return std::any_cast<T>(m_values.front());
1398   }
1399 
1400   template <typename T>
1401   static auto any_cast_container(const std::vector<std::any> &operand) -> T {
1402     using ValueType = typename T::value_type;
1403 
1404     T result;
1405     std::transform(
1406         std::begin(operand), std::end(operand), std::back_inserter(result),
1407         [](const auto &value) { return std::any_cast<ValueType>(value); });
1408     return result;
1409   }
1410 
1411   std::vector<std::string> m_names;
1412   std::string_view m_used_name;
1413   std::string m_help;
1414   std::string m_metavar;
1415   std::any m_default_value;
1416   std::string m_default_value_repr;
1417   std::optional<std::string>
1418       m_default_value_str; // used for checking default_value against choices
1419   std::any m_implicit_value;
1420   std::optional<std::vector<std::string>> m_choices{std::nullopt};
1421   using valued_action = std::function<std::any(const std::string &)>;
1422   using void_action = std::function<void(const std::string &)>;
1423   std::variant<valued_action, void_action> m_action{
1424       std::in_place_type<valued_action>,
1425       [](const std::string &value) { return value; }};
1426   std::vector<std::any> m_values;
1427   NArgsRange m_num_args_range{1, 1};
1428   // Bit field of bool values. Set default value in ctor.
1429   bool m_accepts_optional_like_value : 1;
1430   bool m_is_optional : 1;
1431   bool m_is_required : 1;
1432   bool m_is_repeatable : 1;
1433   bool m_is_used : 1;
1434   std::string_view m_prefix_chars; // ArgumentParser has the prefix_chars
1435 };
1436 
1437 class ArgumentParser {
1438 public:
1439   explicit ArgumentParser(std::string program_name = {},
1440                           std::string version = "1.0",
1441                           default_arguments add_args = default_arguments::all,
1442                           bool exit_on_default_arguments = true,
1443                           std::ostream &os = std::cout)
1444       : m_program_name(std::move(program_name)), m_version(std::move(version)),
1445         m_exit_on_default_arguments(exit_on_default_arguments),
1446         m_parser_path(m_program_name) {
1447     if ((add_args & default_arguments::help) == default_arguments::help) {
1448       add_argument("-h", "--help")
1449           .action([&](const auto & /*unused*/) {
1450             os << help().str();
1451             if (m_exit_on_default_arguments) {
1452               std::exit(0);
1453             }
1454           })
1455           .default_value(false)
1456           .help("shows help message and exits")
1457           .implicit_value(true)
1458           .nargs(0);
1459     }
1460     if ((add_args & default_arguments::version) == default_arguments::version) {
1461       add_argument("-v", "--version")
1462           .action([&](const auto & /*unused*/) {
1463             os << m_version << std::endl;
1464             if (m_exit_on_default_arguments) {
1465               std::exit(0);
1466             }
1467           })
1468           .default_value(false)
1469           .help("prints version information and exits")
1470           .implicit_value(true)
1471           .nargs(0);
1472     }
1473   }
1474 
1475   ~ArgumentParser() = default;
1476 
1477   // ArgumentParser is meant to be used in a single function.
1478   // Setup everything and parse arguments in one place.
1479   //
1480   // ArgumentParser internally uses std::string_views,
1481   // references, iterators, etc.
1482   // Many of these elements become invalidated after a copy or move.
1483   ArgumentParser(const ArgumentParser &other) = delete;
1484   ArgumentParser &operator=(const ArgumentParser &other) = delete;
1485   ArgumentParser(ArgumentParser &&) noexcept = delete;
1486   ArgumentParser &operator=(ArgumentParser &&) = delete;
1487 
1488   explicit operator bool() const {
1489     auto arg_used = std::any_of(m_argument_map.cbegin(), m_argument_map.cend(),
1490                                 [](auto &it) { return it.second->m_is_used; });
1491     auto subparser_used =
1492         std::any_of(m_subparser_used.cbegin(), m_subparser_used.cend(),
1493                     [](auto &it) { return it.second; });
1494 
1495     return m_is_parsed && (arg_used || subparser_used);
1496   }
1497 
1498   // Parameter packing
1499   // Call add_argument with variadic number of string arguments
1500   template <typename... Targs> Argument &add_argument(Targs... f_args) {
1501     using array_of_sv = std::array<std::string_view, sizeof...(Targs)>;
1502     auto argument =
1503         m_optional_arguments.emplace(std::cend(m_optional_arguments),
1504                                      m_prefix_chars, array_of_sv{f_args...});
1505 
1506     if (!argument->m_is_optional) {
1507       m_positional_arguments.splice(std::cend(m_positional_arguments),
1508                                     m_optional_arguments, argument);
1509     }
1510 
1511     index_argument(argument);
1512     return *argument;
1513   }
1514 
1515   class MutuallyExclusiveGroup {
1516     friend class ArgumentParser;
1517 
1518   public:
1519     MutuallyExclusiveGroup() = delete;
1520 
1521     explicit MutuallyExclusiveGroup(ArgumentParser &parent,
1522                                     bool required = false)
1523         : m_parent(parent), m_required(required), m_elements({}) {}
1524 
1525     MutuallyExclusiveGroup(const MutuallyExclusiveGroup &other) = delete;
1526     MutuallyExclusiveGroup &
1527     operator=(const MutuallyExclusiveGroup &other) = delete;
1528 
1529     MutuallyExclusiveGroup(MutuallyExclusiveGroup &&other) noexcept
1530         : m_parent(other.m_parent), m_required(other.m_required),
1531           m_elements(std::move(other.m_elements)) {
1532       other.m_elements.clear();
1533     }
1534 
1535     template <typename... Targs> Argument &add_argument(Targs... f_args) {
1536       auto &argument = m_parent.add_argument(std::forward<Targs>(f_args)...);
1537       m_elements.push_back(&argument);
1538       return argument;
1539     }
1540 
1541   private:
1542     ArgumentParser &m_parent;
1543     bool m_required{false};
1544     std::vector<Argument *> m_elements{};
1545   };
1546 
1547   MutuallyExclusiveGroup &add_mutually_exclusive_group(bool required = false) {
1548     m_mutually_exclusive_groups.emplace_back(*this, required);
1549     return m_mutually_exclusive_groups.back();
1550   }
1551 
1552   // Parameter packed add_parents method
1553   // Accepts a variadic number of ArgumentParser objects
1554   template <typename... Targs>
1555   ArgumentParser &add_parents(const Targs &... f_args) {
1556     for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) {
1557       for (const auto &argument : parent_parser.m_positional_arguments) {
1558         auto it = m_positional_arguments.insert(
1559             std::cend(m_positional_arguments), argument);
1560         index_argument(it);
1561       }
1562       for (const auto &argument : parent_parser.m_optional_arguments) {
1563         auto it = m_optional_arguments.insert(std::cend(m_optional_arguments),
1564                                               argument);
1565         index_argument(it);
1566       }
1567     }
1568     return *this;
1569   }
1570 
1571   ArgumentParser &add_description(std::string description) {
1572     m_description = std::move(description);
1573     return *this;
1574   }
1575 
1576   ArgumentParser &add_epilog(std::string epilog) {
1577     m_epilog = std::move(epilog);
1578     return *this;
1579   }
1580 
1581   /* Getter for arguments and subparsers.
1582    * @throws std::logic_error in case of an invalid argument or subparser name
1583    */
1584   template <typename T = Argument> T &at(std::string_view name) {
1585     if constexpr (std::is_same_v<T, Argument>) {
1586       return (*this)[name];
1587     } else {
1588       auto subparser_it = m_subparser_map.find(name);
1589       if (subparser_it != m_subparser_map.end()) {
1590         return subparser_it->second->get();
1591       }
1592       throw std::logic_error("No such subparser: " + std::string(name));
1593     }
1594   }
1595 
1596   ArgumentParser &set_prefix_chars(std::string prefix_chars) {
1597     m_prefix_chars = std::move(prefix_chars);
1598     return *this;
1599   }
1600 
1601   ArgumentParser &set_assign_chars(std::string assign_chars) {
1602     m_assign_chars = std::move(assign_chars);
1603     return *this;
1604   }
1605 
1606   /* Call parse_args_internal - which does all the work
1607    * Then, validate the parsed arguments
1608    * This variant is used mainly for testing
1609    * @throws std::runtime_error in case of any invalid argument
1610    */
1611   void parse_args(const std::vector<std::string> &arguments) {
1612     parse_args_internal(arguments);
1613     // Check if all arguments are parsed
1614     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
1615       argument->validate();
1616     }
1617 
1618     // Check each mutually exclusive group and make sure
1619     // there are no constraint violations
1620     for (const auto &group : m_mutually_exclusive_groups) {
1621       auto mutex_argument_used{false};
1622       Argument *mutex_argument_it{nullptr};
1623       for (Argument *arg : group.m_elements) {
1624         if (!mutex_argument_used && arg->m_is_used) {
1625           mutex_argument_used = true;
1626           mutex_argument_it = arg;
1627         } else if (mutex_argument_used && arg->m_is_used) {
1628           // Violation
1629           throw std::runtime_error("Argument '" + arg->get_usage_full() +
1630                                    "' not allowed with '" +
1631                                    mutex_argument_it->get_usage_full() + "'");
1632         }
1633       }
1634 
1635       if (!mutex_argument_used && group.m_required) {
1636         // at least one argument from the group is
1637         // required
1638         std::string argument_names{};
1639         std::size_t i = 0;
1640         std::size_t size = group.m_elements.size();
1641         for (Argument *arg : group.m_elements) {
1642           if (i + 1 == size) {
1643             // last
1644             argument_names += "'" + arg->get_usage_full() + "' ";
1645           } else {
1646             argument_names += "'" + arg->get_usage_full() + "' or ";
1647           }
1648           i += 1;
1649         }
1650         throw std::runtime_error("One of the arguments " + argument_names +
1651                                  "is required");
1652       }
1653     }
1654   }
1655 
1656   /* Call parse_known_args_internal - which does all the work
1657    * Then, validate the parsed arguments
1658    * This variant is used mainly for testing
1659    * @throws std::runtime_error in case of any invalid argument
1660    */
1661   std::vector<std::string>
1662   parse_known_args(const std::vector<std::string> &arguments) {
1663     auto unknown_arguments = parse_known_args_internal(arguments);
1664     // Check if all arguments are parsed
1665     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
1666       argument->validate();
1667     }
1668     return unknown_arguments;
1669   }
1670 
1671   /* Main entry point for parsing command-line arguments using this
1672    * ArgumentParser
1673    * @throws std::runtime_error in case of any invalid argument
1674    */
1675   // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
1676   void parse_args(int argc, const char *const argv[]) {
1677     parse_args({argv, argv + argc});
1678   }
1679 
1680   /* Main entry point for parsing command-line arguments using this
1681    * ArgumentParser
1682    * @throws std::runtime_error in case of any invalid argument
1683    */
1684   // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
1685   auto parse_known_args(int argc, const char *const argv[]) {
1686     return parse_known_args({argv, argv + argc});
1687   }
1688 
1689   /* Getter for options with default values.
1690    * @throws std::logic_error if parse_args() has not been previously called
1691    * @throws std::logic_error if there is no such option
1692    * @throws std::logic_error if the option has no value
1693    * @throws std::bad_any_cast if the option is not of type T
1694    */
1695   template <typename T = std::string> T get(std::string_view arg_name) const {
1696     if (!m_is_parsed) {
1697       throw std::logic_error("Nothing parsed, no arguments are available.");
1698     }
1699     return (*this)[arg_name].get<T>();
1700   }
1701 
1702   /* Getter for options without default values.
1703    * @pre The option has no default value.
1704    * @throws std::logic_error if there is no such option
1705    * @throws std::bad_any_cast if the option is not of type T
1706    */
1707   template <typename T = std::string>
1708   auto present(std::string_view arg_name) const -> std::optional<T> {
1709     return (*this)[arg_name].present<T>();
1710   }
1711 
1712   /* Getter that returns true for user-supplied options. Returns false if not
1713    * user-supplied, even with a default value.
1714    */
1715   auto is_used(std::string_view arg_name) const {
1716     return (*this)[arg_name].m_is_used;
1717   }
1718 
1719   /* Getter that returns true if a subcommand is used.
1720    */
1721   auto is_subcommand_used(std::string_view subcommand_name) const {
1722     return m_subparser_used.at(subcommand_name);
1723   }
1724 
1725   /* Getter that returns true if a subcommand is used.
1726    */
1727   auto is_subcommand_used(const ArgumentParser &subparser) const {
1728     return is_subcommand_used(subparser.m_program_name);
1729   }
1730 
1731   /* Indexing operator. Return a reference to an Argument object
1732    * Used in conjunction with Argument.operator== e.g., parser["foo"] == true
1733    * @throws std::logic_error in case of an invalid argument name
1734    */
1735   Argument &operator[](std::string_view arg_name) const {
1736     auto it = m_argument_map.find(arg_name);
1737     if (it != m_argument_map.end()) {
1738       return *(it->second);
1739     }
1740     if (!is_valid_prefix_char(arg_name.front())) {
1741       std::string name(arg_name);
1742       const auto legal_prefix_char = get_any_valid_prefix_char();
1743       const auto prefix = std::string(1, legal_prefix_char);
1744 
1745       // "-" + arg_name
1746       name = prefix + name;
1747       it = m_argument_map.find(name);
1748       if (it != m_argument_map.end()) {
1749         return *(it->second);
1750       }
1751       // "--" + arg_name
1752       name = prefix + name;
1753       it = m_argument_map.find(name);
1754       if (it != m_argument_map.end()) {
1755         return *(it->second);
1756       }
1757     }
1758     throw std::logic_error("No such argument: " + std::string(arg_name));
1759   }
1760 
1761   // Print help message
1762   friend auto operator<<(std::ostream &stream, const ArgumentParser &parser)
1763       -> std::ostream & {
1764     stream.setf(std::ios_base::left);
1765 
1766     auto longest_arg_length = parser.get_length_of_longest_argument();
1767 
1768     stream << parser.usage() << "\n\n";
1769 
1770     if (!parser.m_description.empty()) {
1771       stream << parser.m_description << "\n\n";
1772     }
1773 
1774     if (!parser.m_positional_arguments.empty()) {
1775       stream << "Positional arguments:\n";
1776     }
1777 
1778     for (const auto &argument : parser.m_positional_arguments) {
1779       stream.width(static_cast<std::streamsize>(longest_arg_length));
1780       stream << argument;
1781     }
1782 
1783     if (!parser.m_optional_arguments.empty()) {
1784       stream << (parser.m_positional_arguments.empty() ? "" : "\n")
1785              << "Optional arguments:\n";
1786     }
1787 
1788     for (const auto &argument : parser.m_optional_arguments) {
1789       stream.width(static_cast<std::streamsize>(longest_arg_length));
1790       stream << argument;
1791     }
1792 
1793     bool has_visible_subcommands = std::any_of(
1794         parser.m_subparser_map.begin(), parser.m_subparser_map.end(),
1795         [](auto &p) { return !p.second->get().m_suppress; });
1796 
1797     if (has_visible_subcommands) {
1798       stream << (parser.m_positional_arguments.empty()
1799                      ? (parser.m_optional_arguments.empty() ? "" : "\n")
1800                      : "\n")
1801              << "Subcommands:\n";
1802       for (const auto &[command, subparser] : parser.m_subparser_map) {
1803         if (subparser->get().m_suppress) {
1804           continue;
1805         }
1806 
1807         stream << std::setw(2) << " ";
1808         stream << std::setw(static_cast<int>(longest_arg_length - 2))
1809                << command;
1810         stream << " " << subparser->get().m_description << "\n";
1811       }
1812     }
1813 
1814     if (!parser.m_epilog.empty()) {
1815       stream << '\n';
1816       stream << parser.m_epilog << "\n\n";
1817     }
1818 
1819     return stream;
1820   }
1821 
1822   // Format help message
1823   auto help() const -> std::stringstream {
1824     std::stringstream out;
1825     out << *this;
1826     return out;
1827   }
1828 
1829   // Format usage part of help only
1830   auto usage() const -> std::string {
1831     std::stringstream stream;
1832 
1833     stream << "Usage: " << this->m_program_name;
1834 
1835     // Add any options inline here
1836     for (const auto &argument : this->m_optional_arguments) {
1837       stream << " " << argument.get_inline_usage();
1838     }
1839     // Put positional arguments after the optionals
1840     for (const auto &argument : this->m_positional_arguments) {
1841       if (!argument.m_metavar.empty()) {
1842         stream << " " << argument.m_metavar;
1843       } else {
1844         stream << " " << argument.m_names.front();
1845       }
1846     }
1847     // Put subcommands after positional arguments
1848     if (!m_subparser_map.empty()) {
1849       stream << " {";
1850       std::size_t i{0};
1851       for (const auto &[command, subparser] : m_subparser_map) {
1852         if (subparser->get().m_suppress) {
1853           continue;
1854         }
1855 
1856         if (i == 0) {
1857           stream << command;
1858         } else {
1859           stream << "," << command;
1860         }
1861         ++i;
1862       }
1863       stream << "}";
1864     }
1865 
1866     return stream.str();
1867   }
1868 
1869   // Printing the one and only help message
1870   // I've stuck with a simple message format, nothing fancy.
1871   [[deprecated("Use cout << program; instead.  See also help().")]] std::string
1872   print_help() const {
1873     auto out = help();
1874     std::cout << out.rdbuf();
1875     return out.str();
1876   }
1877 
1878   void add_subparser(ArgumentParser &parser) {
1879     parser.m_parser_path = m_program_name + " " + parser.m_program_name;
1880     auto it = m_subparsers.emplace(std::cend(m_subparsers), parser);
1881     m_subparser_map.insert_or_assign(parser.m_program_name, it);
1882     m_subparser_used.insert_or_assign(parser.m_program_name, false);
1883   }
1884 
1885   void set_suppress(bool suppress) { m_suppress = suppress; }
1886 
1887 private:
1888   bool is_valid_prefix_char(char c) const {
1889     return m_prefix_chars.find(c) != std::string::npos;
1890   }
1891 
1892   char get_any_valid_prefix_char() const { return m_prefix_chars[0]; }
1893 
1894   /*
1895    * Pre-process this argument list. Anything starting with "--", that
1896    * contains an =, where the prefix before the = has an entry in the
1897    * options table, should be split.
1898    */
1899   std::vector<std::string>
1900   preprocess_arguments(const std::vector<std::string> &raw_arguments) const {
1901     std::vector<std::string> arguments{};
1902     for (const auto &arg : raw_arguments) {
1903 
1904       const auto argument_starts_with_prefix_chars =
1905           [this](const std::string &a) -> bool {
1906         if (!a.empty()) {
1907 
1908           const auto legal_prefix = [this](char c) -> bool {
1909             return m_prefix_chars.find(c) != std::string::npos;
1910           };
1911 
1912           // Windows-style
1913           // if '/' is a legal prefix char
1914           // then allow single '/' followed by argument name, followed by an
1915           // assign char, e.g., ':' e.g., 'test.exe /A:Foo'
1916           const auto windows_style = legal_prefix('/');
1917 
1918           if (windows_style) {
1919             if (legal_prefix(a[0])) {
1920               return true;
1921             }
1922           } else {
1923             // Slash '/' is not a legal prefix char
1924             // For all other characters, only support long arguments
1925             // i.e., the argument must start with 2 prefix chars, e.g,
1926             // '--foo' e,g, './test --foo=Bar -DARG=yes'
1927             if (a.size() > 1) {
1928               return (legal_prefix(a[0]) && legal_prefix(a[1]));
1929             }
1930           }
1931         }
1932         return false;
1933       };
1934 
1935       // Check that:
1936       // - We don't have an argument named exactly this
1937       // - The argument starts with a prefix char, e.g., "--"
1938       // - The argument contains an assign char, e.g., "="
1939       auto assign_char_pos = arg.find_first_of(m_assign_chars);
1940 
1941       if (m_argument_map.find(arg) == m_argument_map.end() &&
1942           argument_starts_with_prefix_chars(arg) &&
1943           assign_char_pos != std::string::npos) {
1944         // Get the name of the potential option, and check it exists
1945         std::string opt_name = arg.substr(0, assign_char_pos);
1946         if (m_argument_map.find(opt_name) != m_argument_map.end()) {
1947           // This is the name of an option! Split it into two parts
1948           arguments.push_back(std::move(opt_name));
1949           arguments.push_back(arg.substr(assign_char_pos + 1));
1950           continue;
1951         }
1952       }
1953       // If we've fallen through to here, then it's a standard argument
1954       arguments.push_back(arg);
1955     }
1956     return arguments;
1957   }
1958 
1959   /*
1960    * @throws std::runtime_error in case of any invalid argument
1961    */
1962   void parse_args_internal(const std::vector<std::string> &raw_arguments) {
1963     auto arguments = preprocess_arguments(raw_arguments);
1964     if (m_program_name.empty() && !arguments.empty()) {
1965       m_program_name = arguments.front();
1966     }
1967     auto end = std::end(arguments);
1968     auto positional_argument_it = std::begin(m_positional_arguments);
1969     for (auto it = std::next(std::begin(arguments)); it != end;) {
1970       const auto &current_argument = *it;
1971       if (Argument::is_positional(current_argument, m_prefix_chars)) {
1972         if (positional_argument_it == std::end(m_positional_arguments)) {
1973 
1974           std::string_view maybe_command = current_argument;
1975 
1976           // Check sub-parsers
1977           auto subparser_it = m_subparser_map.find(maybe_command);
1978           if (subparser_it != m_subparser_map.end()) {
1979 
1980             // build list of remaining args
1981             const auto unprocessed_arguments =
1982                 std::vector<std::string>(it, end);
1983 
1984             // invoke subparser
1985             m_is_parsed = true;
1986             m_subparser_used[maybe_command] = true;
1987             return subparser_it->second->get().parse_args(
1988                 unprocessed_arguments);
1989           }
1990 
1991           if (m_positional_arguments.empty()) {
1992 
1993             // Ask the user if they argument they provided was a typo
1994             // for some sub-parser,
1995             // e.g., user provided `git totes` instead of `git notes`
1996             if (!m_subparser_map.empty()) {
1997               throw std::runtime_error(
1998                   "Failed to parse '" + current_argument + "', did you mean '" +
1999                   std::string{details::get_most_similar_string(
2000                       m_subparser_map, current_argument)} +
2001                   "'");
2002             }
2003 
2004             // Ask the user if they meant to use a specific optional argument
2005             if (!m_optional_arguments.empty()) {
2006               for (const auto &opt : m_optional_arguments) {
2007                 if (!opt.m_implicit_value.has_value()) {
2008                   // not a flag, requires a value
2009                   if (!opt.m_is_used) {
2010                     throw std::runtime_error(
2011                         "Zero positional arguments expected, did you mean " +
2012                         opt.get_usage_full());
2013                   }
2014                 }
2015               }
2016 
2017               throw std::runtime_error("Zero positional arguments expected");
2018             } else {
2019               throw std::runtime_error("Zero positional arguments expected");
2020             }
2021           } else {
2022             throw std::runtime_error("Maximum number of positional arguments "
2023                                      "exceeded, failed to parse '" +
2024                                      current_argument + "'");
2025           }
2026         }
2027         auto argument = positional_argument_it++;
2028         it = argument->consume(it, end);
2029         continue;
2030       }
2031 
2032       auto arg_map_it = m_argument_map.find(current_argument);
2033       if (arg_map_it != m_argument_map.end()) {
2034         auto argument = arg_map_it->second;
2035         it = argument->consume(std::next(it), end, arg_map_it->first);
2036       } else if (const auto &compound_arg = current_argument;
2037                  compound_arg.size() > 1 &&
2038                  is_valid_prefix_char(compound_arg[0]) &&
2039                  !is_valid_prefix_char(compound_arg[1])) {
2040         ++it;
2041         for (std::size_t j = 1; j < compound_arg.size(); j++) {
2042           auto hypothetical_arg = std::string{'-', compound_arg[j]};
2043           auto arg_map_it2 = m_argument_map.find(hypothetical_arg);
2044           if (arg_map_it2 != m_argument_map.end()) {
2045             auto argument = arg_map_it2->second;
2046             it = argument->consume(it, end, arg_map_it2->first);
2047           } else {
2048             throw std::runtime_error("Unknown argument: " + current_argument);
2049           }
2050         }
2051       } else {
2052         throw std::runtime_error("Unknown argument: " + current_argument);
2053       }
2054     }
2055     m_is_parsed = true;
2056   }
2057 
2058   /*
2059    * Like parse_args_internal but collects unused args into a vector<string>
2060    */
2061   std::vector<std::string>
2062   parse_known_args_internal(const std::vector<std::string> &raw_arguments) {
2063     auto arguments = preprocess_arguments(raw_arguments);
2064 
2065     std::vector<std::string> unknown_arguments{};
2066 
2067     if (m_program_name.empty() && !arguments.empty()) {
2068       m_program_name = arguments.front();
2069     }
2070     auto end = std::end(arguments);
2071     auto positional_argument_it = std::begin(m_positional_arguments);
2072     for (auto it = std::next(std::begin(arguments)); it != end;) {
2073       const auto &current_argument = *it;
2074       if (Argument::is_positional(current_argument, m_prefix_chars)) {
2075         if (positional_argument_it == std::end(m_positional_arguments)) {
2076 
2077           std::string_view maybe_command = current_argument;
2078 
2079           // Check sub-parsers
2080           auto subparser_it = m_subparser_map.find(maybe_command);
2081           if (subparser_it != m_subparser_map.end()) {
2082 
2083             // build list of remaining args
2084             const auto unprocessed_arguments =
2085                 std::vector<std::string>(it, end);
2086 
2087             // invoke subparser
2088             m_is_parsed = true;
2089             m_subparser_used[maybe_command] = true;
2090             return subparser_it->second->get().parse_known_args_internal(
2091                 unprocessed_arguments);
2092           }
2093 
2094           // save current argument as unknown and go to next argument
2095           unknown_arguments.push_back(current_argument);
2096           ++it;
2097         } else {
2098           // current argument is the value of a positional argument
2099           // consume it
2100           auto argument = positional_argument_it++;
2101           it = argument->consume(it, end);
2102         }
2103         continue;
2104       }
2105 
2106       auto arg_map_it = m_argument_map.find(current_argument);
2107       if (arg_map_it != m_argument_map.end()) {
2108         auto argument = arg_map_it->second;
2109         it = argument->consume(std::next(it), end, arg_map_it->first);
2110       } else if (const auto &compound_arg = current_argument;
2111                  compound_arg.size() > 1 &&
2112                  is_valid_prefix_char(compound_arg[0]) &&
2113                  !is_valid_prefix_char(compound_arg[1])) {
2114         ++it;
2115         for (std::size_t j = 1; j < compound_arg.size(); j++) {
2116           auto hypothetical_arg = std::string{'-', compound_arg[j]};
2117           auto arg_map_it2 = m_argument_map.find(hypothetical_arg);
2118           if (arg_map_it2 != m_argument_map.end()) {
2119             auto argument = arg_map_it2->second;
2120             it = argument->consume(it, end, arg_map_it2->first);
2121           } else {
2122             unknown_arguments.push_back(current_argument);
2123             break;
2124           }
2125         }
2126       } else {
2127         // current argument is an optional-like argument that is unknown
2128         // save it and move to next argument
2129         unknown_arguments.push_back(current_argument);
2130         ++it;
2131       }
2132     }
2133     m_is_parsed = true;
2134     return unknown_arguments;
2135   }
2136 
2137   // Used by print_help.
2138   std::size_t get_length_of_longest_argument() const {
2139     if (m_argument_map.empty()) {
2140       return 0;
2141     }
2142     std::size_t max_size = 0;
2143     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
2144       max_size =
2145           std::max<std::size_t>(max_size, argument->get_arguments_length());
2146     }
2147     for ([[maybe_unused]] const auto &[command, unused] : m_subparser_map) {
2148       max_size = std::max<std::size_t>(max_size, command.size());
2149     }
2150     return max_size;
2151   }
2152 
2153   using argument_it = std::list<Argument>::iterator;
2154   using mutex_group_it = std::vector<MutuallyExclusiveGroup>::iterator;
2155   using argument_parser_it =
2156       std::list<std::reference_wrapper<ArgumentParser>>::iterator;
2157 
2158   void index_argument(argument_it it) {
2159     for (const auto &name : std::as_const(it->m_names)) {
2160       m_argument_map.insert_or_assign(name, it);
2161     }
2162   }
2163 
2164   std::string m_program_name;
2165   std::string m_version;
2166   std::string m_description;
2167   std::string m_epilog;
2168   bool m_exit_on_default_arguments = true;
2169   std::string m_prefix_chars{"-"};
2170   std::string m_assign_chars{"="};
2171   bool m_is_parsed = false;
2172   std::list<Argument> m_positional_arguments;
2173   std::list<Argument> m_optional_arguments;
2174   std::map<std::string_view, argument_it> m_argument_map;
2175   std::string m_parser_path;
2176   std::list<std::reference_wrapper<ArgumentParser>> m_subparsers;
2177   std::map<std::string_view, argument_parser_it> m_subparser_map;
2178   std::map<std::string_view, bool> m_subparser_used;
2179   std::vector<MutuallyExclusiveGroup> m_mutually_exclusive_groups;
2180   bool m_suppress = false;
2181 };
2182 
2183 } // namespace argparse