File indexing completed on 2026-04-10 07:49:36
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
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 {
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 }
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...> ) {
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;
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
0292
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 }
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;
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 }
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...> )
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
0665
0666
0667
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 & ) {
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
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
0937
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
0976
0977
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
0992
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
1084
1085 void validate() const {
1086 if (m_is_optional) {
1087
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
1103
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
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
1168 if (!m_metavar.empty()) {
1169
1170 return 2 + m_metavar.size();
1171 }
1172
1173
1174 return 2 + names_size + (m_names.size() - 1);
1175 }
1176
1177
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;
1183 }
1184
1185 friend std::ostream &operator<<(std::ostream &stream,
1186 const Argument &argument) {
1187 std::stringstream name_stream;
1188 name_stream << " ";
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
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
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 = " ";
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
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
1273
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
1290
1291
1292
1293
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
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
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
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
1452 auto consume_digits = [=](std::string_view sd) {
1453
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
1550
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
1574
1575
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;
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
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;
1630 std::string_view m_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 & ) {
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 & ) {
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
1676
1677
1678
1679
1680
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
1697
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
1755
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
1774
1775
1776 ArgumentParser &add_usage_newline() {
1777 ++m_usage_newline_counter;
1778 return *this;
1779 }
1780
1781
1782
1783
1784
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
1801
1802
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
1816
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
1842
1843
1844
1845
1846 void parse_args(const std::vector<std::string> &arguments) {
1847 parse_args_internal(arguments);
1848
1849 for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
1850 argument->validate();
1851 }
1852
1853
1854
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
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
1872
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
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
1892
1893
1894
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
1900 for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
1901 argument->validate();
1902 }
1903 return unknown_arguments;
1904 }
1905
1906
1907
1908
1909
1910
1911 void parse_args(int argc, const char *const argv[]) {
1912 parse_args({argv, argv + argc});
1913 }
1914
1915
1916
1917
1918
1919
1920 auto parse_known_args(int argc, const char *const argv[]) {
1921 return parse_known_args({argv, argv + argc});
1922 }
1923
1924
1925
1926
1927
1928
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
1938
1939
1940
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
1948
1949
1950 auto is_used(std::string_view arg_name) const {
1951 return (*this)[arg_name].m_is_used;
1952 }
1953
1954
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
1961
1962 auto is_subcommand_used(const ArgumentParser &subparser) const {
1963 return is_subcommand_used(subparser.m_program_name);
1964 }
1965
1966
1967
1968
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
1981 name = prefix + name;
1982 it = m_argument_map.find(name);
1983 if (it != m_argument_map.end()) {
1984 return *(it->second);
1985 }
1986
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
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
2078 auto help() const -> std::stringstream {
2079 std::stringstream out;
2080 out << *this;
2081 return out;
2082 }
2083
2084
2085 ArgumentParser &set_usage_max_line_width(size_t w) {
2086 this->m_usage_max_line_width = w;
2087 return *this;
2088 }
2089
2090
2091
2092 ArgumentParser &set_usage_break_on_mutex() {
2093 this->m_usage_break_on_mutex = true;
2094 return *this;
2095 }
2096
2097
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
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
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
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
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
2249
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
2285
2286
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
2302
2303
2304
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
2313
2314
2315
2316 if (a.size() > 1) {
2317 return (legal_prefix(a[0]) && legal_prefix(a[1]));
2318 }
2319 }
2320 }
2321 return false;
2322 };
2323
2324
2325
2326
2327
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
2334 std::string opt_name = arg.substr(0, assign_char_pos);
2335 if (m_argument_map.find(opt_name) != m_argument_map.end()) {
2336
2337 arguments.push_back(std::move(opt_name));
2338 arguments.push_back(arg.substr(assign_char_pos + 1));
2339 continue;
2340 }
2341 }
2342
2343 arguments.push_back(arg);
2344 }
2345 return arguments;
2346 }
2347
2348
2349
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 ¤t_argument = *it;
2360 if (Argument::is_positional(current_argument, m_prefix_chars)) {
2361 if (positional_argument_it == std::end(m_positional_arguments)) {
2362
2363
2364 auto subparser_it = m_subparser_map.find(current_argument);
2365 if (subparser_it != m_subparser_map.end()) {
2366
2367
2368 const auto unprocessed_arguments =
2369 std::vector<std::string>(it, end);
2370
2371
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
2381
2382
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
2392 if (!m_optional_arguments.empty()) {
2393 for (const auto &opt : m_optional_arguments) {
2394 if (!opt.m_implicit_value.has_value()) {
2395
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
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
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 ¤t_argument = *it;
2477 if (Argument::is_positional(current_argument, m_prefix_chars)) {
2478 if (positional_argument_it == std::end(m_positional_arguments)) {
2479
2480
2481 auto subparser_it = m_subparser_map.find(current_argument);
2482 if (subparser_it != m_subparser_map.end()) {
2483
2484
2485 const auto unprocessed_arguments =
2486 std::vector<std::string>(it, end);
2487
2488
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
2496 unknown_arguments.push_back(current_argument);
2497 ++it;
2498 } else {
2499
2500
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
2529
2530 unknown_arguments.push_back(current_argument);
2531 ++it;
2532 }
2533 }
2534 m_is_parsed = true;
2535 return unknown_arguments;
2536 }
2537
2538
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 }