Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-10 07:49:36

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