args 6.6.0
A simple single-header C++11 STL-only argument parser library
Loading...
Searching...
No Matches
args.hxx
Go to the documentation of this file.
1/* A simple header-only C++ argument parser library.
2 *
3 * https://github.com/Taywee/args
4 *
5 * Copyright (c) 2016-2024 Taylor Richberger <taylor@axfive.net> and Pavel
6 * Belikov
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
33#ifndef ARGS_HXX
34#define ARGS_HXX
35#pragma push_macro("min")
36#pragma push_macro("max")
37#undef min
38#undef max
39
40#define ARGS_VERSION "6.6.0"
41#define ARGS_VERSION_MAJOR 6
42#define ARGS_VERSION_MINOR 6
43#define ARGS_VERSION_PATCH 0
44
45#include <algorithm>
46#include <iterator>
47#include <exception>
48#include <functional>
49#include <sstream>
50#include <string>
51#include <tuple>
52#include <vector>
53#include <unordered_map>
54#include <unordered_set>
55#include <type_traits>
56#include <cstddef>
57#include <cctype>
58#include <cerrno>
59#include <cstdlib>
60#include <limits>
61#include <iostream>
62
63#if defined(_MSC_VER) && _MSC_VER <= 1800
64#define noexcept
65#endif
66
70namespace args
71{
77 template <typename Option>
78 auto get(Option &option_) -> decltype(option_.Get())
79 {
80 return option_.Get();
81 }
82
91 inline std::string::size_type Glyphs(const std::string &string_)
92 {
93 std::string::size_type length = 0;
94 for (const char c: string_)
95 {
96 if ((c & 0xc0) != 0x80)
97 {
98 ++length;
99 }
100 }
101 return length;
102 }
103
107 template<typename T>
108 bool SafeAdd(T a, T b, T& out) noexcept
109 {
110 static_assert(std::is_integral<T>::value, "SafeAdd requires integral types.");
111 if (std::is_unsigned<T>::value)
112 {
113 using U = typename std::make_unsigned<T>::type;
114 const U ua = static_cast<U>(a);
115 const U ub = static_cast<U>(b);
116 const U maxv = std::numeric_limits<U>::max();
117 if (ua > maxv - ub)
118 {
119 return false;
120 }
121 out = static_cast<T>(ua + ub);
122 return true;
123 }
124 else
125 {
126#if defined(__clang__) || defined(__GNUC__)
127 return !__builtin_add_overflow(a, b, &out);
128#else
129 // Fallback bounds check
130 if (b > 0 && a > std::numeric_limits<T>::max() - b)
131 {
132 return false;
133 }
134 if (b < 0 && a < std::numeric_limits<T>::min() - b)
135 {
136 return false;
137 }
138 out = a + b;
139 return true;
140#endif
141 }
142 }
143
147 template<typename T>
148 bool SafeMultiply(T a, T b, T& out) noexcept
149 {
150 static_assert(std::is_integral<T>::value, "SafeMultiply requires integral types.");
151
152 if (a == 0 || b == 0)
153 {
154 out = 0;
155 return true;
156 }
157
158 if (std::is_unsigned<T>::value)
159 {
160 using U = typename std::make_unsigned<T>::type;
161 const U ua = static_cast<U>(a);
162 const U ub = static_cast<U>(b);
163 const U maxv = std::numeric_limits<U>::max();
164 if (ub > maxv / ua)
165 {
166 return false;
167 }
168 out = static_cast<T>(ua * ub);
169 return true;
170 }
171 else
172 {
173#if defined(__clang__) || defined(__GNUC__)
174 return !__builtin_mul_overflow(a, b, &out);
175#else
176 // Fallback bounds check
177 if (a == -1 && b == std::numeric_limits<T>::min())
178 {
179 return false;
180 }
181 if (b == -1 && a == std::numeric_limits<T>::min())
182 {
183 return false;
184 }
185 if ((a > 0 && b > 0 && a > std::numeric_limits<T>::max() / b) ||
186 (a > 0 && b < 0 && b < std::numeric_limits<T>::min() / a) ||
187 (a < 0 && b > 0 && a < std::numeric_limits<T>::min() / b) ||
188 (a < 0 && b < 0 && a < std::numeric_limits<T>::max() / b))
189 {
190 return false;
191 }
192 out = a * b;
193 return true;
194#endif
195 }
196 }
197
201 template<typename T>
202 bool SafeSub(T a, T b, T& out) noexcept
203 {
204 static_assert(std::is_integral<T>::value, "SafeSub requires integral types.");
205 if (std::is_unsigned<T>::value)
206 {
207 if (a < b)
208 {
209 return false;
210 }
211 out = a - b;
212 return true;
213 }
214 else
215 {
216#if defined(__clang__) || defined(__GNUC__)
217 return !__builtin_sub_overflow(a, b, &out);
218#else
219 // Fallback bounds check
220 if (b > 0 && a < std::numeric_limits<T>::min() + b)
221 {
222 return false;
223 }
224 if (b < 0 && a > std::numeric_limits<T>::max() + b)
225 {
226 return false;
227 }
228 out = a - b;
229 return true;
230#endif
231 }
232 }
233
237 // Unsigned overload
238 template<typename T>
239 typename std::enable_if<std::is_unsigned<T>::value, bool>::type
240 SafeNeg(T a, T& out) noexcept
241 {
242 static_assert(std::is_integral<T>::value, "SafeNeg requires integral types.");
243 if (a != 0)
244 {
245 return false;
246 }
247 out = 0;
248 return true;
249 }
250
251 // Signed overload
252 template<typename T>
253 typename std::enable_if<std::is_signed<T>::value, bool>::type
254 SafeNeg(T a, T& out) noexcept
255 {
256 static_assert(std::is_integral<T>::value, "SafeNeg requires integral types.");
257 if (a == std::numeric_limits<T>::min())
258 {
259 return false;
260 }
261 out = -a;
262 return true;
263 }
264
276 template <typename It>
277 inline std::vector<std::string> Wrap(It begin,
278 It end,
279 const std::string::size_type width,
280 std::string::size_type firstlinewidth = 0,
281 std::string::size_type firstlineindent = 0)
282 {
283 std::vector<std::string> output;
284 std::string line(firstlineindent, ' ');
285 bool empty = true;
286
287 if (firstlinewidth == 0)
288 {
289 firstlinewidth = width;
290 }
291
292 auto currentwidth = firstlinewidth;
293
294 for (auto it = begin; it != end; ++it)
295 {
296 if (it->empty())
297 {
298 continue;
299 }
300
301 if (*it == "\n")
302 {
303 if (!empty)
304 {
305 output.push_back(line);
306 line.clear();
307 empty = true;
308 currentwidth = width;
309 }
310
311 continue;
312 }
313
314 auto itemsize = Glyphs(*it);
315
316 // Refactored to prevent integer overflow
317 bool needsWrap = false;
318 if (itemsize >= currentwidth)
319 {
320 needsWrap = true;
321 }
322 else
323 {
324 size_t remainingWidth = (currentwidth > itemsize) ? (currentwidth - itemsize) : 0;
325 size_t nextLength = 0;
326 if (!SafeAdd<std::string::size_type>(line.length(), static_cast<std::string::size_type>(1), nextLength) || nextLength > remainingWidth)
327 {
328 needsWrap = true;
329 }
330 }
331
332 if (needsWrap)
333 {
334 if (!empty)
335 {
336 output.push_back(line);
337 line.clear();
338 empty = true;
339 currentwidth = width;
340 }
341 }
342
343 if (itemsize > 0)
344 {
345 if (!empty)
346 {
347 line += ' ';
348 }
349
350 line += *it;
351 empty = false;
352 }
353 }
354
355 if (!empty)
356 {
357 output.push_back(line);
358 }
359
360 return output;
361 }
362
363 namespace detail
364 {
365 template <typename T>
366 std::string Join(const T& array, const std::string &delimiter)
367 {
368 std::string res;
369
370 // Safely compute reservation size to avoid unbounded reallocations
371 using size_type = std::string::size_type;
372 size_type total = 0;
373 size_type count = 0;
374 const size_type delim_size = static_cast<size_type>(delimiter.size());
375 bool can_reserve = true;
376
377 for (const auto &element : array)
378 {
379 const size_type elem_size = static_cast<size_type>(element.size());
380 if (!SafeAdd<size_type>(total, elem_size, total))
381 {
382 can_reserve = false;
383 break;
384 }
385 ++count;
386 }
387
388 if (can_reserve && count > 1)
389 {
390 size_type delim_count = count - 1;
391 size_type delim_total = 0;
392 if (!SafeMultiply<size_type>(delim_count, delim_size, delim_total) ||
393 !SafeAdd<size_type>(total, delim_total, total))
394 {
395 can_reserve = false;
396 }
397 }
398
399 if (can_reserve && total > 0)
400 {
401 res.reserve(total);
402 }
403
404 bool first = true;
405 for (const auto &element : array)
406 {
407 if (!first)
408 {
409 res += delimiter;
410 }
411 res += element;
412 first = false;
413 }
414
415 return res;
416 }
417 }
418
428 inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
429 {
430 // Preserve existing line breaks
431 const auto newlineloc = in.find('\n');
432 if (newlineloc != in.npos)
433 {
434 auto first = Wrap(std::string(in, 0, newlineloc), width);
435 auto second = Wrap(std::string(in, newlineloc + 1), width);
436 first.insert(
437 std::end(first),
438 std::make_move_iterator(std::begin(second)),
439 std::make_move_iterator(std::end(second)));
440 return first;
441 }
442
443 std::istringstream stream(in);
444 std::string::size_type indent = 0;
445
446 for (auto c : in)
447 {
448 if (!std::isspace(static_cast<unsigned char>(c)))
449 {
450 break;
451 }
452 ++indent;
453 }
454
455 return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(),
456 width, firstlinewidth, indent);
457 }
458
459#ifdef ARGS_NOEXCEPT
461 enum class Error
462 {
463 None,
464 Usage,
465 Parse,
466 Validation,
467 Required,
468 Map,
469 Extra,
470 Help,
471 Subparser,
472 Completion,
473 };
474#else
477 class Error : public std::runtime_error
478 {
479 public:
480 Error(const std::string &problem) : std::runtime_error(problem) {}
481 virtual ~Error() {}
482 };
483
486 class UsageError : public Error
487 {
488 public:
489 UsageError(const std::string &problem) : Error(problem) {}
490 virtual ~UsageError() {}
491 };
492
495 class ParseError : public Error
496 {
497 public:
498 ParseError(const std::string &problem) : Error(problem) {}
499 virtual ~ParseError() {}
500 };
501
504 class ValidationError : public Error
505 {
506 public:
507 ValidationError(const std::string &problem) : Error(problem) {}
508 virtual ~ValidationError() {}
509 };
510
514 {
515 public:
516 RequiredError(const std::string &problem) : ValidationError(problem) {}
517 virtual ~RequiredError() {}
518 };
519
522 class MapError : public ParseError
523 {
524 public:
525 MapError(const std::string &problem) : ParseError(problem) {}
526 virtual ~MapError() {}
527 };
528
531 class ExtraError : public ParseError
532 {
533 public:
534 ExtraError(const std::string &problem) : ParseError(problem) {}
535 virtual ~ExtraError() {}
536 };
537
540 class Help : public Error
541 {
542 public:
543 Help(const std::string &flag) : Error(flag) {}
544 virtual ~Help() {}
545 };
546
549 class SubparserError : public Error
550 {
551 public:
552 SubparserError() : Error("") {}
553 virtual ~SubparserError() {}
554 };
555
558 class Completion : public Error
559 {
560 public:
561 Completion(const std::string &flag) : Error(flag) {}
562 virtual ~Completion() {}
563 };
564#endif
565
569 {
570 const bool isShort;
571 const char shortFlag;
572 const std::string longFlag;
573 EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {}
574 EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {}
575 EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {}
576
579 static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags)
580 {
581 std::unordered_set<std::string> longFlags;
582 for (const EitherFlag &flag: flags)
583 {
584 if (!flag.isShort)
585 {
586 longFlags.insert(flag.longFlag);
587 }
588 }
589 return longFlags;
590 }
591
594 static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags)
595 {
596 std::unordered_set<char> shortFlags;
597 for (const EitherFlag &flag: flags)
598 {
599 if (flag.isShort)
600 {
601 shortFlags.insert(flag.shortFlag);
602 }
603 }
604 return shortFlags;
605 }
606
607 std::string str() const
608 {
609 return isShort ? std::string(1, shortFlag) : longFlag;
610 }
611
612 std::string str(const std::string &shortPrefix, const std::string &longPrefix) const
613 {
614 return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag;
615 }
616 };
617
618
619
627 {
628 private:
629 const std::unordered_set<char> shortFlags;
630 const std::unordered_set<std::string> longFlags;
631
632 public:
637 template <typename ShortIt, typename LongIt>
638 Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) :
639 shortFlags(shortFlagsStart, shortFlagsEnd),
640 longFlags(longFlagsStart, longFlagsEnd)
641 {
642 if (shortFlags.empty() && longFlags.empty())
643 {
644#ifndef ARGS_NOEXCEPT
645 throw UsageError("empty Matcher");
646#endif
647 }
648 }
649
650#ifdef ARGS_NOEXCEPT
652 Error GetError() const noexcept
653 {
654 return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None;
655 }
656#endif
657
662 template <typename Short, typename Long>
663 Matcher(Short &&shortIn, Long &&longIn) :
664 Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn))
665 {}
666
679 Matcher(std::initializer_list<EitherFlag> in) :
680 Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {}
681
682 Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags))
683 {}
684
685 ~Matcher() {}
686
689 bool Match(const char flag) const
690 {
691 return shortFlags.find(flag) != shortFlags.end();
692 }
693
696 bool Match(const std::string &flag) const
697 {
698 return longFlags.find(flag) != longFlags.end();
699 }
700
703 bool Match(const EitherFlag &flag) const
704 {
705 return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag);
706 }
707
710 std::vector<EitherFlag> GetFlagStrings() const
711 {
712 std::vector<EitherFlag> flagStrings;
713 flagStrings.reserve(shortFlags.size() + longFlags.size());
714 for (const char flag: shortFlags)
715 {
716 flagStrings.emplace_back(flag);
717 }
718 for (const std::string &flag: longFlags)
719 {
720 flagStrings.emplace_back(flag);
721 }
722 return flagStrings;
723 }
724
728 {
729 if (!longFlags.empty())
730 {
731 return *longFlags.begin();
732 }
733
734 if (!shortFlags.empty())
735 {
736 return *shortFlags.begin();
737 }
738
739 // should be unreachable
740 return ' ';
741 }
742
746 {
747 if (!shortFlags.empty())
748 {
749 return *shortFlags.begin();
750 }
751
752 if (!longFlags.empty())
753 {
754 return *longFlags.begin();
755 }
756
757 // should be unreachable
758 return ' ';
759 }
760 };
761
764 enum class Options
765 {
768 None = 0x0,
769
772 Single = 0x01,
773
776 Required = 0x02,
777
780 HiddenFromUsage = 0x04,
781
785
788 Global = 0x10,
789
792 KickOut = 0x20,
793
797
801 };
802
803 inline Options operator | (Options lhs, Options rhs)
804 {
805 return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs));
806 }
807
808 inline Options operator & (Options lhs, Options rhs)
809 {
810 return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs));
811 }
812
813 class FlagBase;
814 class PositionalBase;
815 class Command;
816 class ArgumentParser;
817
821 {
824 unsigned int width = 80;
827 unsigned int progindent = 2;
830 unsigned int progtailindent = 4;
833 unsigned int descriptionindent = 4;
836 unsigned int flagindent = 6;
839 unsigned int helpindent = 40;
842 unsigned int eachgroupindent = 2;
843
846 unsigned int gutter = 1;
847
850 bool showTerminator = true;
851
855
859
862 std::string shortPrefix;
863
866 std::string longPrefix;
867
870 std::string shortSeparator;
871
874 std::string longSeparator;
875
878 std::string programName;
879
883
887
890 std::string proglineOptions = "{OPTIONS}";
891
894 std::string proglineCommand = "COMMAND";
895
898 std::string proglineValueOpen = " <";
899
902 std::string proglineValueClose = ">";
903
906 std::string proglineRequiredOpen = "";
907
910 std::string proglineRequiredClose = "";
911
914 std::string proglineNonrequiredOpen = "[";
915
918 std::string proglineNonrequiredClose = "]";
919
922 bool proglineShowFlags = false;
923
927
930 std::string usageString;
931
934 std::string optionsString = "OPTIONS:";
935
938 bool useValueNameOnce = false;
939
942 bool showValueName = true;
943
947
950 std::string valueOpen = "[";
951
954 std::string valueClose = "]";
955
958 bool addChoices = false;
959
962 std::string choiceString = "\nOne of: ";
963
966 bool addDefault = false;
967
970 std::string defaultString = "\nDefault: ";
971 };
972
977 struct Nargs
978 {
979 const size_t min;
980 const size_t max;
981
982 Nargs(size_t min_, size_t max_) : min{min_}, max{max_}
983 {
984#ifndef ARGS_NOEXCEPT
985 if (max < min)
986 {
987 throw UsageError("Nargs: max < min");
988 }
989#endif
990 }
991
992 Nargs(size_t num_) : min{num_}, max{num_}
993 {
994 }
995
996 friend bool operator == (const Nargs &lhs, const Nargs &rhs)
997 {
998 return lhs.min == rhs.min && lhs.max == rhs.max;
999 }
1000
1001 friend bool operator != (const Nargs &lhs, const Nargs &rhs)
1002 {
1003 return !(lhs == rhs);
1004 }
1005 };
1006
1009 class Base
1010 {
1011 private:
1012 Options options = {};
1013
1014 protected:
1015 bool matched = false;
1016 const std::string help;
1017#ifdef ARGS_NOEXCEPT
1019 mutable Error error = Error::None;
1020 mutable std::string errorMsg;
1021#endif
1022
1023 public:
1024 Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {}
1025 virtual ~Base() {}
1026
1027 Options GetOptions() const noexcept
1028 {
1029 return options;
1030 }
1031
1032 bool IsRequired() const noexcept
1033 {
1034 return (GetOptions() & Options::Required) != Options::None;
1035 }
1036
1037 virtual bool Matched() const noexcept
1038 {
1039 return matched;
1040 }
1041
1042 virtual void Validate(const std::string &, const std::string &) const
1043 {
1044 }
1045
1046 operator bool() const noexcept
1047 {
1048 return Matched();
1049 }
1050
1051 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const
1052 {
1053 std::tuple<std::string, std::string, unsigned> description;
1054 std::get<1>(description) = help;
1055 std::get<2>(description) = indentLevel;
1056 return { std::move(description) };
1057 }
1058
1059 virtual std::vector<Command*> GetCommands()
1060 {
1061 return {};
1062 }
1063
1064 virtual bool IsGroup() const
1065 {
1066 return false;
1067 }
1068
1069 virtual bool IsFlag() const
1070 {
1071 return false;
1072 }
1073
1074 virtual FlagBase *Match(const EitherFlag &)
1075 {
1076 return nullptr;
1077 }
1078
1079 virtual PositionalBase *GetNextPositional()
1080 {
1081 return nullptr;
1082 }
1083
1084 virtual std::vector<FlagBase*> GetAllFlags()
1085 {
1086 return {};
1087 }
1088
1089 virtual bool HasFlag() const
1090 {
1091 return false;
1092 }
1093
1094 virtual bool HasPositional() const
1095 {
1096 return false;
1097 }
1098
1099 virtual bool HasCommand() const
1100 {
1101 return false;
1102 }
1103
1104 virtual std::vector<std::string> GetProgramLine(const HelpParams &) const
1105 {
1106 return {};
1107 }
1108
1110 void KickOut(bool kickout_) noexcept
1111 {
1112 if (kickout_)
1113 {
1114 options = options | Options::KickOut;
1115 }
1116 else
1117 {
1118 options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut));
1119 }
1120 }
1121
1123 bool KickOut() const noexcept
1124 {
1125 return (options & Options::KickOut) != Options::None;
1126 }
1127
1128 virtual void Reset() noexcept
1129 {
1130 matched = false;
1131#ifdef ARGS_NOEXCEPT
1132 error = Error::None;
1133 errorMsg.clear();
1134#endif
1135 }
1136
1137#ifdef ARGS_NOEXCEPT
1139 virtual Error GetError() const
1140 {
1141 return error;
1142 }
1143
1145 virtual std::string GetErrorMsg() const
1146 {
1147 return errorMsg;
1148 }
1149#endif
1150 };
1151
1154 class NamedBase : public Base
1155 {
1156 protected:
1157 const std::string name;
1158 bool kickout = false;
1159 std::string defaultString;
1160 bool defaultStringManual = false;
1161 std::vector<std::string> choicesStrings;
1162 bool choicesStringManual = false;
1163
1164 virtual std::string GetDefaultString(const HelpParams&) const { return {}; }
1165
1166 virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; }
1167
1168 virtual std::string GetNameString(const HelpParams&) const { return Name(); }
1169
1170 void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const
1171 {
1172 if (isManual && !manual.empty())
1173 {
1174 dest += str;
1175 dest += manual;
1176 }
1177 else if (!isManual && isGenerated && !generated.empty())
1178 {
1179 dest += str;
1180 dest += generated;
1181 }
1182 }
1183
1184 public:
1185 NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {}
1186 virtual ~NamedBase() {}
1187
1191 void HelpDefault(const std::string &str)
1192 {
1193 defaultStringManual = true;
1194 defaultString = str;
1195 }
1196
1199 std::string HelpDefault(const HelpParams &params) const
1200 {
1201 return defaultStringManual ? defaultString : GetDefaultString(params);
1202 }
1203
1207 void HelpChoices(const std::vector<std::string> &array)
1208 {
1209 choicesStringManual = true;
1210 choicesStrings = array;
1211 }
1212
1215 std::vector<std::string> HelpChoices(const HelpParams &params) const
1216 {
1217 return choicesStringManual ? choicesStrings : GetChoicesStrings(params);
1218 }
1219
1220 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
1221 {
1222 std::tuple<std::string, std::string, unsigned> description;
1223 std::get<0>(description) = GetNameString(params);
1224 std::get<1>(description) = help;
1225 std::get<2>(description) = indentLevel;
1226
1227 AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString);
1228 AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString);
1229
1230 return { std::move(description) };
1231 }
1232
1233 virtual std::string Name() const
1234 {
1235 return name;
1236 }
1237 };
1238
1239 namespace detail
1240 {
1241 template<typename T>
1242 using vector = std::vector<T, std::allocator<T>>;
1243
1244 template<typename K, typename T>
1245 using unordered_map = std::unordered_map<K, T, std::hash<K>,
1246 std::equal_to<K>, std::allocator<std::pair<const K, T> > >;
1247
1248 template<typename S, typename T>
1250 {
1251 template<typename SS, typename TT>
1252 static auto test(int)
1253 -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
1254
1255 template<typename, typename>
1256 static auto test(...) -> std::false_type;
1257
1258 public:
1259 using type = decltype(test<S,T>(0));
1260 };
1261
1262 template <typename T>
1263 using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type;
1264
1265 template <typename T>
1266 typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
1267 ToString(const T &value)
1268 {
1269 std::ostringstream s;
1270 s << value;
1271 return s.str();
1272 }
1273
1274 template <typename T>
1275 typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
1276 ToString(const T &)
1277 {
1278 return {};
1279 }
1280
1281 template <typename T>
1282 std::vector<std::string> MapKeysToStrings(const T &map)
1283 {
1284 std::vector<std::string> res;
1285 using K = typename std::decay<decltype(std::begin(map)->first)>::type;
1286 if (IsConvertableToString<K>::value)
1287 {
1288 for (const auto &p : map)
1289 {
1290 res.push_back(detail::ToString(p.first));
1291 }
1292
1293 std::sort(res.begin(), res.end());
1294 }
1295 return res;
1296 }
1297 }
1298
1301 class FlagBase : public NamedBase
1302 {
1303 protected:
1304 const Matcher matcher;
1305
1306 virtual std::string GetNameString(const HelpParams &params) const override
1307 {
1308 const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
1309 std::string flags;
1310 const auto flagStrings = matcher.GetFlagStrings();
1311 const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
1312 for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
1313 {
1314 auto &flag = *it;
1315 if (it != flagStrings.begin())
1316 {
1317 flags += ", ";
1318 }
1319
1320 flags += flag.isShort ? params.shortPrefix : params.longPrefix;
1321 flags += flag.str();
1322
1323 if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
1324 {
1325 flags += flag.isShort ? params.shortSeparator : params.longSeparator;
1326 flags += params.valueOpen + postfix + params.valueClose;
1327 }
1328 }
1329
1330 return flags;
1331 }
1332
1333 public:
1334 FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {}
1335
1336 FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {}
1337
1338 virtual ~FlagBase() {}
1339
1340 virtual bool IsFlag() const override
1341 {
1342 return true;
1343 }
1344
1345 virtual FlagBase *Match(const EitherFlag &flag) override
1346 {
1347 if (matcher.Match(flag))
1348 {
1349 if ((GetOptions() & Options::Single) != Options::None && matched)
1350 {
1351 std::ostringstream problem;
1352 problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once";
1353#ifdef ARGS_NOEXCEPT
1354 error = Error::Extra;
1355 errorMsg = problem.str();
1356#else
1357 throw ExtraError(problem.str());
1358#endif
1359 }
1360 matched = true;
1361 return this;
1362 }
1363 return nullptr;
1364 }
1365
1366 virtual std::vector<FlagBase*> GetAllFlags() override
1367 {
1368 return { this };
1369 }
1370
1371 const Matcher &GetMatcher() const
1372 {
1373 return matcher;
1374 }
1375
1376 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1377 {
1378 if (!Matched() && IsRequired())
1379 {
1380 std::ostringstream problem;
1381 problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required";
1382#ifdef ARGS_NOEXCEPT
1383 error = Error::Required;
1384 errorMsg = problem.str();
1385#else
1386 throw RequiredError(problem.str());
1387#endif
1388 }
1389 }
1390
1391 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1392 {
1393 if (!params.proglineShowFlags)
1394 {
1395 return {};
1396 }
1397
1398 const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name();
1399 const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny();
1400 std::string res = flag.str(params.shortPrefix, params.longPrefix);
1401 if (!postfix.empty())
1402 {
1403 res += params.proglineValueOpen + postfix + params.proglineValueClose;
1404 }
1405
1406 return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
1407 : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
1408 }
1409
1410 virtual bool HasFlag() const override
1411 {
1412 return true;
1413 }
1414
1415#ifdef ARGS_NOEXCEPT
1417 bool usageError = false;
1418 void SetUsageError()
1419 {
1420 usageError = true;
1421 }
1422 void ClearUsageError()
1423 {
1424 usageError = false;
1425 }
1426 virtual Error GetError() const override
1427 {
1428 if(usageError)
1429 {
1430 return Error::Usage;
1431 }
1432 const auto nargs = NumberOfArguments();
1433 if (nargs.min > nargs.max)
1434 {
1435 return Error::Usage;
1436 }
1437
1438 const auto matcherError = matcher.GetError();
1439 if (matcherError != Error::None)
1440 {
1441 return matcherError;
1442 }
1443
1444 return error;
1445 }
1446#endif
1447
1452 virtual Nargs NumberOfArguments() const noexcept = 0;
1453
1458 virtual void ParseValue(const std::vector<std::string> &value) = 0;
1459 };
1460
1464 {
1465 public:
1466 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {}
1467 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {}
1468 virtual ~ValueFlagBase() {}
1469
1470 virtual Nargs NumberOfArguments() const noexcept override
1471 {
1472 return 1;
1473 }
1474 };
1475
1477 {
1478 public:
1479 std::vector<std::string> reply;
1480 size_t cword = 0;
1481 std::string syntax;
1482
1483 template <typename GroupClass>
1484 CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden)
1485 {
1486 group_.AddCompletion(*this);
1487 }
1488
1489 virtual ~CompletionFlag() {}
1490
1491 virtual Nargs NumberOfArguments() const noexcept override
1492 {
1493 return 2;
1494 }
1495
1496 virtual void ParseValue(const std::vector<std::string> &value_) override
1497 {
1498 syntax = value_.at(0);
1499 const std::string &raw = value_.at(1);
1500 bool failed = false;
1501
1502 const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c)
1503 {
1504 return std::isspace(static_cast<unsigned char>(c)) != 0;
1505 });
1506
1507 // Reject explicit signs: cword must be a plain non-negative
1508 // decimal index. istringstream would otherwise silently
1509 // accept "+1".
1510 if (firstNonSpace != raw.end() && (*firstNonSpace == '-' || *firstNonSpace == '+'))
1511 {
1512 failed = true;
1513 }
1514
1515 size_t parsed = 0;
1516 if (!failed)
1517 {
1518 std::istringstream ss(raw);
1519 // Use the C locale so that the cword index parses
1520 // consistently regardless of any std::locale::global call
1521 // elsewhere in the process. A locale with a non-empty
1522 // grouping facet would otherwise reject digit-only inputs
1523 // like "12" when grouping rules expect separators.
1524 ss.imbue(std::locale::classic());
1525 ss >> parsed;
1526 if (ss.fail())
1527 {
1528 failed = true;
1529 }
1530 else
1531 {
1532 char extra;
1533 if (ss >> extra)
1534 {
1535 failed = true;
1536 }
1537 else if (!ss.eof())
1538 {
1539 failed = true;
1540 }
1541 }
1542 }
1543
1544 if (failed)
1545 {
1546#ifdef ARGS_NOEXCEPT
1547 error = Error::Parse;
1548 errorMsg = "Argument 'completion' received invalid value type '" + raw + "'";
1549#else
1550 std::ostringstream problem;
1551 problem << "Argument 'completion' received invalid value type '" << raw << "'";
1552 throw ParseError(problem.str());
1553#endif
1554 return;
1555 }
1556
1557 cword = parsed;
1558 }
1559
1562 std::string Get() noexcept
1563 {
1564 return detail::Join(reply, "\n");
1565 }
1566
1567 virtual void Reset() noexcept override
1568 {
1569 ValueFlagBase::Reset();
1570 cword = 0;
1571 syntax.clear();
1572 reply.clear();
1573 }
1574 };
1575
1576
1580 {
1581 protected:
1582 bool ready;
1583
1584 public:
1585 PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {}
1586 virtual ~PositionalBase() {}
1587
1588 bool Ready()
1589 {
1590 return ready;
1591 }
1592
1593 virtual void ParseValue(const std::string &value_) = 0;
1594
1595 virtual void Reset() noexcept override
1596 {
1597 matched = false;
1598 ready = true;
1599#ifdef ARGS_NOEXCEPT
1600 error = Error::None;
1601 errorMsg.clear();
1602#endif
1603 }
1604
1605 virtual PositionalBase *GetNextPositional() override
1606 {
1607 return Ready() ? this : nullptr;
1608 }
1609
1610 virtual bool HasPositional() const override
1611 {
1612 return true;
1613 }
1614
1615 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1616 {
1617 return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose
1618 : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose };
1619 }
1620
1621 virtual void Validate(const std::string &, const std::string &) const override
1622 {
1623 if (IsRequired() && !Matched())
1624 {
1625 std::ostringstream problem;
1626 problem << "Option '" << Name() << "' is required";
1627#ifdef ARGS_NOEXCEPT
1628 error = Error::Required;
1629 errorMsg = problem.str();
1630#else
1631 throw RequiredError(problem.str());
1632#endif
1633 }
1634 }
1635 };
1636
1639 class Group : public Base
1640 {
1641 private:
1642 Group* parent;
1643 std::vector<Base*> children;
1644 std::function<bool(const Group &)> validator;
1645
1646 public:
1650 {
1651 static bool Xor(const Group &group)
1652 {
1653 return group.MatchedChildren() == 1;
1654 }
1655
1656 static bool AtLeastOne(const Group &group)
1657 {
1658 return group.MatchedChildren() >= 1;
1659 }
1660
1661 static bool AtMostOne(const Group &group)
1662 {
1663 return group.MatchedChildren() <= 1;
1664 }
1665
1666 static bool All(const Group &group)
1667 {
1668 return group.Children().size() == group.MatchedChildren();
1669 }
1670
1671 static bool AllOrNone(const Group &group)
1672 {
1673 return (All(group) || None(group));
1674 }
1675
1676 static bool AllChildGroups(const Group &group)
1677 {
1678 return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool {
1679 return child->IsGroup() && !child->Matched();
1680 });
1681 }
1682
1683 static bool DontCare(const Group &)
1684 {
1685 return true;
1686 }
1687
1688 static bool CareTooMuch(const Group &)
1689 {
1690 return false;
1691 }
1692
1693 static bool None(const Group &group)
1694 {
1695 return group.MatchedChildren() == 0;
1696 }
1697 };
1699 Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_)
1700 {
1701 parent = nullptr;
1702 }
1704 Group(Group &group_, const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_)
1705 {
1706 group_.Add(*this);
1707 parent = &group_;
1708 }
1709 virtual ~Group() {}
1710
1713 void Add(Base &child)
1714 {
1715 children.emplace_back(&child);
1716
1717 if(child.IsFlag()) {
1718#ifndef ARGS_NOEXCEPT
1719 // Detection runs from the child's own constructor, so a
1720 // duplicate throws before that constructor completes and the
1721 // child's storage is released while the stack unwinds. Undo
1722 // the registration first, or a caller that catches the error
1723 // leaves this group holding a pointer to a dead object.
1724 try
1725 {
1726 SignalDetectDuplicates();
1727 }
1728 catch (...)
1729 {
1730 children.pop_back();
1731 throw;
1732 }
1733#else
1734 SignalDetectDuplicates();
1735#endif
1736 }
1737 }
1738
1741 const std::vector<Base *> &Children() const
1742 {
1743 return children;
1744 }
1745
1751 virtual FlagBase *Match(const EitherFlag &flag) override
1752 {
1753 for (Base *child: Children())
1754 {
1755 if (FlagBase *match = child->Match(flag))
1756 {
1757 return match;
1758 }
1759 }
1760 return nullptr;
1761 }
1762
1763 virtual std::vector<FlagBase*> GetAllFlags() override
1764 {
1765 std::vector<FlagBase*> res;
1766 for (Base *child: Children())
1767 {
1768 auto childRes = child->GetAllFlags();
1769 res.insert(res.end(), childRes.begin(), childRes.end());
1770 }
1771 return res;
1772 }
1773
1774 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1775 {
1776 for (Base *child: Children())
1777 {
1778 child->Validate(shortPrefix, longPrefix);
1779 }
1780 }
1781
1787 {
1788 for (Base *child: Children())
1789 {
1790 if (auto next = child->GetNextPositional())
1791 {
1792 return next;
1793 }
1794 }
1795 return nullptr;
1796 }
1797
1802 virtual bool HasFlag() const override
1803 {
1804 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
1805 }
1806
1811 virtual bool HasPositional() const override
1812 {
1813 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
1814 }
1815
1820 virtual bool HasCommand() const override
1821 {
1822 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
1823 }
1824
1827 std::vector<Base *>::size_type MatchedChildren() const
1828 {
1829 // Cast to avoid warnings from -Wsign-conversion
1830 return static_cast<std::vector<Base *>::size_type>(
1831 std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
1832 }
1833
1836 std::vector<Base *> GetMatchedChildren() const
1837 {
1838 // Could be replaced by C++ 20 filter, or a custom iterator.
1839 std::vector<Base*> matched_children;
1840 std::copy_if(children.begin(), children.end(), std::back_inserter(matched_children), [](Base* b){
1841 return b->Matched();
1842 });
1843 return matched_children;
1844 }
1845
1851 template <typename ChildType>
1852 std::vector<ChildType *> GetFilteredChildren(bool matching = false) const
1853 {
1854 std::vector<ChildType *> filtered_children;
1855 for(Base *child : children) {
1856 if(!matching || child->Matched())
1857 {
1858 ChildType* cast_result = dynamic_cast<ChildType*>(child);
1859 if(cast_result != nullptr)
1860 {
1861 filtered_children.push_back(cast_result);
1862 }
1863
1864 }
1865 }
1866 return filtered_children;
1867 }
1868
1871 virtual bool Matched() const noexcept override
1872 {
1873 return validator(*this);
1874 }
1875
1878 bool Get() const
1879 {
1880 return Matched();
1881 }
1882
1885 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
1886 {
1887 std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
1888
1889 // Push that group description on the back if not empty
1890 unsigned addindent = 0;
1891 if (!help.empty())
1892 {
1893 descriptions.emplace_back(help, "", indent);
1894 addindent = 1;
1895 }
1896
1897 for (Base *child: Children())
1898 {
1899 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
1900 {
1901 continue;
1902 }
1903
1904 auto groupDescriptions = child->GetDescription(params, indent + addindent);
1905 descriptions.insert(
1906 std::end(descriptions),
1907 std::make_move_iterator(std::begin(groupDescriptions)),
1908 std::make_move_iterator(std::end(groupDescriptions)));
1909 }
1910 return descriptions;
1911 }
1912
1915 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1916 {
1917 std::vector <std::string> names;
1918 for (Base *child: Children())
1919 {
1920 if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
1921 {
1922 continue;
1923 }
1924
1925 auto groupNames = child->GetProgramLine(params);
1926 names.insert(
1927 std::end(names),
1928 std::make_move_iterator(std::begin(groupNames)),
1929 std::make_move_iterator(std::end(groupNames)));
1930 }
1931 return names;
1932 }
1933
1934 virtual std::vector<Command*> GetCommands() override
1935 {
1936 std::vector<Command*> res;
1937 for (const auto &child : Children())
1938 {
1939 auto subparsers = child->GetCommands();
1940 res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
1941 }
1942 return res;
1943 }
1944
1945 virtual bool IsGroup() const override
1946 {
1947 return true;
1948 }
1949
1950 virtual void Reset() noexcept override
1951 {
1952 Base::Reset();
1953
1954 for (auto &child: Children())
1955 {
1956 child->Reset();
1957 }
1958#ifdef ARGS_NOEXCEPT
1959 error = Error::None;
1960 errorMsg.clear();
1961#endif
1962 }
1963
1968 {
1969 if(parent != nullptr) parent->SignalDetectDuplicates();
1970 else DetectDuplicateFlags();
1971 }
1972
1978 {
1979 std::unordered_set<char> usedShortFlags;
1980 std::unordered_set<std::string> usedLongFlags;
1981 DetectDuplicateFlags(usedShortFlags, usedLongFlags);
1982 }
1983
1986 void DetectDuplicateFlags(std::unordered_set<char> &usedShortFlags, std::unordered_set<std::string> &usedLongFlags)
1987 {
1988 for (Base *child: Children())
1989 {
1990 if(auto flag = dynamic_cast<FlagBase*>(child))
1991 {
1992 // Check for duplicate flags, setting a usage error on the
1993 // flag if a duplicate is detected.
1994 for(EitherFlag flagString: flag->GetMatcher().GetFlagStrings())
1995 {
1996 if(flagString.isShort)
1997 {
1998 if(usedShortFlags.count(flagString.shortFlag))
1999 {
2000#ifdef ARGS_NOEXCEPT
2001 flag->SetUsageError();
2002#else
2003 throw ParseError("duplicate short flag detected");
2004#endif
2005 }
2006 else
2007 {
2008 usedShortFlags.insert(flagString.shortFlag);
2009 }
2010 }
2011 else
2012 {
2013 if(usedLongFlags.count(flagString.longFlag))
2014 {
2015#ifdef ARGS_NOEXCEPT
2016 flag->SetUsageError();
2017#else
2018 throw ParseError("duplicate long flag detected");
2019#endif
2020 }
2021 else
2022 {
2023 usedLongFlags.insert(flagString.longFlag);
2024 }
2025 }
2026 }
2027 }
2028 else if(auto group = dynamic_cast<Group*>(child))
2029 {
2030 // A command opens its own flag namespace and runs its
2031 // own duplicate detection as a separate root, so a flag
2032 // reused either side of a command boundary is not a
2033 // genuine duplicate. Only descend into plain groups
2034 // here; IsGroup() is false for a Command.
2035 if(group->IsGroup())
2036 {
2037 group->DetectDuplicateFlags(usedShortFlags, usedLongFlags);
2038 }
2039 }
2040 }
2041 }
2042
2043#ifdef ARGS_NOEXCEPT
2045 virtual Error GetError() const override
2046 {
2047 if (error != Error::None)
2048 {
2049 return error;
2050 }
2051
2052 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2053 if (it == Children().end())
2054 {
2055 return Error::None;
2056 } else
2057 {
2058 return (*it)->GetError();
2059 }
2060 }
2061
2063 virtual std::string GetErrorMsg() const override
2064 {
2065 if (error != Error::None)
2066 {
2067 return errorMsg;
2068 }
2069
2070 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2071 if (it == Children().end())
2072 {
2073 return "";
2074 } else
2075 {
2076 return (*it)->GetErrorMsg();
2077 }
2078 }
2079#endif
2080
2081 };
2082
2085 class GlobalOptions : public Group
2086 {
2087 public:
2088 GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
2089 {
2090 Add(options_);
2091 }
2092 };
2093
2111 class Subparser : public Group
2112 {
2113 private:
2114 std::vector<std::string> args;
2115 std::vector<std::string> kicked;
2116 ArgumentParser *parser = nullptr;
2117 const HelpParams &helpParams;
2118 const Command &command;
2119 bool isParsed = false;
2120
2121 public:
2122 Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
2123 : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
2124 {
2125 }
2126
2127 Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
2128 {
2129 }
2130
2131 Subparser(const Subparser&) = delete;
2132 Subparser(Subparser&&) = delete;
2133 Subparser &operator = (const Subparser&) = delete;
2134 Subparser &operator = (Subparser&&) = delete;
2135
2136 const Command &GetCommand()
2137 {
2138 return command;
2139 }
2140
2143 bool IsParsed() const
2144 {
2145 return isParsed;
2146 }
2147
2150 void Parse();
2151
2156 const std::vector<std::string> &KickedOut() const noexcept
2157 {
2158 return kicked;
2159 }
2160 };
2161
2166 class Command : public Group
2167 {
2168 private:
2169 friend class Subparser;
2170
2171 std::string name;
2172 std::string help;
2173 std::string description;
2174 std::string epilog;
2175 std::string proglinePostfix;
2176
2177 std::function<void(Subparser&)> parserCoroutine;
2178 bool commandIsRequired = true;
2179 Command *selectedCommand = nullptr;
2180
2181 mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
2182 mutable std::vector<std::string> subparserProgramLine;
2183 mutable bool subparserHasFlag = false;
2184 mutable bool subparserHasPositional = false;
2185 mutable bool subparserHasCommand = false;
2186#ifdef ARGS_NOEXCEPT
2187 mutable Error subparserError = Error::None;
2188#endif
2189 mutable Subparser *subparser = nullptr;
2190
2191 protected:
2192
2193 class RaiiSubparser
2194 {
2195 public:
2196 RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
2197 RaiiSubparser(const Command &command_, const HelpParams &params_);
2198
2199 ~RaiiSubparser()
2200 {
2201 command.subparser = oldSubparser;
2202 }
2203
2204 Subparser &Parser()
2205 {
2206 return parser;
2207 }
2208
2209 private:
2210 const Command &command;
2211 Subparser parser;
2212 Subparser *oldSubparser;
2213 };
2214
2215 Command() = default;
2216
2217 std::function<void(Subparser&)> &GetCoroutine()
2218 {
2219 return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
2220 }
2221
2222 Command &SelectedCommand()
2223 {
2224 Command *res = this;
2225 while (res->selectedCommand != nullptr)
2226 {
2227 res = res->selectedCommand;
2228 }
2229
2230 return *res;
2231 }
2232
2233 const Command &SelectedCommand() const
2234 {
2235 const Command *res = this;
2236 while (res->selectedCommand != nullptr)
2237 {
2238 res = res->selectedCommand;
2239 }
2240
2241 return *res;
2242 }
2243
2244 void UpdateSubparserHelp(const HelpParams &params) const
2245 {
2246 if (parserCoroutine)
2247 {
2248 RaiiSubparser coro(*this, params);
2249#ifndef ARGS_NOEXCEPT
2250 try
2251 {
2252 parserCoroutine(coro.Parser());
2253 }
2254 catch (args::SubparserError&)
2255 {
2256 }
2257#else
2258 parserCoroutine(coro.Parser());
2259#endif
2260 }
2261 }
2262
2263 public:
2264 Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
2265 : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
2266 {
2267 base_.Add(*this);
2268 }
2269
2272 const std::string &ProglinePostfix() const
2273 { return proglinePostfix; }
2274
2277 void ProglinePostfix(const std::string &proglinePostfix_)
2278 { this->proglinePostfix = proglinePostfix_; }
2279
2282 const std::string &Description() const
2283 { return description; }
2287 void Description(const std::string &description_)
2288 { this->description = description_; }
2289
2292 const std::string &Epilog() const
2293 { return epilog; }
2294
2297 void Epilog(const std::string &epilog_)
2298 { this->epilog = epilog_; }
2299
2302 const std::string &Name() const
2303 { return name; }
2304
2307 const std::string &Help() const
2308 { return help; }
2309
2314 void RequireCommand(bool value)
2315 { commandIsRequired = value; }
2316
2317 virtual bool IsGroup() const override
2318 { return false; }
2319
2320 virtual bool Matched() const noexcept override
2321 { return Base::Matched(); }
2322
2323 operator bool() const noexcept
2324 { return Matched(); }
2325
2326 void Match() noexcept
2327 { matched = true; }
2328
2329 void SelectCommand(Command *c) noexcept
2330 {
2331 selectedCommand = c;
2332
2333 if (c != nullptr)
2334 {
2335 c->Match();
2336 }
2337 }
2338
2339 virtual FlagBase *Match(const EitherFlag &flag) override
2340 {
2341 if (selectedCommand != nullptr)
2342 {
2343 if (auto *res = selectedCommand->Match(flag))
2344 {
2345 return res;
2346 }
2347
2348 for (auto *child: Children())
2349 {
2350 if ((child->GetOptions() & Options::Global) != Options::None)
2351 {
2352 if (auto *res = child->Match(flag))
2353 {
2354 return res;
2355 }
2356 }
2357 }
2358
2359 return nullptr;
2360 }
2361
2362 if (subparser != nullptr)
2363 {
2364 return subparser->Match(flag);
2365 }
2366
2367 return Matched() ? Group::Match(flag) : nullptr;
2368 }
2369
2370 virtual std::vector<FlagBase*> GetAllFlags() override
2371 {
2372 std::vector<FlagBase*> res;
2373
2374 if (!Matched())
2375 {
2376 return res;
2377 }
2378
2379 for (auto *child: Children())
2380 {
2381 if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
2382 {
2383 auto childFlags = child->GetAllFlags();
2384 res.insert(res.end(), childFlags.begin(), childFlags.end());
2385 }
2386 }
2387
2388 if (selectedCommand != nullptr)
2389 {
2390 auto childFlags = selectedCommand->GetAllFlags();
2391 res.insert(res.end(), childFlags.begin(), childFlags.end());
2392 }
2393
2394 if (subparser != nullptr)
2395 {
2396 auto childFlags = subparser->GetAllFlags();
2397 res.insert(res.end(), childFlags.begin(), childFlags.end());
2398 }
2399
2400 return res;
2401 }
2402
2404 {
2405 if (selectedCommand != nullptr)
2406 {
2407 if (auto *res = selectedCommand->GetNextPositional())
2408 {
2409 return res;
2410 }
2411
2412 for (auto *child: Children())
2413 {
2414 if ((child->GetOptions() & Options::Global) != Options::None)
2415 {
2416 if (auto *res = child->GetNextPositional())
2417 {
2418 return res;
2419 }
2420 }
2421 }
2422
2423 return nullptr;
2424 }
2425
2426 if (subparser != nullptr)
2427 {
2428 return subparser->GetNextPositional();
2429 }
2430
2431 return Matched() ? Group::GetNextPositional() : nullptr;
2432 }
2433
2434 virtual bool HasFlag() const override
2435 {
2436 return subparserHasFlag || Group::HasFlag();
2437 }
2438
2439 virtual bool HasPositional() const override
2440 {
2441 return subparserHasPositional || Group::HasPositional();
2442 }
2443
2444 virtual bool HasCommand() const override
2445 {
2446 return true;
2447 }
2448
2449 std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
2450 {
2451 UpdateSubparserHelp(params);
2452
2453 std::vector<std::string> res;
2454
2455 if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
2456 {
2457 res.push_back(params.proglineOptions);
2458 }
2459
2460 auto group_res = Group::GetProgramLine(params);
2461 std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res));
2462
2463 res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
2464
2465 if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
2466 {
2467 res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
2468 }
2469
2470 if (!Name().empty())
2471 {
2472 res.insert(res.begin(), Name());
2473 }
2474
2475 if (!ProglinePostfix().empty())
2476 {
2477 std::string line;
2478 for (auto c : ProglinePostfix())
2479 {
2480 if (std::isspace(static_cast<unsigned char>(c)))
2481 {
2482 if (!line.empty())
2483 {
2484 res.push_back(line);
2485 line.clear();
2486 }
2487
2488 if (c == '\n')
2489 {
2490 res.push_back("\n");
2491 }
2492 }
2493 else
2494 {
2495 line += c;
2496 }
2497 }
2498
2499 if (!line.empty())
2500 {
2501 res.push_back(line);
2502 }
2503 }
2504
2505 return res;
2506 }
2507
2508 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
2509 {
2510 if (!Matched())
2511 {
2512 return {};
2513 }
2514
2515 return GetCommandProgramLine(params);
2516 }
2517
2518 virtual std::vector<Command*> GetCommands() override
2519 {
2520 if (selectedCommand != nullptr)
2521 {
2522 return selectedCommand->GetCommands();
2523 }
2524
2525 if (Matched())
2526 {
2527 return Group::GetCommands();
2528 }
2529
2530 return { this };
2531 }
2532
2533 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
2534 {
2535 std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
2536 unsigned addindent = 0;
2537
2538 UpdateSubparserHelp(params);
2539
2540 if (!Matched())
2541 {
2542 if (params.showCommandFullHelp)
2543 {
2544 std::ostringstream s;
2545 bool empty = true;
2546 for (const auto &progline: GetCommandProgramLine(params))
2547 {
2548 if (!empty)
2549 {
2550 s << ' ';
2551 }
2552 else
2553 {
2554 empty = false;
2555 }
2556
2557 s << progline;
2558 }
2559
2560 descriptions.emplace_back(s.str(), "", indent);
2561 }
2562 else
2563 {
2564 descriptions.emplace_back(Name(), help, indent);
2565 }
2566
2567 if (!params.showCommandChildren && !params.showCommandFullHelp)
2568 {
2569 return descriptions;
2570 }
2571
2572 addindent = 1;
2573 }
2574
2575 if (params.showCommandFullHelp && !Matched())
2576 {
2577 descriptions.emplace_back("", "", indent + addindent);
2578 descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
2579 descriptions.emplace_back("", "", indent + addindent);
2580 }
2581
2582 for (Base *child: Children())
2583 {
2584 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
2585 {
2586 continue;
2587 }
2588
2589 auto groupDescriptions = child->GetDescription(params, indent + addindent);
2590 descriptions.insert(
2591 std::end(descriptions),
2592 std::make_move_iterator(std::begin(groupDescriptions)),
2593 std::make_move_iterator(std::end(groupDescriptions)));
2594 }
2595
2596 for (auto childDescription: subparserDescription)
2597 {
2598 std::get<2>(childDescription) += indent + addindent;
2599 descriptions.push_back(std::move(childDescription));
2600 }
2601
2602 if (params.showCommandFullHelp && !Matched())
2603 {
2604 descriptions.emplace_back("", "", indent + addindent);
2605 if (!Epilog().empty())
2606 {
2607 descriptions.emplace_back(Epilog(), "", indent + addindent);
2608 descriptions.emplace_back("", "", indent + addindent);
2609 }
2610 }
2611
2612 return descriptions;
2613 }
2614
2615 virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
2616 {
2617 if (!Matched())
2618 {
2619 return;
2620 }
2621
2622 auto onValidationError = [&]
2623 {
2624 std::ostringstream problem;
2625 problem << "Group validation failed somewhere!";
2626#ifdef ARGS_NOEXCEPT
2627 error = Error::Validation;
2628 errorMsg = problem.str();
2629#else
2630 throw ValidationError(problem.str());
2631#endif
2632 };
2633
2634 for (Base *child: Children())
2635 {
2636 if (child->IsGroup() && !child->Matched())
2637 {
2638 onValidationError();
2639 }
2640
2641 child->Validate(shortprefix, longprefix);
2642 }
2643
2644 if (subparser != nullptr)
2645 {
2646 subparser->Validate(shortprefix, longprefix);
2647 if (!subparser->Matched())
2648 {
2649 onValidationError();
2650 }
2651 }
2652
2653 if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
2654 {
2655 std::ostringstream problem;
2656 problem << "Command is required";
2657#ifdef ARGS_NOEXCEPT
2658 error = Error::Validation;
2659 errorMsg = problem.str();
2660#else
2661 throw ValidationError(problem.str());
2662#endif
2663 }
2664 }
2665
2666 virtual void Reset() noexcept override
2667 {
2668 Group::Reset();
2669 selectedCommand = nullptr;
2670 subparserProgramLine.clear();
2671 subparserDescription.clear();
2672 subparserHasFlag = false;
2673 subparserHasPositional = false;
2674 subparserHasCommand = false;
2675#ifdef ARGS_NOEXCEPT
2676 subparserError = Error::None;
2677#endif
2678 }
2679
2680#ifdef ARGS_NOEXCEPT
2682 virtual Error GetError() const override
2683 {
2684 if (!Matched())
2685 {
2686 return Error::None;
2687 }
2688
2689 if (error != Error::None)
2690 {
2691 return error;
2692 }
2693
2694 if (subparserError != Error::None)
2695 {
2696 return subparserError;
2697 }
2698
2699 return Group::GetError();
2700 }
2701#endif
2702 };
2703
2707 {
2708 friend class Subparser;
2709
2710 private:
2711 std::string longprefix;
2712 std::string shortprefix;
2713
2714 std::string longseparator;
2715
2716 std::string terminator;
2717
2718 bool allowJoinedShortValue = true;
2719 bool allowJoinedLongValue = true;
2720 bool allowSeparateShortValue = true;
2721 bool allowSeparateLongValue = true;
2722
2723 bool readCompletion = false;
2724 CompletionFlag *completion = nullptr;
2725
2726 protected:
2727 enum class OptionType
2728 {
2729 LongFlag,
2730 ShortFlag,
2732 };
2733
2734 OptionType ParseOption(const std::string &s, bool allowEmpty = false)
2735 {
2736 const bool matchesLong = s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length());
2737 const bool matchesShort = s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length());
2738
2739 // A chunk can start with both prefixes when one is a prefix of
2740 // the other, or when the long prefix is empty (every string
2741 // starts with it). Resolve to the longer, more specific prefix:
2742 // this keeps the default "--"/"-" preference for long flags
2743 // while letting a short flag be recognised under an empty long
2744 // prefix instead of being swallowed as a nameless long flag.
2745 if (matchesLong && matchesShort)
2746 {
2747 return longprefix.length() >= shortprefix.length() ? OptionType::LongFlag : OptionType::ShortFlag;
2748 }
2749
2750 if (matchesLong)
2751 {
2752 return OptionType::LongFlag;
2753 }
2754
2755 if (matchesShort)
2756 {
2757 return OptionType::ShortFlag;
2758 }
2759
2760 return OptionType::Positional;
2761 }
2762
2763 template <typename It>
2764 bool Complete(FlagBase &flag, It it, It end)
2765 {
2766 auto nextIt = it;
2767 if (!readCompletion || (++nextIt != end))
2768 {
2769 return false;
2770 }
2771
2772 const auto &chunk = *it;
2773 for (auto &choice : flag.HelpChoices(helpParams))
2774 {
2775 AddCompletionReply(chunk, choice);
2776 }
2777
2778#ifndef ARGS_NOEXCEPT
2779 throw Completion(completion->Get());
2780#else
2781 return true;
2782#endif
2783 }
2784
2794 template <typename It>
2795 std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
2796 const bool allowSeparate, const bool allowJoined,
2797 const bool hasJoined, const std::string &joinedArg,
2798 const bool canDiscardJoined, std::vector<std::string> &values)
2799 {
2800 values.clear();
2801
2802 Nargs nargs = flag.NumberOfArguments();
2803
2804 if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined))
2805 {
2806 return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2807 }
2808
2809 if (hasJoined)
2810 {
2811 if (!canDiscardJoined || (allowJoined && nargs.max != 0))
2812 {
2813 values.push_back(joinedArg);
2814 }
2815 } else if (!allowSeparate)
2816 {
2817 if (nargs.min != 0)
2818 {
2819 return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2820 }
2821 }
2822
2823 // Only gather separate values when they are allowed. A joined
2824 // value that was discarded rather than taken (short chunks such
2825 // as -nf when joined short values are off) means this flag isn't
2826 // taking an argument here, so the rest of the chunk is flags.
2827 if (allowSeparate && (!hasJoined || !values.empty()))
2828 {
2829 auto valueIt = it;
2830 ++valueIt;
2831
2832 while (valueIt != end &&
2833 *valueIt != terminator &&
2834 values.size() < nargs.max &&
2835 (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2836 {
2837 if (Complete(flag, valueIt, end))
2838 {
2839 // Park `it` on the completion position rather than
2840 // `end`. In ARGS_NOEXCEPT mode Complete returns
2841 // true (no throw), so the caller's for-loop will
2842 // run its ++it after we return; advancing an
2843 // already-end iterator is undefined behavior and
2844 // causes a subsequent out-of-bounds read of the
2845 // arg vector. Since Complete only fires when
2846 // ++nextIt == end, valueIt is the last element,
2847 // and ++(it=valueIt) safely lands on end.
2848 it = valueIt;
2849 return "";
2850 }
2851
2852 values.push_back(*valueIt);
2853 ++it;
2854 ++valueIt;
2855 }
2856 }
2857
2858 if (values.size() > nargs.max)
2859 {
2860 return "Passed an argument into a non-argument flag: " + arg;
2861 } else if (values.size() < nargs.min)
2862 {
2863 if (nargs.min == 1 && nargs.max == 1)
2864 {
2865 return "Flag '" + arg + "' requires an argument but received none";
2866 } else if (nargs.min == 1)
2867 {
2868 return "Flag '" + arg + "' requires at least one argument but received none";
2869 } else if (nargs.min != nargs.max)
2870 {
2871 return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2872 " arguments but received " + std::to_string(values.size());
2873 } else
2874 {
2875 return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2876 " arguments but received " + std::to_string(values.size());
2877 }
2878 }
2879
2880 return {};
2881 }
2882
2883 template <typename It>
2884 bool ParseLong(It &it, It end)
2885 {
2886 const auto &chunk = *it;
2887 const auto argchunk = chunk.substr(longprefix.size());
2888 // Try to separate it, in case of a separator:
2889 const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2890 // If the separator is in the argument, separate it.
2891 const auto arg = (separator != argchunk.npos ?
2892 std::string(argchunk, 0, separator)
2893 : argchunk);
2894 const auto joined = (separator != argchunk.npos ?
2895 argchunk.substr(separator + longseparator.size())
2896 : std::string());
2897
2898 if (auto flag = Match(arg))
2899 {
2900#ifdef ARGS_NOEXCEPT
2901 // Match() may set the flag's error (e.g. Error::Extra when
2902 // Options::Single is violated). In non-noexcept mode that
2903 // path throws and parsing stops before the value is read;
2904 // in noexcept mode we must mirror that and skip the value
2905 // parsing so the previously-stored value is preserved.
2906 if (flag->GetError() != Error::None)
2907 {
2908 return false;
2909 }
2910#endif
2911 std::vector<std::string> values;
2912 const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2913 separator != argchunk.npos, joined, false, values);
2914 if (!errorMessage.empty())
2915 {
2916#ifndef ARGS_NOEXCEPT
2917 throw ParseError(errorMessage);
2918#else
2919 error = Error::Parse;
2920 errorMsg = errorMessage;
2921 return false;
2922#endif
2923 }
2924
2925 if (!readCompletion)
2926 {
2927 flag->ParseValue(values);
2928#ifdef ARGS_NOEXCEPT
2929 // Non-noexcept ParseValue paths throw on Help, reader
2930 // failure, or Map miss, which halts parsing. Mirror
2931 // that here so a later parser-level error (e.g. an
2932 // unknown flag) cannot shadow the flag's error in
2933 // ArgumentParser::GetError().
2934 if (flag->GetError() != Error::None)
2935 {
2936 return false;
2937 }
2938#endif
2939 }
2940
2941 if (flag->KickOut())
2942 {
2943 ++it;
2944 return false;
2945 }
2946 } else
2947 {
2948 const std::string errorMessage("Flag could not be matched: " + arg);
2949#ifndef ARGS_NOEXCEPT
2950 throw ParseError(errorMessage);
2951#else
2952 error = Error::Parse;
2953 errorMsg = errorMessage;
2954 return false;
2955#endif
2956 }
2957
2958 return true;
2959 }
2960
2961 template <typename It>
2962 bool ParseShort(It &it, It end)
2963 {
2964 const auto &chunk = *it;
2965 const auto argchunk = chunk.substr(shortprefix.size());
2966 for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2967 {
2968 const auto arg = *argit;
2969
2970 if (auto flag = Match(arg))
2971 {
2972#ifdef ARGS_NOEXCEPT
2973 // See ParseLong: if Match recorded an error
2974 // (e.g. Options::Single violation), bail before the
2975 // value is parsed so the prior value is preserved.
2976 if (flag->GetError() != Error::None)
2977 {
2978 return false;
2979 }
2980#endif
2981 const std::string value(argit + 1, std::end(argchunk));
2982 std::vector<std::string> values;
2983 const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2984 allowSeparateShortValue, allowJoinedShortValue,
2985 !value.empty(), value, !value.empty(), values);
2986
2987 if (!errorMessage.empty())
2988 {
2989#ifndef ARGS_NOEXCEPT
2990 throw ParseError(errorMessage);
2991#else
2992 error = Error::Parse;
2993 errorMsg = errorMessage;
2994 return false;
2995#endif
2996 }
2997
2998 if (!readCompletion)
2999 {
3000 flag->ParseValue(values);
3001#ifdef ARGS_NOEXCEPT
3002 // See ParseLong: ensure a flag-level error from
3003 // ParseValue (Help, Parse, Map) halts parsing so
3004 // it cannot be shadowed by a later parser error.
3005 if (flag->GetError() != Error::None)
3006 {
3007 return false;
3008 }
3009#endif
3010 }
3011
3012 if (flag->KickOut())
3013 {
3014 ++it;
3015 return false;
3016 }
3017
3018 if (!values.empty())
3019 {
3020 break;
3021 }
3022 } else
3023 {
3024 const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
3025#ifndef ARGS_NOEXCEPT
3026 throw ParseError(errorMessage);
3027#else
3028 error = Error::Parse;
3029 errorMsg = errorMessage;
3030 return false;
3031#endif
3032 }
3033 }
3034
3035 return true;
3036 }
3037
3038 bool AddCompletionReply(const std::string &cur, const std::string &choice)
3039 {
3040 if (cur.empty() || choice.find(cur) == 0)
3041 {
3042 if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
3043 {
3044 completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size()));
3045 } else
3046 {
3047 completion->reply.push_back(choice);
3048 }
3049 return true;
3050 }
3051
3052 return false;
3053 }
3054
3055 template <typename It>
3056 bool Complete(It it, It end, bool terminated)
3057 {
3058 auto nextIt = it;
3059 if (!readCompletion || (++nextIt != end))
3060 {
3061 return false;
3062 }
3063
3064 const auto &chunk = *it;
3065 auto pos = GetNextPositional();
3066 std::vector<Command *> commands = GetCommands();
3067 const auto optionType = ParseOption(chunk, true);
3068
3069 // Once the terminator has been seen the parser treats every
3070 // following chunk as positional, so only positional choices are
3071 // valid completions here. Suggesting flags or commands past the
3072 // terminator offers candidates the parser would then reject.
3073 if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
3074 {
3075 for (auto &cmd : commands)
3076 {
3077 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3078 {
3079 AddCompletionReply(chunk, cmd->Name());
3080 }
3081 }
3082 } else
3083 {
3084 bool hasPositionalCompletion = true;
3085
3086 if (!terminated && !commands.empty())
3087 {
3088 for (auto &cmd : commands)
3089 {
3090 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3091 {
3092 AddCompletionReply(chunk, cmd->Name());
3093 }
3094 }
3095 } else if (pos)
3096 {
3097 if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3098 {
3099 auto choices = pos->HelpChoices(helpParams);
3100 hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
3101 for (auto &choice : choices)
3102 {
3103 AddCompletionReply(chunk, choice);
3104 }
3105 }
3106 }
3107
3108 if (!terminated && hasPositionalCompletion)
3109 {
3110 auto flags = GetAllFlags();
3111 for (auto flag : flags)
3112 {
3113 if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
3114 {
3115 continue;
3116 }
3117
3118 auto &matcher = flag->GetMatcher();
3119 if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
3120 {
3121 for (auto &flagName : matcher.GetFlagStrings())
3122 {
3123 if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
3124 {
3125 break;
3126 }
3127 }
3128 }
3129 }
3130
3131 if (optionType == OptionType::LongFlag && allowJoinedLongValue)
3132 {
3133 const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
3134 // Only attempt joined-value completion when the
3135 // separator lies at or past the long prefix, so
3136 // there is a (possibly empty) flag name between
3137 // them. With a custom longseparator that overlaps
3138 // the prefix (e.g. LongSeparator("-") under the
3139 // default "--" prefix), an attacker-controlled
3140 // completion word like "--x" puts the separator
3141 // inside the prefix, making `arg` shorter than
3142 // longprefix. arg.substr(longprefix.size()) would
3143 // then throw std::out_of_range, which escapes the
3144 // parser as a non-args exception (bypassing the
3145 // documented catch(args::Error) idiom) and is
3146 // thrown even under ARGS_NOEXCEPT.
3147 if (separator != chunk.npos && separator >= longprefix.size())
3148 {
3149 std::string arg(chunk, 0, separator);
3150 if (auto flag = this->Match(arg.substr(longprefix.size())))
3151 {
3152 for (auto &choice : flag->HelpChoices(helpParams))
3153 {
3154 AddCompletionReply(chunk, arg + longseparator + choice);
3155 }
3156 }
3157 }
3158 } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
3159 {
3160 if (chunk.size() > shortprefix.size() + 1)
3161 {
3162 auto arg = chunk.at(shortprefix.size());
3163 //TODO: support -abcVALUE where a and b take no value
3164 if (auto flag = this->Match(arg))
3165 {
3166 for (auto &choice : flag->HelpChoices(helpParams))
3167 {
3168 AddCompletionReply(chunk, shortprefix + arg + choice);
3169 }
3170 }
3171 }
3172 }
3173 }
3174 }
3175
3176#ifndef ARGS_NOEXCEPT
3177 throw Completion(completion->Get());
3178#else
3179 return true;
3180#endif
3181 }
3182
3183 template <typename It>
3184 It Parse(It begin, It end)
3185 {
3186 bool terminated = false;
3187 std::vector<Command *> commands = GetCommands();
3188
3189 // Check all arg chunks
3190 for (auto it = begin; it != end; ++it)
3191 {
3192 if (Complete(it, end, terminated))
3193 {
3194 return end;
3195 }
3196
3197 const auto &chunk = *it;
3198
3199 if (!terminated && chunk == terminator)
3200 {
3201 terminated = true;
3202 } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
3203 {
3204 if (!ParseLong(it, end))
3205 {
3206 return it;
3207 }
3208 } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
3209 {
3210 if (!ParseShort(it, end))
3211 {
3212 return it;
3213 }
3214 } else if (!terminated && !commands.empty())
3215 {
3216 auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
3217 if (itCommand == commands.end())
3218 {
3219 const std::string errorMessage("Unknown command: " + chunk);
3220#ifndef ARGS_NOEXCEPT
3221 throw ParseError(errorMessage);
3222#else
3223 error = Error::Parse;
3224 errorMsg = errorMessage;
3225 return it;
3226#endif
3227 }
3228
3229 SelectCommand(*itCommand);
3230
3231 if (const auto &coroutine = GetCoroutine())
3232 {
3233 ++it;
3234 RaiiSubparser coro(*this, std::vector<std::string>(it, end));
3235 coroutine(coro.Parser());
3236#ifdef ARGS_NOEXCEPT
3237 error = GetError();
3238 if (error != Error::None)
3239 {
3240 return end;
3241 }
3242
3243 if (!coro.Parser().IsParsed())
3244 {
3245 error = Error::Usage;
3246 return end;
3247 }
3248#else
3249 if (!coro.Parser().IsParsed())
3250 {
3251 throw UsageError("Subparser::Parse was not called");
3252 }
3253#endif
3254
3255 break;
3256 }
3257
3258 commands = GetCommands();
3259 } else
3260 {
3261 auto pos = GetNextPositional();
3262 if (pos)
3263 {
3264 pos->ParseValue(chunk);
3265#ifdef ARGS_NOEXCEPT
3266 if (pos->GetError() != Error::None)
3267 {
3268 return it;
3269 }
3270#endif
3271
3272 if (pos->KickOut())
3273 {
3274 return ++it;
3275 }
3276 } else
3277 {
3278 const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
3279#ifndef ARGS_NOEXCEPT
3280 throw ParseError(errorMessage);
3281#else
3282 error = Error::Parse;
3283 errorMsg = errorMessage;
3284 return it;
3285#endif
3286 }
3287 }
3288
3289 if (!readCompletion && completion != nullptr && completion->Matched())
3290 {
3291#ifdef ARGS_NOEXCEPT
3292 if (completion->GetError() != Error::None)
3293 {
3294 error = completion->GetError();
3295 if (errorMsg.empty())
3296 {
3297 errorMsg = completion->GetErrorMsg();
3298 }
3299 return it;
3300 }
3301
3302 error = Error::Completion;
3303#endif
3304 readCompletion = true;
3305 ++it;
3306 const auto argsLeft = static_cast<size_t>(std::distance(it, end));
3307 if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
3308 {
3309#ifndef ARGS_NOEXCEPT
3310 throw Completion("");
3311#else
3312 return end;
3313#endif
3314 }
3315
3316 ++it;
3317 std::vector<std::string> curArgs;
3318 curArgs.reserve(completion->cword);
3319 auto curIt = it;
3320 for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
3321 {
3322 curArgs.push_back(*curIt);
3323 }
3324
3325 if (completion->syntax == "bash")
3326 {
3327 // bash tokenizes --flag=value as --flag=value
3328 // Security fix: Use size_t arithmetic throughout to avoid conversion issues
3329 for (size_t idx = 0; idx < curArgs.size(); )
3330 {
3331 if (idx > 0 && curArgs[idx] == "=")
3332 {
3333 size_t prev_idx = idx - 1; // Safe since we checked idx > 0
3334 curArgs[prev_idx] += "=";
3335 size_t next_idx = 0;
3336 if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
3337 {
3338 curArgs[prev_idx] += curArgs[next_idx];
3339 // Erase the '=' token and the following value token.
3340 size_t erase_end = 0;
3341 if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
3342 {
3343 typedef std::vector<std::string>::difference_type diff_t;
3344 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx),
3345 curArgs.begin() + static_cast<diff_t>(erase_end));
3346 }
3347 } else
3348 {
3349 // Safe erase of single '=' token at the end
3350 typedef std::vector<std::string>::difference_type diff_t;
3351 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx));
3352 }
3353 // Do not increment idx - next element slides into current position
3354 } else
3355 {
3356 ++idx;
3357 }
3358 }
3359
3360 }
3361#ifndef ARGS_NOEXCEPT
3362 try
3363 {
3364 Parse(curArgs.begin(), curArgs.end());
3365 throw Completion("");
3366 }
3367 catch (Completion &)
3368 {
3369 throw;
3370 }
3371 catch (args::Error&)
3372 {
3373 throw Completion("");
3374 }
3375#else
3376 // Discard the nested Parse's return value: it points
3377 // into the local curArgs vector, which is destroyed
3378 // when this function returns, leaving the caller with
3379 // a dangling iterator that would be compared against
3380 // the outer `end` in ParseCLI. Return the outer
3381 // `end` instead so the iterator stays in the caller's
3382 // container.
3383 Parse(curArgs.begin(), curArgs.end());
3384 error = Error::Completion;
3385 errorMsg.clear();
3386 return end;
3387#endif
3388 }
3389 }
3390
3391 Validate(shortprefix, longprefix);
3392 return end;
3393 }
3394
3395 public:
3396 HelpParams helpParams;
3397
3398 ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
3399 {
3400 Description(description_);
3401 Epilog(epilog_);
3402 LongPrefix("--");
3403 ShortPrefix("-");
3404 LongSeparator("=");
3405 Terminator("--");
3406 SetArgumentSeparations(true, true, true, true);
3407 matched = true;
3408 }
3409
3410 void AddCompletion(CompletionFlag &completionFlag)
3411 {
3412 // Only take the pointer once registration has succeeded: Add()
3413 // throws on a duplicate flag, and the flag it was handed is
3414 // gone by the time that error reaches the caller.
3415 Add(completionFlag);
3416 completion = &completionFlag;
3417 }
3418
3421 const std::string &Prog() const
3422 { return helpParams.programName; }
3425 void Prog(const std::string &prog_)
3426 { this->helpParams.programName = prog_; }
3427
3430 const std::string &LongPrefix() const
3431 { return longprefix; }
3434 void LongPrefix(const std::string &longprefix_)
3435 {
3436 this->longprefix = longprefix_;
3437 this->helpParams.longPrefix = longprefix_;
3438 }
3439
3442 const std::string &ShortPrefix() const
3443 { return shortprefix; }
3446 void ShortPrefix(const std::string &shortprefix_)
3447 {
3448 this->shortprefix = shortprefix_;
3449 this->helpParams.shortPrefix = shortprefix_;
3450 }
3451
3454 const std::string &LongSeparator() const
3455 { return longseparator; }
3458 void LongSeparator(const std::string &longseparator_)
3459 {
3460 if (longseparator_.empty())
3461 {
3462 const std::string errorMessage("longseparator can not be set to empty");
3463#ifdef ARGS_NOEXCEPT
3464 error = Error::Usage;
3465 errorMsg = errorMessage;
3466#else
3467 throw UsageError(errorMessage);
3468#endif
3469 } else
3470 {
3471 this->longseparator = longseparator_;
3472 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3473 }
3474 }
3475
3478 const std::string &Terminator() const
3479 { return terminator; }
3482 void Terminator(const std::string &terminator_)
3483 { this->terminator = terminator_; }
3484
3490 bool &allowJoinedShortValue_,
3491 bool &allowJoinedLongValue_,
3492 bool &allowSeparateShortValue_,
3493 bool &allowSeparateLongValue_) const
3494 {
3495 allowJoinedShortValue_ = this->allowJoinedShortValue;
3496 allowJoinedLongValue_ = this->allowJoinedLongValue;
3497 allowSeparateShortValue_ = this->allowSeparateShortValue;
3498 allowSeparateLongValue_ = this->allowSeparateLongValue;
3499 }
3500
3509 const bool allowJoinedShortValue_,
3510 const bool allowJoinedLongValue_,
3511 const bool allowSeparateShortValue_,
3512 const bool allowSeparateLongValue_)
3513 {
3514 this->allowJoinedShortValue = allowJoinedShortValue_;
3515 this->allowJoinedLongValue = allowJoinedLongValue_;
3516 this->allowSeparateShortValue = allowSeparateShortValue_;
3517 this->allowSeparateLongValue = allowSeparateLongValue_;
3518
3519 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3520 this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
3521 }
3522
3525 void Help(std::ostream &help_) const
3526 {
3527 auto &command = SelectedCommand();
3528 const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
3529 const auto desc_indent = helpParams.descriptionindent;
3530 const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0;
3531 const auto description_text = Wrap(commandDescription, effective_desc_width);
3532 const auto epilog_text = Wrap(command.Epilog(), effective_desc_width);
3533
3534 const bool hasoptions = command.HasFlag();
3535 const bool hasarguments = command.HasPositional();
3536
3537 std::vector<std::string> prognameline;
3538 prognameline.push_back(helpParams.usageString);
3539 prognameline.push_back(Prog());
3540 auto commandProgLine = command.GetProgramLine(helpParams);
3541 prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
3542
3543 const auto prog_sum = helpParams.progindent + helpParams.progtailindent;
3544 const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0;
3545 const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0;
3546 const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
3547 effective_prog_width,
3548 effective_prog_first);
3549 auto progit = std::begin(proglines);
3550 if (progit != std::end(proglines))
3551 {
3552 help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
3553 ++progit;
3554 }
3555 for (; progit != std::end(proglines); ++progit)
3556 {
3557 help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
3558 }
3559
3560 help_ << '\n';
3561
3562 if (!description_text.empty())
3563 {
3564 for (const auto &line: description_text)
3565 {
3566 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3567 }
3568 help_ << "\n";
3569 }
3570
3571 bool lastDescriptionIsNewline = false;
3572
3573 if (!helpParams.optionsString.empty())
3574 {
3575 help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
3576 }
3577
3578 for (const auto &desc: command.GetDescription(helpParams, 0))
3579 {
3580 lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
3581 const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
3582 const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter;
3583 const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0;
3584 const auto flags = Wrap(std::get<0>(desc), effective_flag_width);
3585 const auto info_sum = helpParams.helpindent + groupindent;
3586 const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0;
3587 const auto info = Wrap(std::get<1>(desc), effective_info_width);
3588
3589 std::string::size_type flagssize = 0;
3590 for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
3591 {
3592 if (flagsit != std::begin(flags))
3593 {
3594 help_ << '\n';
3595 }
3596 help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
3597 flagssize = Glyphs(*flagsit);
3598 }
3599
3600 auto infoit = std::begin(info);
3601 // groupindent is on both sides of this inequality, and therefore can be removed
3602 if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
3603 {
3604 help_ << '\n';
3605 } else
3606 {
3607 // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
3608 const auto indent_sum = helpParams.flagindent + flagssize;
3609 const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0;
3610 help_ << std::string(effective_space, ' ') << *infoit << '\n';
3611 ++infoit;
3612 }
3613 for (; infoit != std::end(info); ++infoit)
3614 {
3615 help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
3616 }
3617 }
3618 if (hasoptions && hasarguments && helpParams.showTerminator)
3619 {
3620 lastDescriptionIsNewline = false;
3621 const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0;
3622 for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", effective_term_width))
3623 {
3624 help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
3625 }
3626 }
3627
3628 if (!lastDescriptionIsNewline)
3629 {
3630 help_ << "\n";
3631 }
3632
3633 for (const auto &line: epilog_text)
3634 {
3635 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3636 }
3637 }
3638
3643 std::string Help() const
3644 {
3645 std::ostringstream help_;
3646 Help(help_);
3647 return help_.str();
3648 }
3649
3650 virtual void Reset() noexcept override
3651 {
3652 Command::Reset();
3653 matched = true;
3654 readCompletion = false;
3655 }
3656
3663 template <typename It>
3664 It ParseArgs(It begin, It end)
3665 {
3666 // Reset all Matched statuses and errors
3667 Reset();
3668#ifdef ARGS_NOEXCEPT
3669 error = GetError();
3670 if (error != Error::None)
3671 {
3672 return end;
3673 }
3674#endif
3675 return Parse(begin, end);
3676 }
3677
3683 template <typename T>
3684 auto ParseArgs(const T &args) -> decltype(std::begin(args))
3685 {
3686 return ParseArgs(std::begin(args), std::end(args));
3687 }
3688
3695 bool ParseCLI(const int argc, const char * const * argv)
3696 {
3697 if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty())
3698 {
3699 Prog(argv[0]);
3700 }
3701
3702 std::vector<std::string> args;
3703 if (argc > 1 && argv != nullptr)
3704 {
3705 args.assign(argv + 1, argv + argc);
3706 }
3707
3708 return ParseArgs(args) == std::end(args);
3709 }
3710
3711 template <typename T>
3712 bool ParseCLI(const T &args)
3713 {
3714 return ParseArgs(args) == std::end(args);
3715 }
3716 };
3717
3718 inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
3719 : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
3720 {
3721 command.subparser = &parser;
3722 }
3723
3724 inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
3725 {
3726 command.subparser = &parser;
3727 }
3728
3729 inline void Subparser::Parse()
3730 {
3731 isParsed = true;
3732 Reset();
3733 command.subparserDescription = GetDescription(helpParams, 0);
3734 command.subparserHasFlag = HasFlag();
3735 command.subparserHasPositional = HasPositional();
3736 command.subparserHasCommand = HasCommand();
3737 command.subparserProgramLine = GetProgramLine(helpParams);
3738 if (parser == nullptr)
3739 {
3740#ifndef ARGS_NOEXCEPT
3741 throw args::SubparserError();
3742#else
3743 error = Error::Subparser;
3744 return;
3745#endif
3746 }
3747
3748 auto it = parser->Parse(args.begin(), args.end());
3749 command.Validate(parser->ShortPrefix(), parser->LongPrefix());
3750 kicked.assign(it, args.end());
3751
3752#ifdef ARGS_NOEXCEPT
3753 command.subparserError = GetError();
3754#endif
3755 }
3756
3757 inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
3758 {
3759 parser.Help(os);
3760 return os;
3761 }
3762
3765 class Flag : public FlagBase
3766 {
3767 public:
3768 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
3769 {
3770 group_.Add(*this);
3771 }
3772
3773 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false): Flag(group_, name_, help_, std::move(matcher_), extraError_ ? Options::Single : Options::None)
3774 {
3775 }
3776
3777 virtual ~Flag() {}
3778
3781 bool Get() const
3782 {
3783 return Matched();
3784 }
3785
3786 virtual Nargs NumberOfArguments() const noexcept override
3787 {
3788 return 0;
3789 }
3790
3791 virtual void ParseValue(const std::vector<std::string>&) override
3792 {
3793 }
3794 };
3795
3800 class HelpFlag : public Flag
3801 {
3802 public:
3803 HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
3804
3805 virtual ~HelpFlag() {}
3806
3807 virtual void ParseValue(const std::vector<std::string> &)
3808 {
3809#ifdef ARGS_NOEXCEPT
3810 error = Error::Help;
3811 errorMsg = Name();
3812#else
3813 throw Help(Name());
3814#endif
3815 }
3816
3819 bool Get() const noexcept
3820 {
3821 return Matched();
3822 }
3823 };
3824
3827 class CounterFlag : public Flag
3828 {
3829 private:
3830 const int startcount;
3831 int count;
3832
3833 public:
3834 CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
3835 Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
3836
3837 virtual ~CounterFlag() {}
3838
3839 virtual FlagBase *Match(const EitherFlag &arg) override
3840 {
3841 auto me = FlagBase::Match(arg);
3842 if (me)
3843 {
3844#ifdef ARGS_NOEXCEPT
3845 // Suppress increment when FlagBase::Match recorded an
3846 // error on this same call (e.g. Options::Single violated).
3847 // In non-noexcept mode that path would have thrown before
3848 // reaching here and the count would not have advanced.
3849 if (GetError() != Error::None)
3850 {
3851 return me;
3852 }
3853#endif
3854 ++count;
3855 }
3856 return me;
3857 }
3858
3861 int &Get() noexcept
3862 {
3863 return count;
3864 }
3865
3866 int &operator *() noexcept {
3867 return count;
3868 }
3869
3870 const int &operator *() const noexcept {
3871 return count;
3872 }
3873
3874 virtual void Reset() noexcept override
3875 {
3876 FlagBase::Reset();
3877 count = startcount;
3878 }
3879 };
3880
3883 class ActionFlag : public FlagBase
3884 {
3885 private:
3886 std::function<void(const std::vector<std::string> &)> action;
3887 Nargs nargs;
3888
3889 public:
3890 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function<void(const std::vector<std::string> &)> action_, Options options_ = {}):
3891 FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
3892 {
3893 group_.Add(*this);
3894 }
3895
3896 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
3897 FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
3898 {
3899 group_.Add(*this);
3900 action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
3901 }
3902
3903 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
3904 FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
3905 {
3906 group_.Add(*this);
3907 action = [action_](const std::vector<std::string> &) { return action_(); };
3908 }
3909
3910 virtual Nargs NumberOfArguments() const noexcept override
3911 { return nargs; }
3912
3913 virtual void ParseValue(const std::vector<std::string> &value) override
3914 { action(value); }
3915 };
3916
3924 {
3925 private:
3926 template <typename T>
3927 static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type
3928 HasUnsignedNegativeSign(const std::string &value)
3929 {
3930 const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c)
3931 {
3932 return std::isspace(static_cast<unsigned char>(c)) != 0;
3933 });
3934
3935 return firstNonSpace != value.end() && *firstNonSpace == '-';
3936 }
3937
3938 template <typename T>
3939 static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type
3940 HasUnsignedNegativeSign(const std::string &)
3941 {
3942 return false;
3943 }
3944
3945 public:
3946 template <typename T>
3947 typename std::enable_if<
3948 std::is_integral<T>::value &&
3949 !std::is_same<T, bool>::value &&
3950 !std::is_same<T, char>::value &&
3951 !std::is_same<T, signed char>::value &&
3952 !std::is_same<T, unsigned char>::value,
3953 bool>::type
3954 ParseNumericValue(const std::string &value, T &destination)
3955 {
3956 if (HasUnsignedNegativeSign<T>(value))
3957 {
3958 return false;
3959 }
3960
3961 const char *begin = value.c_str();
3962 // The true end of the value, derived from its length rather than
3963 // from the first NUL. strtoull/strtoll treat the buffer as a C
3964 // string and stop at an embedded '\0', so checking `*end == '\0'`
3965 // for "no trailing data" is defeated by a value like "12\0junk":
3966 // end lands on the embedded NUL and the junk after it is silently
3967 // accepted. Comparing against `stop` validates the whole string
3968 // and matches the istringstream-based reader used for other types.
3969 const char *const stop = begin + value.size();
3970
3971 // C++11-compatible: use strtoull/strtoll. Hardening retained from
3972 // the original from_chars draft (errno save/restore, ERANGE check,
3973 // narrowing range check, trailing-whitespace tolerance). No
3974 // unconditional dependency on <charconv> / C++17.
3975 const int saved_errno = errno;
3976 errno = 0;
3977
3978 char *end = nullptr;
3979
3980 if (std::is_unsigned<T>::value)
3981 {
3982 const unsigned long long parsed = std::strtoull(begin, &end, 0);
3983 if (end == begin)
3984 {
3985 errno = saved_errno;
3986 return false;
3987 }
3988 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3989 {
3990 ++end;
3991 }
3992 if (end != stop || errno == ERANGE ||
3993 parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
3994 {
3995 errno = saved_errno;
3996 return false;
3997 }
3998
3999 destination = static_cast<T>(parsed);
4000 }
4001 else
4002 {
4003 const long long parsed = std::strtoll(begin, &end, 0);
4004 if (end == begin)
4005 {
4006 errno = saved_errno;
4007 return false;
4008 }
4009 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
4010 {
4011 ++end;
4012 }
4013 if (end != stop || errno == ERANGE ||
4014 parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
4015 parsed > static_cast<long long>(std::numeric_limits<T>::max()))
4016 {
4017 errno = saved_errno;
4018 return false;
4019 }
4020
4021 destination = static_cast<T>(parsed);
4022 }
4023
4024 errno = saved_errno;
4025 return true;
4026 }
4027
4028 template <typename T>
4029 typename std::enable_if<
4030 !std::is_integral<T>::value ||
4031 std::is_same<T, bool>::value ||
4032 std::is_same<T, char>::value ||
4033 std::is_same<T, signed char>::value ||
4034 std::is_same<T, unsigned char>::value,
4035 bool>::type
4036 ParseNumericValue(const std::string &value, T &destination)
4037 {
4038 std::istringstream ss(value);
4039 // Pin parsing to the C locale so that the decimal separator and
4040 // thousands grouping behavior do not silently depend on whatever
4041 // std::locale::global was last set to elsewhere in the process.
4042 // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in
4043 // any locale whose numpunct facet treats ',' as the decimal point.
4044 ss.imbue(std::locale::classic());
4045 ss >> destination;
4046 if (ss.fail())
4047 {
4048 return false;
4049 }
4050
4051 // Check for trailing garbage by attempting to extract any remaining characters.
4052 // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit
4053 // on EOF, causing false rejection of valid input.
4054 char extra = '\0';
4055 ss >> std::ws >> extra;
4056 // If extraction succeeded, there's trailing garbage (return false).
4057 // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true).
4058 // If extraction failed for other reasons, it's invalid (return false).
4059 if (ss.fail())
4060 {
4061 // Clear the failbit to check if EOF is the only issue
4062 ss.clear(ss.rdstate() & ~std::ios::failbit);
4063 return ss.eof();
4064 }
4065 // Extraction succeeded, meaning there's trailing garbage
4066 return false;
4067 }
4068
4069 template <typename T>
4070 typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
4071 operator ()(const std::string &name, const std::string &value, T &destination)
4072 {
4073 const bool success = ParseNumericValue(value, destination);
4074 if (!success)
4075 {
4076#ifdef ARGS_NOEXCEPT
4077 (void)name;
4078 return false;
4079#else
4080 std::ostringstream problem;
4081 problem << "Argument '" << name << "' received invalid value type '" << value << "'";
4082 throw ParseError(problem.str());
4083#endif
4084 }
4085 return true;
4086 }
4087
4088 template <typename T>
4089 typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
4090 operator()(const std::string &, const std::string &value, T &destination)
4091 {
4092 destination = value;
4093 return true;
4094 }
4095 };
4096
4102 template <
4103 typename T,
4104 typename Reader = ValueReader>
4106 {
4107 protected:
4108 T value;
4109 T defaultValue;
4110
4111 virtual std::string GetDefaultString(const HelpParams&) const override
4112 {
4113 return detail::ToString(defaultValue);
4114 }
4115
4116 private:
4117 Reader reader;
4118
4119 public:
4120
4121 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_)
4122 {
4123 group_.Add(*this);
4124 }
4125
4126 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, extraError_ ? Options::Single : Options::None)
4127 {
4128 }
4129
4130 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
4131 {
4132 }
4133
4134 virtual ~ValueFlag() {}
4135
4136 virtual void ParseValue(const std::vector<std::string> &values_) override
4137 {
4138 const std::string &value_ = values_.at(0);
4139
4140#ifdef ARGS_NOEXCEPT
4141 if (!reader(name, value_, this->value))
4142 {
4143 error = Error::Parse;
4144 }
4145#else
4146 reader(name, value_, this->value);
4147#endif
4148 }
4149
4150 virtual void Reset() noexcept override
4151 {
4152 ValueFlagBase::Reset();
4153 value = defaultValue;
4154 }
4155
4158 T &Get() noexcept
4159 {
4160 return value;
4161 }
4162
4165 T &operator *() noexcept
4166 {
4167 return value;
4168 }
4169
4172 const T &operator *() const noexcept
4173 {
4174 return value;
4175 }
4176
4179 T *operator ->() noexcept
4180 {
4181 return &value;
4182 }
4183
4186 const T *operator ->() const noexcept
4187 {
4188 return &value;
4189 }
4190
4193 const T &GetDefault() noexcept
4194 {
4195 return defaultValue;
4196 }
4197 };
4198
4204 template <
4205 typename T,
4206 typename Reader = ValueReader>
4207 class ImplicitValueFlag : public ValueFlag<T, Reader>
4208 {
4209 protected:
4210 T implicitValue;
4211
4212 public:
4213
4214 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
4215 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
4216 {
4217 }
4218
4219 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
4220 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
4221 {
4222 }
4223
4224 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
4225 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
4226 {
4227 }
4228
4229 virtual ~ImplicitValueFlag() {}
4230
4231 virtual Nargs NumberOfArguments() const noexcept override
4232 {
4233 return {0, 1};
4234 }
4235
4236 virtual void ParseValue(const std::vector<std::string> &value_) override
4237 {
4238 if (value_.empty())
4239 {
4240 this->value = implicitValue;
4241 } else
4242 {
4244 }
4245 }
4246 };
4247
4252 template <typename T>
4253 class ConstantFlag : public Flag
4254 {
4255 T value;
4256
4257 public:
4258
4259 ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_, const T& value_):
4260 Flag(group_, name_, help_, std::move(matcher_), options_),
4261 value(value_)
4262 {}
4263
4264 ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T& value_, bool extraError_ = false):
4265 Flag(group_, name_, help_, std::move(matcher_), extraError_),
4266 value(value_)
4267 {}
4268
4269 T operator * () const noexcept
4270 {
4271 return value;
4272 }
4273
4274 T Get() const noexcept
4275 {
4276 return value;
4277 }
4278
4279 const T *operator -> () const noexcept
4280 {
4281 return &value;
4282 }
4283 };
4284
4291 template <
4292 typename T,
4293 template <typename...> class List = detail::vector,
4294 typename Reader = ValueReader>
4296 {
4297 protected:
4298
4299 List<T> values;
4300 const List<T> defaultValues;
4301 Nargs nargs;
4302 Reader reader;
4303
4304 public:
4305
4306 typedef List<T> Container;
4307 typedef T value_type;
4308 typedef typename Container::allocator_type allocator_type;
4309 typedef typename Container::pointer pointer;
4310 typedef typename Container::const_pointer const_pointer;
4311 typedef T& reference;
4312 typedef const T& const_reference;
4313 typedef typename Container::size_type size_type;
4314 typedef typename Container::difference_type difference_type;
4315 typedef typename Container::iterator iterator;
4316 typedef typename Container::const_iterator const_iterator;
4317 typedef std::reverse_iterator<iterator> reverse_iterator;
4318 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4319
4320 NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
4321 : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
4322 {
4323 group_.Add(*this);
4324 }
4325
4326 virtual ~NargsValueFlag() {}
4327
4328 virtual Nargs NumberOfArguments() const noexcept override
4329 {
4330 return nargs;
4331 }
4332
4333 virtual void ParseValue(const std::vector<std::string> &values_) override
4334 {
4335 values.clear();
4336
4337 for (const std::string &value : values_)
4338 {
4339 T v {};
4340#ifdef ARGS_NOEXCEPT
4341 if (!reader(name, value, v))
4342 {
4343 error = Error::Parse;
4344 return;
4345 }
4346#else
4347 reader(name, value, v);
4348#endif
4349 values.insert(std::end(values), v);
4350 }
4351 }
4352
4353 List<T> &Get() noexcept
4354 {
4355 return values;
4356 }
4357
4360 List<T> &operator *() noexcept
4361 {
4362 return values;
4363 }
4364
4367 const List<T> &operator *() const noexcept
4368 {
4369 return values;
4370 }
4371
4374 List<T> *operator ->() noexcept
4375 {
4376 return &values;
4377 }
4378
4381 const List<T> *operator ->() const noexcept
4382 {
4383 return &values;
4384 }
4385
4386 iterator begin() noexcept
4387 {
4388 return values.begin();
4389 }
4390
4391 const_iterator begin() const noexcept
4392 {
4393 return values.begin();
4394 }
4395
4396 const_iterator cbegin() const noexcept
4397 {
4398 return values.cbegin();
4399 }
4400
4401 iterator end() noexcept
4402 {
4403 return values.end();
4404 }
4405
4406 const_iterator end() const noexcept
4407 {
4408 return values.end();
4409 }
4410
4411 const_iterator cend() const noexcept
4412 {
4413 return values.cend();
4414 }
4415
4416 virtual void Reset() noexcept override
4417 {
4418 FlagBase::Reset();
4419 values = defaultValues;
4420 }
4421
4422 virtual FlagBase *Match(const EitherFlag &arg) override
4423 {
4424 const bool wasMatched = Matched();
4425 auto me = FlagBase::Match(arg);
4426 if (me && !wasMatched)
4427 {
4428 values.clear();
4429 }
4430 return me;
4431 }
4432 };
4433
4440 template <
4441 typename T,
4442 template <typename...> class List = detail::vector,
4443 typename Reader = ValueReader>
4445 {
4446 private:
4447 using Container = List<T>;
4448 Container values;
4449 const Container defaultValues;
4450 Reader reader;
4451
4452 public:
4453
4454 typedef T value_type;
4455 typedef typename Container::allocator_type allocator_type;
4456 typedef typename Container::pointer pointer;
4457 typedef typename Container::const_pointer const_pointer;
4458 typedef T& reference;
4459 typedef const T& const_reference;
4460 typedef typename Container::size_type size_type;
4461 typedef typename Container::difference_type difference_type;
4462 typedef typename Container::iterator iterator;
4463 typedef typename Container::const_iterator const_iterator;
4464 typedef std::reverse_iterator<iterator> reverse_iterator;
4465 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4466
4467 ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
4468 ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
4469 {
4470 group_.Add(*this);
4471 }
4472
4473 virtual ~ValueFlagList() {}
4474
4475 virtual void ParseValue(const std::vector<std::string> &values_) override
4476 {
4477 const std::string &value_ = values_.at(0);
4478
4479 T v{};
4480#ifdef ARGS_NOEXCEPT
4481 if (!reader(name, value_, v))
4482 {
4483 error = Error::Parse;
4484 return;
4485 }
4486#else
4487 reader(name, value_, v);
4488#endif
4489 values.insert(std::end(values), v);
4490 }
4491
4494 Container &Get() noexcept
4495 {
4496 return values;
4497 }
4498
4501 Container &operator *() noexcept
4502 {
4503 return values;
4504 }
4505
4508 const Container &operator *() const noexcept
4509 {
4510 return values;
4511 }
4512
4515 Container *operator ->() noexcept
4516 {
4517 return &values;
4518 }
4519
4522 const Container *operator ->() const noexcept
4523 {
4524 return &values;
4525 }
4526
4527 virtual std::string Name() const override
4528 {
4529 return name + std::string("...");
4530 }
4531
4532 virtual void Reset() noexcept override
4533 {
4534 ValueFlagBase::Reset();
4535 values = defaultValues;
4536 }
4537
4538 virtual FlagBase *Match(const EitherFlag &arg) override
4539 {
4540 const bool wasMatched = Matched();
4541 auto me = FlagBase::Match(arg);
4542 if (me && !wasMatched)
4543 {
4544 values.clear();
4545 }
4546 return me;
4547 }
4548
4549 iterator begin() noexcept
4550 {
4551 return values.begin();
4552 }
4553
4554 const_iterator begin() const noexcept
4555 {
4556 return values.begin();
4557 }
4558
4559 const_iterator cbegin() const noexcept
4560 {
4561 return values.cbegin();
4562 }
4563
4564 iterator end() noexcept
4565 {
4566 return values.end();
4567 }
4568
4569 const_iterator end() const noexcept
4570 {
4571 return values.end();
4572 }
4573
4574 const_iterator cend() const noexcept
4575 {
4576 return values.cend();
4577 }
4578 };
4579
4587 template <
4588 typename K,
4589 typename T,
4590 typename Reader = ValueReader,
4591 template <typename...> class Map = detail::unordered_map>
4592 class MapFlag : public ValueFlagBase
4593 {
4594 private:
4595 const Map<K, T> map;
4596 T value;
4597 const T defaultValue;
4598 Reader reader;
4599
4600 protected:
4601 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4602 {
4603 return detail::MapKeysToStrings(map);
4604 }
4605
4606 public:
4607
4608 MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
4609 {
4610 group_.Add(*this);
4611 }
4612
4613 MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_ = T(), const bool extraError_ = false): MapFlag(group_, name_, help_, std::move(matcher_), map_, defaultValue_, extraError_ ? Options::Single : Options::None)
4614 {
4615 }
4616
4617 MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, Options options_): MapFlag(group_, name_, help_, std::move(matcher_), map_, T(), options_)
4618 {
4619 }
4620
4621 virtual ~MapFlag() {}
4622
4623 virtual void ParseValue(const std::vector<std::string> &values_) override
4624 {
4625 const std::string &value_ = values_.at(0);
4626
4627 K key{};
4628#ifdef ARGS_NOEXCEPT
4629 if (!reader(name, value_, key))
4630 {
4631 error = Error::Parse;
4632 return;
4633 }
4634#else
4635 reader(name, value_, key);
4636#endif
4637 auto it = map.find(key);
4638 if (it == std::end(map))
4639 {
4640 std::ostringstream problem;
4641 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4642#ifdef ARGS_NOEXCEPT
4643 error = Error::Map;
4644 errorMsg = problem.str();
4645#else
4646 throw MapError(problem.str());
4647#endif
4648 } else
4649 {
4650 this->value = it->second;
4651 }
4652 }
4653
4656 T &Get() noexcept
4657 {
4658 return value;
4659 }
4660
4663 T &operator *() noexcept
4664 {
4665 return value;
4666 }
4667
4670 const T &operator *() const noexcept
4671 {
4672 return value;
4673 }
4674
4677 T *operator ->() noexcept
4678 {
4679 return &value;
4680 }
4681
4684 const T *operator ->() const noexcept
4685 {
4686 return &value;
4687 }
4688
4689 virtual void Reset() noexcept override
4690 {
4691 ValueFlagBase::Reset();
4692 value = defaultValue;
4693 }
4694 };
4695
4704 template <
4705 typename K,
4706 typename T,
4707 template <typename...> class List = detail::vector,
4708 typename Reader = ValueReader,
4709 template <typename...> class Map = detail::unordered_map>
4711 {
4712 private:
4713 using Container = List<T>;
4714 const Map<K, T> map;
4715 Container values;
4716 const Container defaultValues;
4717 Reader reader;
4718
4719 protected:
4720 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4721 {
4722 return detail::MapKeysToStrings(map);
4723 }
4724
4725 public:
4726 typedef T value_type;
4727 typedef typename Container::allocator_type allocator_type;
4728 typedef typename Container::pointer pointer;
4729 typedef typename Container::const_pointer const_pointer;
4730 typedef T& reference;
4731 typedef const T& const_reference;
4732 typedef typename Container::size_type size_type;
4733 typedef typename Container::difference_type difference_type;
4734 typedef typename Container::iterator iterator;
4735 typedef typename Container::const_iterator const_iterator;
4736 typedef std::reverse_iterator<iterator> reverse_iterator;
4737 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4738
4739 MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4740 ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4741 {
4742 group_.Add(*this);
4743 }
4744
4745 virtual ~MapFlagList() {}
4746
4747 virtual void ParseValue(const std::vector<std::string> &values_) override
4748 {
4749 const std::string &value_ = values_.at(0);
4750
4751 K key{};
4752#ifdef ARGS_NOEXCEPT
4753 if (!reader(name, value_, key))
4754 {
4755 error = Error::Parse;
4756 return;
4757 }
4758#else
4759 reader(name, value_, key);
4760#endif
4761 auto it = map.find(key);
4762 if (it == std::end(map))
4763 {
4764 std::ostringstream problem;
4765 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4766#ifdef ARGS_NOEXCEPT
4767 error = Error::Map;
4768 errorMsg = problem.str();
4769#else
4770 throw MapError(problem.str());
4771#endif
4772 } else
4773 {
4774 this->values.emplace_back(it->second);
4775 }
4776 }
4777
4780 Container &Get() noexcept
4781 {
4782 return values;
4783 }
4784
4787 Container &operator *() noexcept
4788 {
4789 return values;
4790 }
4791
4794 const Container &operator *() const noexcept
4795 {
4796 return values;
4797 }
4798
4801 Container *operator ->() noexcept
4802 {
4803 return &values;
4804 }
4805
4808 const Container *operator ->() const noexcept
4809 {
4810 return &values;
4811 }
4812
4813 virtual std::string Name() const override
4814 {
4815 return name + std::string("...");
4816 }
4817
4818 virtual void Reset() noexcept override
4819 {
4820 ValueFlagBase::Reset();
4821 values = defaultValues;
4822 }
4823
4824 virtual FlagBase *Match(const EitherFlag &arg) override
4825 {
4826 const bool wasMatched = Matched();
4827 auto me = FlagBase::Match(arg);
4828 if (me && !wasMatched)
4829 {
4830 values.clear();
4831 }
4832 return me;
4833 }
4834
4835 iterator begin() noexcept
4836 {
4837 return values.begin();
4838 }
4839
4840 const_iterator begin() const noexcept
4841 {
4842 return values.begin();
4843 }
4844
4845 const_iterator cbegin() const noexcept
4846 {
4847 return values.cbegin();
4848 }
4849
4850 iterator end() noexcept
4851 {
4852 return values.end();
4853 }
4854
4855 const_iterator end() const noexcept
4856 {
4857 return values.end();
4858 }
4859
4860 const_iterator cend() const noexcept
4861 {
4862 return values.cend();
4863 }
4864 };
4865
4871 template <
4872 typename T,
4873 typename Reader = ValueReader>
4875 {
4876 private:
4877 T value;
4878 const T defaultValue;
4879 Reader reader;
4880 public:
4881 Positional(Group &group_, const std::string &name_, const std::string &help_, const T &defaultValue_ = T(), Options options_ = {}): PositionalBase(name_, help_, options_), value(defaultValue_), defaultValue(defaultValue_)
4882 {
4883 group_.Add(*this);
4884 }
4885
4886 Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
4887 {
4888 }
4889
4890 virtual ~Positional() {}
4891
4892 virtual void ParseValue(const std::string &value_) override
4893 {
4894#ifdef ARGS_NOEXCEPT
4895 if (!reader(name, value_, this->value))
4896 {
4897 error = Error::Parse;
4898 return;
4899 }
4900#else
4901 reader(name, value_, this->value);
4902#endif
4903 ready = false;
4904 matched = true;
4905 }
4906
4909 T &Get() noexcept
4910 {
4911 return value;
4912 }
4913
4916 T &operator *() noexcept
4917 {
4918 return value;
4919 }
4920
4923 const T &operator *() const noexcept
4924 {
4925 return value;
4926 }
4927
4930 T *operator ->() noexcept
4931 {
4932 return &value;
4933 }
4934
4937 const T *operator ->() const noexcept
4938 {
4939 return &value;
4940 }
4941
4942 virtual void Reset() noexcept override
4943 {
4944 PositionalBase::Reset();
4945 value = defaultValue;
4946 }
4947 };
4948
4955 template <
4956 typename T,
4957 template <typename...> class List = detail::vector,
4958 typename Reader = ValueReader>
4960 {
4961 private:
4962 using Container = List<T>;
4963 Container values;
4964 const Container defaultValues;
4965 Reader reader;
4966
4967 public:
4968 typedef T value_type;
4969 typedef typename Container::allocator_type allocator_type;
4970 typedef typename Container::pointer pointer;
4971 typedef typename Container::const_pointer const_pointer;
4972 typedef T& reference;
4973 typedef const T& const_reference;
4974 typedef typename Container::size_type size_type;
4975 typedef typename Container::difference_type difference_type;
4976 typedef typename Container::iterator iterator;
4977 typedef typename Container::const_iterator const_iterator;
4978 typedef std::reverse_iterator<iterator> reverse_iterator;
4979 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4980
4981 PositionalList(Group &group_, const std::string &name_, const std::string &help_, const Container &defaultValues_ = Container(), Options options_ = {}): PositionalBase(name_, help_, options_), values(defaultValues_), defaultValues(defaultValues_)
4982 {
4983 group_.Add(*this);
4984 }
4985
4986 PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
4987 {
4988 }
4989
4990 virtual ~PositionalList() {}
4991
4992 virtual void ParseValue(const std::string &value_) override
4993 {
4994 T v{};
4995#ifdef ARGS_NOEXCEPT
4996 if (!reader(name, value_, v))
4997 {
4998 error = Error::Parse;
4999 return;
5000 }
5001#else
5002 reader(name, value_, v);
5003#endif
5004 values.insert(std::end(values), v);
5005 matched = true;
5006 }
5007
5008 virtual std::string Name() const override
5009 {
5010 return name + std::string("...");
5011 }
5012
5015 Container &Get() noexcept
5016 {
5017 return values;
5018 }
5019
5022 Container &operator *() noexcept
5023 {
5024 return values;
5025 }
5026
5029 const Container &operator *() const noexcept
5030 {
5031 return values;
5032 }
5033
5036 Container *operator ->() noexcept
5037 {
5038 return &values;
5039 }
5040
5043 const Container *operator ->() const noexcept
5044 {
5045 return &values;
5046 }
5047
5048 virtual void Reset() noexcept override
5049 {
5050 PositionalBase::Reset();
5051 values = defaultValues;
5052 }
5053
5054 virtual PositionalBase *GetNextPositional() override
5055 {
5056 const bool wasMatched = Matched();
5057 auto me = PositionalBase::GetNextPositional();
5058 if (me && !wasMatched)
5059 {
5060 values.clear();
5061 }
5062 return me;
5063 }
5064
5065 iterator begin() noexcept
5066 {
5067 return values.begin();
5068 }
5069
5070 const_iterator begin() const noexcept
5071 {
5072 return values.begin();
5073 }
5074
5075 const_iterator cbegin() const noexcept
5076 {
5077 return values.cbegin();
5078 }
5079
5080 iterator end() noexcept
5081 {
5082 return values.end();
5083 }
5084
5085 const_iterator end() const noexcept
5086 {
5087 return values.end();
5088 }
5089
5090 const_iterator cend() const noexcept
5091 {
5092 return values.cend();
5093 }
5094 };
5095
5103 template <
5104 typename K,
5105 typename T,
5106 typename Reader = ValueReader,
5107 template <typename...> class Map = detail::unordered_map>
5109 {
5110 private:
5111 const Map<K, T> map;
5112 T value;
5113 const T defaultValue;
5114 Reader reader;
5115
5116 protected:
5117 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5118 {
5119 return detail::MapKeysToStrings(map);
5120 }
5121
5122 public:
5123
5124 MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
5125 PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
5126 {
5127 group_.Add(*this);
5128 }
5129
5130 virtual ~MapPositional() {}
5131
5132 virtual void ParseValue(const std::string &value_) override
5133 {
5134 K key{};
5135#ifdef ARGS_NOEXCEPT
5136 if (!reader(name, value_, key))
5137 {
5138 error = Error::Parse;
5139 return;
5140 }
5141#else
5142 reader(name, value_, key);
5143#endif
5144 auto it = map.find(key);
5145 if (it == std::end(map))
5146 {
5147 std::ostringstream problem;
5148 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5149#ifdef ARGS_NOEXCEPT
5150 error = Error::Map;
5151 errorMsg = problem.str();
5152#else
5153 throw MapError(problem.str());
5154#endif
5155 } else
5156 {
5157 this->value = it->second;
5158 ready = false;
5159 matched = true;
5160 }
5161 }
5162
5165 T &Get() noexcept
5166 {
5167 return value;
5168 }
5169
5172 T &operator *() noexcept
5173 {
5174 return value;
5175 }
5176
5179 const T &operator *() const noexcept
5180 {
5181 return value;
5182 }
5183
5186 T *operator ->() noexcept
5187 {
5188 return &value;
5189 }
5190
5193 const T *operator ->() const noexcept
5194 {
5195 return &value;
5196 }
5197
5198 virtual void Reset() noexcept override
5199 {
5200 PositionalBase::Reset();
5201 value = defaultValue;
5202 }
5203 };
5204
5213 template <
5214 typename K,
5215 typename T,
5216 template <typename...> class List = detail::vector,
5217 typename Reader = ValueReader,
5218 template <typename...> class Map = detail::unordered_map>
5220 {
5221 private:
5222 using Container = List<T>;
5223
5224 const Map<K, T> map;
5225 Container values;
5226 const Container defaultValues;
5227 Reader reader;
5228
5229 protected:
5230 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5231 {
5232 return detail::MapKeysToStrings(map);
5233 }
5234
5235 public:
5236 typedef T value_type;
5237 typedef typename Container::allocator_type allocator_type;
5238 typedef typename Container::pointer pointer;
5239 typedef typename Container::const_pointer const_pointer;
5240 typedef T& reference;
5241 typedef const T& const_reference;
5242 typedef typename Container::size_type size_type;
5243 typedef typename Container::difference_type difference_type;
5244 typedef typename Container::iterator iterator;
5245 typedef typename Container::const_iterator const_iterator;
5246 typedef std::reverse_iterator<iterator> reverse_iterator;
5247 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
5248
5249 MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
5250 PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
5251 {
5252 group_.Add(*this);
5253 }
5254
5255 virtual ~MapPositionalList() {}
5256
5257 virtual void ParseValue(const std::string &value_) override
5258 {
5259 K key{};
5260#ifdef ARGS_NOEXCEPT
5261 if (!reader(name, value_, key))
5262 {
5263 error = Error::Parse;
5264 return;
5265 }
5266#else
5267 reader(name, value_, key);
5268#endif
5269 auto it = map.find(key);
5270 if (it == std::end(map))
5271 {
5272 std::ostringstream problem;
5273 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5274#ifdef ARGS_NOEXCEPT
5275 error = Error::Map;
5276 errorMsg = problem.str();
5277#else
5278 throw MapError(problem.str());
5279#endif
5280 } else
5281 {
5282 this->values.emplace_back(it->second);
5283 matched = true;
5284 }
5285 }
5286
5289 Container &Get() noexcept
5290 {
5291 return values;
5292 }
5293
5296 Container &operator *() noexcept
5297 {
5298 return values;
5299 }
5300
5303 const Container &operator *() const noexcept
5304 {
5305 return values;
5306 }
5307
5310 Container *operator ->() noexcept
5311 {
5312 return &values;
5313 }
5314
5317 const Container *operator ->() const noexcept
5318 {
5319 return &values;
5320 }
5321
5322 virtual std::string Name() const override
5323 {
5324 return name + std::string("...");
5325 }
5326
5327 virtual void Reset() noexcept override
5328 {
5329 PositionalBase::Reset();
5330 values = defaultValues;
5331 }
5332
5333 virtual PositionalBase *GetNextPositional() override
5334 {
5335 const bool wasMatched = Matched();
5336 auto me = PositionalBase::GetNextPositional();
5337 if (me && !wasMatched)
5338 {
5339 values.clear();
5340 }
5341 return me;
5342 }
5343
5344 iterator begin() noexcept
5345 {
5346 return values.begin();
5347 }
5348
5349 const_iterator begin() const noexcept
5350 {
5351 return values.begin();
5352 }
5353
5354 const_iterator cbegin() const noexcept
5355 {
5356 return values.cbegin();
5357 }
5358
5359 iterator end() noexcept
5360 {
5361 return values.end();
5362 }
5363
5364 const_iterator end() const noexcept
5365 {
5366 return values.end();
5367 }
5368
5369 const_iterator cend() const noexcept
5370 {
5371 return values.cend();
5372 }
5373 };
5374}
5375
5376#pragma pop_macro("min")
5377#pragma pop_macro("max")
5378#endif
A flag class that calls a function when it's matched.
Definition args.hxx:3884
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3910
virtual void ParseValue(const std::vector< std::string > &value) override
Parse values of this option.
Definition args.hxx:3913
The main user facing command line argument parser class.
Definition args.hxx:2707
const std::string & ShortPrefix() const
The prefix for short flags.
Definition args.hxx:3442
void SetArgumentSeparations(const bool allowJoinedShortValue_, const bool allowJoinedLongValue_, const bool allowSeparateShortValue_, const bool allowSeparateLongValue_)
Change allowed option separation.
Definition args.hxx:3508
void Prog(const std::string &prog_)
The program name for help generation.
Definition args.hxx:3425
It ParseArgs(It begin, It end)
Parse all arguments.
Definition args.hxx:3664
const std::string & Prog() const
The program name for help generation.
Definition args.hxx:3421
void LongPrefix(const std::string &longprefix_)
The prefix for long flags.
Definition args.hxx:3434
void LongSeparator(const std::string &longseparator_)
The separator for long flags.
Definition args.hxx:3458
void Help(std::ostream &help_) const
Pass the help menu into an ostream.
Definition args.hxx:3525
const std::string & LongPrefix() const
The prefix for long flags.
Definition args.hxx:3430
const std::string & LongSeparator() const
The separator for long flags.
Definition args.hxx:3454
bool ParseCLI(const int argc, const char *const *argv)
Convenience function to parse the CLI from argc and argv.
Definition args.hxx:3695
const std::string & Terminator() const
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3478
auto ParseArgs(const T &args) -> decltype(std::begin(args))
Parse all arguments.
Definition args.hxx:3684
void Terminator(const std::string &terminator_)
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3482
void GetArgumentSeparations(bool &allowJoinedShortValue_, bool &allowJoinedLongValue_, bool &allowSeparateShortValue_, bool &allowSeparateLongValue_) const
Get the current argument separation parameters.
Definition args.hxx:3489
std::string Help() const
Generate a help menu as a string.
Definition args.hxx:3643
void ShortPrefix(const std::string &shortprefix_)
The prefix for short flags.
Definition args.hxx:3446
std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end, const bool allowSeparate, const bool allowJoined, const bool hasJoined, const std::string &joinedArg, const bool canDiscardJoined, std::vector< std::string > &values)
(INTERNAL) Parse flag's values
Definition args.hxx:2795
Base class for all match types.
Definition args.hxx:1010
void KickOut(bool kickout_) noexcept
Sets a kick-out value for building subparsers.
Definition args.hxx:1110
bool KickOut() const noexcept
Gets the kick-out value for building subparsers.
Definition args.hxx:1123
Main class for building subparsers.
Definition args.hxx:2167
const std::string & Name() const
The name of command.
Definition args.hxx:2302
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:2403
const std::string & ProglinePostfix() const
The description that appears on the prog line after options.
Definition args.hxx:2272
void Description(const std::string &description_)
The description that appears above options.
Definition args.hxx:2287
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:2439
const std::string & Description() const
The description that appears above options.
Definition args.hxx:2282
const std::string & Help() const
The description of command.
Definition args.hxx:2307
void Epilog(const std::string &epilog_)
The description that appears below options.
Definition args.hxx:2297
void ProglinePostfix(const std::string &proglinePostfix_)
The description that appears on the prog line after options.
Definition args.hxx:2277
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:2339
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:2444
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:2508
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:2434
virtual std::vector< std::tuple< std::string, std::string, unsigned > > GetDescription(const HelpParams &params, const unsigned int indent) const override
Get all the child descriptions for help generation.
Definition args.hxx:2533
const std::string & Epilog() const
The description that appears below options.
Definition args.hxx:2292
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:2320
void RequireCommand(bool value)
If value is true, parser will fail if no command was parsed.
Definition args.hxx:2314
Definition args.hxx:1477
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1491
std::string Get() noexcept
Get the completion reply.
Definition args.hxx:1562
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:1496
An exception that contains autocompletion reply.
Definition args.hxx:559
A boolean flag containing a retrievable constant.
Definition args.hxx:4254
A flag class that simply counts the number of times it's matched.
Definition args.hxx:3828
int & Get() noexcept
Get the count.
Definition args.hxx:3861
Base error class.
Definition args.hxx:478
Error that occurs when a singular flag is specified multiple times.
Definition args.hxx:532
Base class for all flag options.
Definition args.hxx:1302
virtual void ParseValue(const std::vector< std::string > &value)=0
Parse values of this option.
virtual Nargs NumberOfArguments() const noexcept=0
Defines how many values can be consumed by this option.
Boolean argument matcher.
Definition args.hxx:3766
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3786
virtual void ParseValue(const std::vector< std::string > &) override
Parse values of this option.
Definition args.hxx:3791
bool Get() const
Get whether this was matched.
Definition args.hxx:3781
Class for using global options in ArgumentParser.
Definition args.hxx:2086
Class for all kinds of validating groups, including ArgumentParser.
Definition args.hxx:1640
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:1820
std::vector< Base * >::size_type MatchedChildren() const
Count the number of matched children this group has.
Definition args.hxx:1827
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:1811
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:1915
Group(Group &group_, const std::string &help_=std::string(), const std::function< bool(const Group &)> &validator_=Validators::DontCare, Options options_={})
If help is empty, this group will not be printed in help output.
Definition args.hxx:1704
void SignalDetectDuplicates()
Sends a signal to the root of the tree to begin checking for duplicates.
Definition args.hxx:1967
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:1802
Group(const std::string &help_=std::string(), const std::function< bool(const Group &)> &validator_=Validators::DontCare, Options options_={})
If help is empty, this group will not be printed in help output.
Definition args.hxx:1699
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:1871
void DetectDuplicateFlags(std::unordered_set< char > &usedShortFlags, std::unordered_set< std::string > &usedLongFlags)
Used by parameterless DetectDuplicateFlags.
Definition args.hxx:1986
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:1751
bool Get() const
Get validation.
Definition args.hxx:1878
const std::vector< Base * > & Children() const
Get all this group's children.
Definition args.hxx:1741
std::vector< Base * > GetMatchedChildren() const
Get the list of children which were matched.
Definition args.hxx:1836
void DetectDuplicateFlags()
Detect duplicate flags.
Definition args.hxx:1977
void Add(Base &child)
Append a child to this Group.
Definition args.hxx:1713
std::vector< ChildType * > GetFilteredChildren(bool matching=false) const
Gets the children which are a certain type.
Definition args.hxx:1852
virtual std::vector< std::tuple< std::string, std::string, unsigned > > GetDescription(const HelpParams &params, const unsigned int indent) const override
Get all the child descriptions for help generation.
Definition args.hxx:1885
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:1786
Help flag class.
Definition args.hxx:3801
bool Get() const noexcept
Get whether this was matched.
Definition args.hxx:3819
virtual void ParseValue(const std::vector< std::string > &)
Parse values of this option.
Definition args.hxx:3807
An exception that indicates that the user has requested help.
Definition args.hxx:541
An optional argument-accepting flag class.
Definition args.hxx:4208
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:4236
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4231
Errors in map lookups.
Definition args.hxx:523
A mapping value flag list class.
Definition args.hxx:4711
Container * operator->() noexcept
Get the values.
Definition args.hxx:4801
Container & Get() noexcept
Get the value.
Definition args.hxx:4780
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4747
Container & operator*() noexcept
Get the value.
Definition args.hxx:4787
A mapping value flag class.
Definition args.hxx:4593
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4623
T & Get() noexcept
Get the value.
Definition args.hxx:4656
T * operator->() noexcept
Get the value.
Definition args.hxx:4677
T & operator*() noexcept
Get the value.
Definition args.hxx:4663
A positional argument mapping list class.
Definition args.hxx:5220
Container & operator*() noexcept
Get the value.
Definition args.hxx:5296
Container * operator->() noexcept
Get the values.
Definition args.hxx:5310
Container & Get() noexcept
Get the value.
Definition args.hxx:5289
A positional argument mapping class.
Definition args.hxx:5109
T * operator->() noexcept
Get the value.
Definition args.hxx:5186
T & Get() noexcept
Get the value.
Definition args.hxx:5165
T & operator*() noexcept
Get the value.
Definition args.hxx:5172
A class of "matchers", specifying short and flags that can possibly be matched.
Definition args.hxx:627
EitherFlag GetShortOrAny() const
(INTERNAL) Get short flag if it exists or any long flag
Definition args.hxx:745
std::vector< EitherFlag > GetFlagStrings() const
(INTERNAL) Get all flag strings as a vector, with the prefixes embedded
Definition args.hxx:710
Matcher(Short &&shortIn, Long &&longIn)
Specify short and long flags separately as iterables.
Definition args.hxx:663
Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd)
Specify short and long flags separately as iterators.
Definition args.hxx:638
bool Match(const std::string &flag) const
(INTERNAL) Check if there is a match of a long flag
Definition args.hxx:696
EitherFlag GetLongOrAny() const
(INTERNAL) Get long flag if it exists or any short flag
Definition args.hxx:727
bool Match(const char flag) const
(INTERNAL) Check if there is a match of a short flag
Definition args.hxx:689
bool Match(const EitherFlag &flag) const
(INTERNAL) Check if there is a match of a flag
Definition args.hxx:703
Matcher(std::initializer_list< EitherFlag > in)
Specify a mixed single initializer-list of both short and long flags.
Definition args.hxx:679
Base class for all match types that have a name.
Definition args.hxx:1155
void HelpDefault(const std::string &str)
Sets default value string that will be added to argument description.
Definition args.hxx:1191
void HelpChoices(const std::vector< std::string > &array)
Sets choices strings that will be added to argument description.
Definition args.hxx:1207
std::string HelpDefault(const HelpParams &params) const
Gets default value string that will be added to argument description.
Definition args.hxx:1199
std::vector< std::string > HelpChoices(const HelpParams &params) const
Gets choices strings that will be added to argument description.
Definition args.hxx:1215
A variadic arguments accepting flag class.
Definition args.hxx:4296
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4328
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4333
List< T > * operator->() noexcept
Get the values.
Definition args.hxx:4374
List< T > & operator*() noexcept
Get the value.
Definition args.hxx:4360
Errors that occur during regular parsing.
Definition args.hxx:496
Base class for positional options.
Definition args.hxx:1580
A positional argument class that pushes the found values into a list.
Definition args.hxx:4960
Container & operator*() noexcept
Get the value.
Definition args.hxx:5022
Container & Get() noexcept
Get the values.
Definition args.hxx:5015
Container * operator->() noexcept
Get the values.
Definition args.hxx:5036
A positional argument class.
Definition args.hxx:4875
T & operator*() noexcept
Get the value.
Definition args.hxx:4916
T & Get() noexcept
Get the value.
Definition args.hxx:4909
T * operator->() noexcept
Get the value.
Definition args.hxx:4930
Errors that when a required flag is omitted.
Definition args.hxx:514
Utility class for building subparsers with coroutines/callbacks.
Definition args.hxx:2112
void Parse()
Continue parsing arguments for new command.
Definition args.hxx:3729
const std::vector< std::string > & KickedOut() const noexcept
Returns a vector of kicked out arguments.
Definition args.hxx:2156
bool IsParsed() const
(INTERNAL) Determines whether Parse was called or not.
Definition args.hxx:2143
Errors that occur during usage.
Definition args.hxx:487
Errors that are detected from group validation after parsing finishes.
Definition args.hxx:505
Base class for value-accepting flag options.
Definition args.hxx:1464
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1470
An argument-accepting flag class that pushes the found values into a list.
Definition args.hxx:4445
Container * operator->() noexcept
Get the values.
Definition args.hxx:4515
Container & Get() noexcept
Get the values.
Definition args.hxx:4494
Container & operator*() noexcept
Get the value.
Definition args.hxx:4501
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4475
An argument-accepting flag class.
Definition args.hxx:4106
T & operator*() noexcept
Get the value.
Definition args.hxx:4165
T * operator->() noexcept
Get the value.
Definition args.hxx:4179
T & Get() noexcept
Get the value.
Definition args.hxx:4158
const T & GetDefault() noexcept
Get the default value.
Definition args.hxx:4193
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4136
Definition args.hxx:1250
contains all the functionality of the args library
std::vector< std::string > Wrap(It begin, It end, const std::string::size_type width, std::string::size_type firstlinewidth=0, std::string::size_type firstlineindent=0)
(INTERNAL) Wrap a vector of words into a vector of lines
Definition args.hxx:277
bool SafeAdd(T a, T b, T &out) noexcept
Safe addition to prevent integer overflow.
Definition args.hxx:108
bool SafeMultiply(T a, T b, T &out) noexcept
Safe multiplication to prevent integer overflow.
Definition args.hxx:148
bool SafeSub(T a, T b, T &out) noexcept
Safe subtraction to prevent integer underflow.
Definition args.hxx:202
Options
Attributes for flags.
Definition args.hxx:765
@ HiddenFromCompletion
Flag is excluded from auto completion.
@ Global
Flag is global and can be used in any subcommand.
@ Single
Flag can't be passed multiple times.
@ None
Default options.
@ HiddenFromUsage
Flag is excluded from usage line.
@ Hidden
Flag is excluded from options help and usage line.
@ Required
Flag can't be omitted.
@ KickOut
Flag stops a parser.
@ HiddenFromDescription
Flag is excluded from options help.
std::enable_if< std::is_unsigned< T >::value, bool >::type SafeNeg(T a, T &out) noexcept
Safe negation to prevent integer overflow.
Definition args.hxx:240
auto get(Option &option_) -> decltype(option_.Get())
Getter to grab the value from the argument type.
Definition args.hxx:78
std::string::size_type Glyphs(const std::string &string_)
(INTERNAL) Count UTF-8 glyphs
Definition args.hxx:91
A simple unified option type for unified initializer lists for the Matcher class.
Definition args.hxx:569
static std::unordered_set< char > GetShort(std::initializer_list< EitherFlag > flags)
Get just the short flags from an initializer list of EitherFlags.
Definition args.hxx:594
static std::unordered_set< std::string > GetLong(std::initializer_list< EitherFlag > flags)
Get just the long flags from an initializer list of EitherFlags.
Definition args.hxx:579
Default validators.
Definition args.hxx:1650
A simple structure of parameters for easy user-modifyable help menus.
Definition args.hxx:821
std::string programName
The program name for help generation.
Definition args.hxx:878
bool showCommandFullHelp
Show command's descriptions and epilog.
Definition args.hxx:886
bool proglineShowFlags
Show flags in program line.
Definition args.hxx:922
std::string usageString
Program line prefix.
Definition args.hxx:930
unsigned int width
The width of the help menu.
Definition args.hxx:824
std::string proglineNonrequiredClose
The postfix for progline non-required argument.
Definition args.hxx:918
unsigned int helpindent
The indent of the flag descriptions.
Definition args.hxx:839
bool proglinePreferShortFlags
Use short flags in program lines when possible.
Definition args.hxx:926
std::string proglineCommand
The prefix for progline when command has any subcommands.
Definition args.hxx:894
std::string proglineOptions
The postfix for progline when showProglineOptions is true and command has any flags.
Definition args.hxx:890
std::string longSeparator
The separator for long flags.
Definition args.hxx:874
bool showValueName
Show value name.
Definition args.hxx:942
bool showTerminator
Show the terminator when both options and positional parameters are present.
Definition args.hxx:850
std::string proglineValueOpen
The prefix for progline value.
Definition args.hxx:898
std::string valueOpen
The prefix for option value.
Definition args.hxx:950
unsigned int progtailindent
The indent of the program trailing lines for long parameters.
Definition args.hxx:830
std::string proglineNonrequiredOpen
The prefix for progline non-required argument.
Definition args.hxx:914
unsigned int descriptionindent
The indent of the description and epilogs.
Definition args.hxx:833
bool showCommandChildren
Show command's flags.
Definition args.hxx:882
bool showProglineOptions
Show the {OPTIONS} on the prog line when this is true.
Definition args.hxx:854
std::string valueClose
The postfix for option value.
Definition args.hxx:954
std::string longPrefix
The prefix for long flags.
Definition args.hxx:866
std::string proglineRequiredClose
The postfix for progline required argument.
Definition args.hxx:910
unsigned int flagindent
The indent of the flags.
Definition args.hxx:836
std::string optionsString
String shown in help before flags descriptions.
Definition args.hxx:934
bool addNewlineBeforeDescription
Add newline before flag description.
Definition args.hxx:946
std::string shortPrefix
The prefix for short flags.
Definition args.hxx:862
bool addDefault
Add default values to argument description.
Definition args.hxx:966
unsigned int gutter
The minimum gutter between each flag and its help.
Definition args.hxx:846
bool showProglinePositionals
Show the positionals on the prog line when this is true.
Definition args.hxx:858
std::string shortSeparator
The separator for short flags.
Definition args.hxx:870
std::string proglineValueClose
The postfix for progline value.
Definition args.hxx:902
bool useValueNameOnce
Display value name after all the long and short flags.
Definition args.hxx:938
std::string defaultString
The prefix for default values.
Definition args.hxx:970
std::string proglineRequiredOpen
The prefix for progline required argument.
Definition args.hxx:906
unsigned int eachgroupindent
The additional indent each group adds.
Definition args.hxx:842
unsigned int progindent
The indent of the program line.
Definition args.hxx:827
bool addChoices
Add choices to argument description.
Definition args.hxx:958
std::string choiceString
The prefix for choices.
Definition args.hxx:962
A number of arguments which can be consumed by an option.
Definition args.hxx:978
A default Reader class for argument classes.
Definition args.hxx:3924