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 SignalDetectDuplicates();
1719 }
1720 }
1721
1724 const std::vector<Base *> &Children() const
1725 {
1726 return children;
1727 }
1728
1734 virtual FlagBase *Match(const EitherFlag &flag) override
1735 {
1736 for (Base *child: Children())
1737 {
1738 if (FlagBase *match = child->Match(flag))
1739 {
1740 return match;
1741 }
1742 }
1743 return nullptr;
1744 }
1745
1746 virtual std::vector<FlagBase*> GetAllFlags() override
1747 {
1748 std::vector<FlagBase*> res;
1749 for (Base *child: Children())
1750 {
1751 auto childRes = child->GetAllFlags();
1752 res.insert(res.end(), childRes.begin(), childRes.end());
1753 }
1754 return res;
1755 }
1756
1757 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1758 {
1759 for (Base *child: Children())
1760 {
1761 child->Validate(shortPrefix, longPrefix);
1762 }
1763 }
1764
1770 {
1771 for (Base *child: Children())
1772 {
1773 if (auto next = child->GetNextPositional())
1774 {
1775 return next;
1776 }
1777 }
1778 return nullptr;
1779 }
1780
1785 virtual bool HasFlag() const override
1786 {
1787 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
1788 }
1789
1794 virtual bool HasPositional() const override
1795 {
1796 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
1797 }
1798
1803 virtual bool HasCommand() const override
1804 {
1805 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
1806 }
1807
1810 std::vector<Base *>::size_type MatchedChildren() const
1811 {
1812 // Cast to avoid warnings from -Wsign-conversion
1813 return static_cast<std::vector<Base *>::size_type>(
1814 std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
1815 }
1816
1819 std::vector<Base *> GetMatchedChildren() const
1820 {
1821 // Could be replaced by C++ 20 filter, or a custom iterator.
1822 std::vector<Base*> matched_children;
1823 std::copy_if(children.begin(), children.end(), std::back_inserter(matched_children), [](Base* b){
1824 return b->Matched();
1825 });
1826 return matched_children;
1827 }
1828
1834 template <typename ChildType>
1835 std::vector<ChildType *> GetFilteredChildren(bool matching = false) const
1836 {
1837 std::vector<ChildType *> filtered_children;
1838 for(Base *child : children) {
1839 if(!matching || child->Matched())
1840 {
1841 ChildType* cast_result = dynamic_cast<ChildType*>(child);
1842 if(cast_result != nullptr)
1843 {
1844 filtered_children.push_back(cast_result);
1845 }
1846
1847 }
1848 }
1849 return filtered_children;
1850 }
1851
1854 virtual bool Matched() const noexcept override
1855 {
1856 return validator(*this);
1857 }
1858
1861 bool Get() const
1862 {
1863 return Matched();
1864 }
1865
1868 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
1869 {
1870 std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
1871
1872 // Push that group description on the back if not empty
1873 unsigned addindent = 0;
1874 if (!help.empty())
1875 {
1876 descriptions.emplace_back(help, "", indent);
1877 addindent = 1;
1878 }
1879
1880 for (Base *child: Children())
1881 {
1882 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
1883 {
1884 continue;
1885 }
1886
1887 auto groupDescriptions = child->GetDescription(params, indent + addindent);
1888 descriptions.insert(
1889 std::end(descriptions),
1890 std::make_move_iterator(std::begin(groupDescriptions)),
1891 std::make_move_iterator(std::end(groupDescriptions)));
1892 }
1893 return descriptions;
1894 }
1895
1898 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1899 {
1900 std::vector <std::string> names;
1901 for (Base *child: Children())
1902 {
1903 if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
1904 {
1905 continue;
1906 }
1907
1908 auto groupNames = child->GetProgramLine(params);
1909 names.insert(
1910 std::end(names),
1911 std::make_move_iterator(std::begin(groupNames)),
1912 std::make_move_iterator(std::end(groupNames)));
1913 }
1914 return names;
1915 }
1916
1917 virtual std::vector<Command*> GetCommands() override
1918 {
1919 std::vector<Command*> res;
1920 for (const auto &child : Children())
1921 {
1922 auto subparsers = child->GetCommands();
1923 res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
1924 }
1925 return res;
1926 }
1927
1928 virtual bool IsGroup() const override
1929 {
1930 return true;
1931 }
1932
1933 virtual void Reset() noexcept override
1934 {
1935 Base::Reset();
1936
1937 for (auto &child: Children())
1938 {
1939 child->Reset();
1940 }
1941#ifdef ARGS_NOEXCEPT
1942 error = Error::None;
1943 errorMsg.clear();
1944#endif
1945 }
1946
1951 {
1952 if(parent != nullptr) parent->SignalDetectDuplicates();
1953 else DetectDuplicateFlags();
1954 }
1955
1961 {
1962 std::unordered_set<char> usedShortFlags;
1963 std::unordered_set<std::string> usedLongFlags;
1964 DetectDuplicateFlags(usedShortFlags, usedLongFlags);
1965 }
1966
1969 void DetectDuplicateFlags(std::unordered_set<char> &usedShortFlags, std::unordered_set<std::string> &usedLongFlags)
1970 {
1971 for (Base *child: Children())
1972 {
1973 if(auto flag = dynamic_cast<FlagBase*>(child))
1974 {
1975 // Check for duplicate flags, setting a usage error on the
1976 // flag if a duplicate is detected.
1977 for(EitherFlag flagString: flag->GetMatcher().GetFlagStrings())
1978 {
1979 if(flagString.isShort)
1980 {
1981 if(usedShortFlags.count(flagString.shortFlag))
1982 {
1983#ifdef ARGS_NOEXCEPT
1984 flag->SetUsageError();
1985#else
1986 throw ParseError("duplicate short flag detected");
1987#endif
1988 }
1989 else
1990 {
1991 usedShortFlags.insert(flagString.shortFlag);
1992 }
1993 }
1994 else
1995 {
1996 if(usedLongFlags.count(flagString.longFlag))
1997 {
1998#ifdef ARGS_NOEXCEPT
1999 flag->SetUsageError();
2000#else
2001 throw ParseError("duplicate long flag detected");
2002#endif
2003 }
2004 else
2005 {
2006 usedLongFlags.insert(flagString.longFlag);
2007 }
2008 }
2009 }
2010 }
2011 else if(auto group = dynamic_cast<Group*>(child))
2012 {
2013 // A command opens its own flag namespace and runs its
2014 // own duplicate detection as a separate root, so a flag
2015 // reused either side of a command boundary is not a
2016 // genuine duplicate. Only descend into plain groups
2017 // here; IsGroup() is false for a Command.
2018 if(group->IsGroup())
2019 {
2020 group->DetectDuplicateFlags(usedShortFlags, usedLongFlags);
2021 }
2022 }
2023 }
2024 }
2025
2026#ifdef ARGS_NOEXCEPT
2028 virtual Error GetError() const override
2029 {
2030 if (error != Error::None)
2031 {
2032 return error;
2033 }
2034
2035 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2036 if (it == Children().end())
2037 {
2038 return Error::None;
2039 } else
2040 {
2041 return (*it)->GetError();
2042 }
2043 }
2044
2046 virtual std::string GetErrorMsg() const override
2047 {
2048 if (error != Error::None)
2049 {
2050 return errorMsg;
2051 }
2052
2053 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2054 if (it == Children().end())
2055 {
2056 return "";
2057 } else
2058 {
2059 return (*it)->GetErrorMsg();
2060 }
2061 }
2062#endif
2063
2064 };
2065
2068 class GlobalOptions : public Group
2069 {
2070 public:
2071 GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
2072 {
2073 Add(options_);
2074 }
2075 };
2076
2094 class Subparser : public Group
2095 {
2096 private:
2097 std::vector<std::string> args;
2098 std::vector<std::string> kicked;
2099 ArgumentParser *parser = nullptr;
2100 const HelpParams &helpParams;
2101 const Command &command;
2102 bool isParsed = false;
2103
2104 public:
2105 Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
2106 : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
2107 {
2108 }
2109
2110 Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
2111 {
2112 }
2113
2114 Subparser(const Subparser&) = delete;
2115 Subparser(Subparser&&) = delete;
2116 Subparser &operator = (const Subparser&) = delete;
2117 Subparser &operator = (Subparser&&) = delete;
2118
2119 const Command &GetCommand()
2120 {
2121 return command;
2122 }
2123
2126 bool IsParsed() const
2127 {
2128 return isParsed;
2129 }
2130
2133 void Parse();
2134
2139 const std::vector<std::string> &KickedOut() const noexcept
2140 {
2141 return kicked;
2142 }
2143 };
2144
2149 class Command : public Group
2150 {
2151 private:
2152 friend class Subparser;
2153
2154 std::string name;
2155 std::string help;
2156 std::string description;
2157 std::string epilog;
2158 std::string proglinePostfix;
2159
2160 std::function<void(Subparser&)> parserCoroutine;
2161 bool commandIsRequired = true;
2162 Command *selectedCommand = nullptr;
2163
2164 mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
2165 mutable std::vector<std::string> subparserProgramLine;
2166 mutable bool subparserHasFlag = false;
2167 mutable bool subparserHasPositional = false;
2168 mutable bool subparserHasCommand = false;
2169#ifdef ARGS_NOEXCEPT
2170 mutable Error subparserError = Error::None;
2171#endif
2172 mutable Subparser *subparser = nullptr;
2173
2174 protected:
2175
2176 class RaiiSubparser
2177 {
2178 public:
2179 RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
2180 RaiiSubparser(const Command &command_, const HelpParams &params_);
2181
2182 ~RaiiSubparser()
2183 {
2184 command.subparser = oldSubparser;
2185 }
2186
2187 Subparser &Parser()
2188 {
2189 return parser;
2190 }
2191
2192 private:
2193 const Command &command;
2194 Subparser parser;
2195 Subparser *oldSubparser;
2196 };
2197
2198 Command() = default;
2199
2200 std::function<void(Subparser&)> &GetCoroutine()
2201 {
2202 return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
2203 }
2204
2205 Command &SelectedCommand()
2206 {
2207 Command *res = this;
2208 while (res->selectedCommand != nullptr)
2209 {
2210 res = res->selectedCommand;
2211 }
2212
2213 return *res;
2214 }
2215
2216 const Command &SelectedCommand() const
2217 {
2218 const Command *res = this;
2219 while (res->selectedCommand != nullptr)
2220 {
2221 res = res->selectedCommand;
2222 }
2223
2224 return *res;
2225 }
2226
2227 void UpdateSubparserHelp(const HelpParams &params) const
2228 {
2229 if (parserCoroutine)
2230 {
2231 RaiiSubparser coro(*this, params);
2232#ifndef ARGS_NOEXCEPT
2233 try
2234 {
2235 parserCoroutine(coro.Parser());
2236 }
2237 catch (args::SubparserError&)
2238 {
2239 }
2240#else
2241 parserCoroutine(coro.Parser());
2242#endif
2243 }
2244 }
2245
2246 public:
2247 Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
2248 : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
2249 {
2250 base_.Add(*this);
2251 }
2252
2255 const std::string &ProglinePostfix() const
2256 { return proglinePostfix; }
2257
2260 void ProglinePostfix(const std::string &proglinePostfix_)
2261 { this->proglinePostfix = proglinePostfix_; }
2262
2265 const std::string &Description() const
2266 { return description; }
2270 void Description(const std::string &description_)
2271 { this->description = description_; }
2272
2275 const std::string &Epilog() const
2276 { return epilog; }
2277
2280 void Epilog(const std::string &epilog_)
2281 { this->epilog = epilog_; }
2282
2285 const std::string &Name() const
2286 { return name; }
2287
2290 const std::string &Help() const
2291 { return help; }
2292
2297 void RequireCommand(bool value)
2298 { commandIsRequired = value; }
2299
2300 virtual bool IsGroup() const override
2301 { return false; }
2302
2303 virtual bool Matched() const noexcept override
2304 { return Base::Matched(); }
2305
2306 operator bool() const noexcept
2307 { return Matched(); }
2308
2309 void Match() noexcept
2310 { matched = true; }
2311
2312 void SelectCommand(Command *c) noexcept
2313 {
2314 selectedCommand = c;
2315
2316 if (c != nullptr)
2317 {
2318 c->Match();
2319 }
2320 }
2321
2322 virtual FlagBase *Match(const EitherFlag &flag) override
2323 {
2324 if (selectedCommand != nullptr)
2325 {
2326 if (auto *res = selectedCommand->Match(flag))
2327 {
2328 return res;
2329 }
2330
2331 for (auto *child: Children())
2332 {
2333 if ((child->GetOptions() & Options::Global) != Options::None)
2334 {
2335 if (auto *res = child->Match(flag))
2336 {
2337 return res;
2338 }
2339 }
2340 }
2341
2342 return nullptr;
2343 }
2344
2345 if (subparser != nullptr)
2346 {
2347 return subparser->Match(flag);
2348 }
2349
2350 return Matched() ? Group::Match(flag) : nullptr;
2351 }
2352
2353 virtual std::vector<FlagBase*> GetAllFlags() override
2354 {
2355 std::vector<FlagBase*> res;
2356
2357 if (!Matched())
2358 {
2359 return res;
2360 }
2361
2362 for (auto *child: Children())
2363 {
2364 if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
2365 {
2366 auto childFlags = child->GetAllFlags();
2367 res.insert(res.end(), childFlags.begin(), childFlags.end());
2368 }
2369 }
2370
2371 if (selectedCommand != nullptr)
2372 {
2373 auto childFlags = selectedCommand->GetAllFlags();
2374 res.insert(res.end(), childFlags.begin(), childFlags.end());
2375 }
2376
2377 if (subparser != nullptr)
2378 {
2379 auto childFlags = subparser->GetAllFlags();
2380 res.insert(res.end(), childFlags.begin(), childFlags.end());
2381 }
2382
2383 return res;
2384 }
2385
2387 {
2388 if (selectedCommand != nullptr)
2389 {
2390 if (auto *res = selectedCommand->GetNextPositional())
2391 {
2392 return res;
2393 }
2394
2395 for (auto *child: Children())
2396 {
2397 if ((child->GetOptions() & Options::Global) != Options::None)
2398 {
2399 if (auto *res = child->GetNextPositional())
2400 {
2401 return res;
2402 }
2403 }
2404 }
2405
2406 return nullptr;
2407 }
2408
2409 if (subparser != nullptr)
2410 {
2411 return subparser->GetNextPositional();
2412 }
2413
2414 return Matched() ? Group::GetNextPositional() : nullptr;
2415 }
2416
2417 virtual bool HasFlag() const override
2418 {
2419 return subparserHasFlag || Group::HasFlag();
2420 }
2421
2422 virtual bool HasPositional() const override
2423 {
2424 return subparserHasPositional || Group::HasPositional();
2425 }
2426
2427 virtual bool HasCommand() const override
2428 {
2429 return true;
2430 }
2431
2432 std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
2433 {
2434 UpdateSubparserHelp(params);
2435
2436 std::vector<std::string> res;
2437
2438 if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
2439 {
2440 res.push_back(params.proglineOptions);
2441 }
2442
2443 auto group_res = Group::GetProgramLine(params);
2444 std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res));
2445
2446 res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
2447
2448 if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
2449 {
2450 res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
2451 }
2452
2453 if (!Name().empty())
2454 {
2455 res.insert(res.begin(), Name());
2456 }
2457
2458 if (!ProglinePostfix().empty())
2459 {
2460 std::string line;
2461 for (auto c : ProglinePostfix())
2462 {
2463 if (std::isspace(static_cast<unsigned char>(c)))
2464 {
2465 if (!line.empty())
2466 {
2467 res.push_back(line);
2468 line.clear();
2469 }
2470
2471 if (c == '\n')
2472 {
2473 res.push_back("\n");
2474 }
2475 }
2476 else
2477 {
2478 line += c;
2479 }
2480 }
2481
2482 if (!line.empty())
2483 {
2484 res.push_back(line);
2485 }
2486 }
2487
2488 return res;
2489 }
2490
2491 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
2492 {
2493 if (!Matched())
2494 {
2495 return {};
2496 }
2497
2498 return GetCommandProgramLine(params);
2499 }
2500
2501 virtual std::vector<Command*> GetCommands() override
2502 {
2503 if (selectedCommand != nullptr)
2504 {
2505 return selectedCommand->GetCommands();
2506 }
2507
2508 if (Matched())
2509 {
2510 return Group::GetCommands();
2511 }
2512
2513 return { this };
2514 }
2515
2516 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
2517 {
2518 std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
2519 unsigned addindent = 0;
2520
2521 UpdateSubparserHelp(params);
2522
2523 if (!Matched())
2524 {
2525 if (params.showCommandFullHelp)
2526 {
2527 std::ostringstream s;
2528 bool empty = true;
2529 for (const auto &progline: GetCommandProgramLine(params))
2530 {
2531 if (!empty)
2532 {
2533 s << ' ';
2534 }
2535 else
2536 {
2537 empty = false;
2538 }
2539
2540 s << progline;
2541 }
2542
2543 descriptions.emplace_back(s.str(), "", indent);
2544 }
2545 else
2546 {
2547 descriptions.emplace_back(Name(), help, indent);
2548 }
2549
2550 if (!params.showCommandChildren && !params.showCommandFullHelp)
2551 {
2552 return descriptions;
2553 }
2554
2555 addindent = 1;
2556 }
2557
2558 if (params.showCommandFullHelp && !Matched())
2559 {
2560 descriptions.emplace_back("", "", indent + addindent);
2561 descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
2562 descriptions.emplace_back("", "", indent + addindent);
2563 }
2564
2565 for (Base *child: Children())
2566 {
2567 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
2568 {
2569 continue;
2570 }
2571
2572 auto groupDescriptions = child->GetDescription(params, indent + addindent);
2573 descriptions.insert(
2574 std::end(descriptions),
2575 std::make_move_iterator(std::begin(groupDescriptions)),
2576 std::make_move_iterator(std::end(groupDescriptions)));
2577 }
2578
2579 for (auto childDescription: subparserDescription)
2580 {
2581 std::get<2>(childDescription) += indent + addindent;
2582 descriptions.push_back(std::move(childDescription));
2583 }
2584
2585 if (params.showCommandFullHelp && !Matched())
2586 {
2587 descriptions.emplace_back("", "", indent + addindent);
2588 if (!Epilog().empty())
2589 {
2590 descriptions.emplace_back(Epilog(), "", indent + addindent);
2591 descriptions.emplace_back("", "", indent + addindent);
2592 }
2593 }
2594
2595 return descriptions;
2596 }
2597
2598 virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
2599 {
2600 if (!Matched())
2601 {
2602 return;
2603 }
2604
2605 auto onValidationError = [&]
2606 {
2607 std::ostringstream problem;
2608 problem << "Group validation failed somewhere!";
2609#ifdef ARGS_NOEXCEPT
2610 error = Error::Validation;
2611 errorMsg = problem.str();
2612#else
2613 throw ValidationError(problem.str());
2614#endif
2615 };
2616
2617 for (Base *child: Children())
2618 {
2619 if (child->IsGroup() && !child->Matched())
2620 {
2621 onValidationError();
2622 }
2623
2624 child->Validate(shortprefix, longprefix);
2625 }
2626
2627 if (subparser != nullptr)
2628 {
2629 subparser->Validate(shortprefix, longprefix);
2630 if (!subparser->Matched())
2631 {
2632 onValidationError();
2633 }
2634 }
2635
2636 if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
2637 {
2638 std::ostringstream problem;
2639 problem << "Command is required";
2640#ifdef ARGS_NOEXCEPT
2641 error = Error::Validation;
2642 errorMsg = problem.str();
2643#else
2644 throw ValidationError(problem.str());
2645#endif
2646 }
2647 }
2648
2649 virtual void Reset() noexcept override
2650 {
2651 Group::Reset();
2652 selectedCommand = nullptr;
2653 subparserProgramLine.clear();
2654 subparserDescription.clear();
2655 subparserHasFlag = false;
2656 subparserHasPositional = false;
2657 subparserHasCommand = false;
2658#ifdef ARGS_NOEXCEPT
2659 subparserError = Error::None;
2660#endif
2661 }
2662
2663#ifdef ARGS_NOEXCEPT
2665 virtual Error GetError() const override
2666 {
2667 if (!Matched())
2668 {
2669 return Error::None;
2670 }
2671
2672 if (error != Error::None)
2673 {
2674 return error;
2675 }
2676
2677 if (subparserError != Error::None)
2678 {
2679 return subparserError;
2680 }
2681
2682 return Group::GetError();
2683 }
2684#endif
2685 };
2686
2690 {
2691 friend class Subparser;
2692
2693 private:
2694 std::string longprefix;
2695 std::string shortprefix;
2696
2697 std::string longseparator;
2698
2699 std::string terminator;
2700
2701 bool allowJoinedShortValue = true;
2702 bool allowJoinedLongValue = true;
2703 bool allowSeparateShortValue = true;
2704 bool allowSeparateLongValue = true;
2705
2706 bool readCompletion = false;
2707 CompletionFlag *completion = nullptr;
2708
2709 protected:
2710 enum class OptionType
2711 {
2712 LongFlag,
2713 ShortFlag,
2715 };
2716
2717 OptionType ParseOption(const std::string &s, bool allowEmpty = false)
2718 {
2719 const bool matchesLong = s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length());
2720 const bool matchesShort = s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length());
2721
2722 // A chunk can start with both prefixes when one is a prefix of
2723 // the other, or when the long prefix is empty (every string
2724 // starts with it). Resolve to the longer, more specific prefix:
2725 // this keeps the default "--"/"-" preference for long flags
2726 // while letting a short flag be recognised under an empty long
2727 // prefix instead of being swallowed as a nameless long flag.
2728 if (matchesLong && matchesShort)
2729 {
2730 return longprefix.length() >= shortprefix.length() ? OptionType::LongFlag : OptionType::ShortFlag;
2731 }
2732
2733 if (matchesLong)
2734 {
2735 return OptionType::LongFlag;
2736 }
2737
2738 if (matchesShort)
2739 {
2740 return OptionType::ShortFlag;
2741 }
2742
2743 return OptionType::Positional;
2744 }
2745
2746 template <typename It>
2747 bool Complete(FlagBase &flag, It it, It end)
2748 {
2749 auto nextIt = it;
2750 if (!readCompletion || (++nextIt != end))
2751 {
2752 return false;
2753 }
2754
2755 const auto &chunk = *it;
2756 for (auto &choice : flag.HelpChoices(helpParams))
2757 {
2758 AddCompletionReply(chunk, choice);
2759 }
2760
2761#ifndef ARGS_NOEXCEPT
2762 throw Completion(completion->Get());
2763#else
2764 return true;
2765#endif
2766 }
2767
2777 template <typename It>
2778 std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
2779 const bool allowSeparate, const bool allowJoined,
2780 const bool hasJoined, const std::string &joinedArg,
2781 const bool canDiscardJoined, std::vector<std::string> &values)
2782 {
2783 values.clear();
2784
2785 Nargs nargs = flag.NumberOfArguments();
2786
2787 if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined))
2788 {
2789 return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2790 }
2791
2792 if (hasJoined)
2793 {
2794 if (!canDiscardJoined || (allowJoined && nargs.max != 0))
2795 {
2796 values.push_back(joinedArg);
2797 }
2798 } else if (!allowSeparate)
2799 {
2800 if (nargs.min != 0)
2801 {
2802 return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2803 }
2804 }
2805
2806 // Only gather separate values when they are allowed. A joined
2807 // value that was discarded rather than taken (short chunks such
2808 // as -nf when joined short values are off) means this flag isn't
2809 // taking an argument here, so the rest of the chunk is flags.
2810 if (allowSeparate && (!hasJoined || !values.empty()))
2811 {
2812 auto valueIt = it;
2813 ++valueIt;
2814
2815 while (valueIt != end &&
2816 *valueIt != terminator &&
2817 values.size() < nargs.max &&
2818 (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2819 {
2820 if (Complete(flag, valueIt, end))
2821 {
2822 // Park `it` on the completion position rather than
2823 // `end`. In ARGS_NOEXCEPT mode Complete returns
2824 // true (no throw), so the caller's for-loop will
2825 // run its ++it after we return; advancing an
2826 // already-end iterator is undefined behavior and
2827 // causes a subsequent out-of-bounds read of the
2828 // arg vector. Since Complete only fires when
2829 // ++nextIt == end, valueIt is the last element,
2830 // and ++(it=valueIt) safely lands on end.
2831 it = valueIt;
2832 return "";
2833 }
2834
2835 values.push_back(*valueIt);
2836 ++it;
2837 ++valueIt;
2838 }
2839 }
2840
2841 if (values.size() > nargs.max)
2842 {
2843 return "Passed an argument into a non-argument flag: " + arg;
2844 } else if (values.size() < nargs.min)
2845 {
2846 if (nargs.min == 1 && nargs.max == 1)
2847 {
2848 return "Flag '" + arg + "' requires an argument but received none";
2849 } else if (nargs.min == 1)
2850 {
2851 return "Flag '" + arg + "' requires at least one argument but received none";
2852 } else if (nargs.min != nargs.max)
2853 {
2854 return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2855 " arguments but received " + std::to_string(values.size());
2856 } else
2857 {
2858 return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2859 " arguments but received " + std::to_string(values.size());
2860 }
2861 }
2862
2863 return {};
2864 }
2865
2866 template <typename It>
2867 bool ParseLong(It &it, It end)
2868 {
2869 const auto &chunk = *it;
2870 const auto argchunk = chunk.substr(longprefix.size());
2871 // Try to separate it, in case of a separator:
2872 const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2873 // If the separator is in the argument, separate it.
2874 const auto arg = (separator != argchunk.npos ?
2875 std::string(argchunk, 0, separator)
2876 : argchunk);
2877 const auto joined = (separator != argchunk.npos ?
2878 argchunk.substr(separator + longseparator.size())
2879 : std::string());
2880
2881 if (auto flag = Match(arg))
2882 {
2883#ifdef ARGS_NOEXCEPT
2884 // Match() may set the flag's error (e.g. Error::Extra when
2885 // Options::Single is violated). In non-noexcept mode that
2886 // path throws and parsing stops before the value is read;
2887 // in noexcept mode we must mirror that and skip the value
2888 // parsing so the previously-stored value is preserved.
2889 if (flag->GetError() != Error::None)
2890 {
2891 return false;
2892 }
2893#endif
2894 std::vector<std::string> values;
2895 const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2896 separator != argchunk.npos, joined, false, values);
2897 if (!errorMessage.empty())
2898 {
2899#ifndef ARGS_NOEXCEPT
2900 throw ParseError(errorMessage);
2901#else
2902 error = Error::Parse;
2903 errorMsg = errorMessage;
2904 return false;
2905#endif
2906 }
2907
2908 if (!readCompletion)
2909 {
2910 flag->ParseValue(values);
2911#ifdef ARGS_NOEXCEPT
2912 // Non-noexcept ParseValue paths throw on Help, reader
2913 // failure, or Map miss, which halts parsing. Mirror
2914 // that here so a later parser-level error (e.g. an
2915 // unknown flag) cannot shadow the flag's error in
2916 // ArgumentParser::GetError().
2917 if (flag->GetError() != Error::None)
2918 {
2919 return false;
2920 }
2921#endif
2922 }
2923
2924 if (flag->KickOut())
2925 {
2926 ++it;
2927 return false;
2928 }
2929 } else
2930 {
2931 const std::string errorMessage("Flag could not be matched: " + arg);
2932#ifndef ARGS_NOEXCEPT
2933 throw ParseError(errorMessage);
2934#else
2935 error = Error::Parse;
2936 errorMsg = errorMessage;
2937 return false;
2938#endif
2939 }
2940
2941 return true;
2942 }
2943
2944 template <typename It>
2945 bool ParseShort(It &it, It end)
2946 {
2947 const auto &chunk = *it;
2948 const auto argchunk = chunk.substr(shortprefix.size());
2949 for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2950 {
2951 const auto arg = *argit;
2952
2953 if (auto flag = Match(arg))
2954 {
2955#ifdef ARGS_NOEXCEPT
2956 // See ParseLong: if Match recorded an error
2957 // (e.g. Options::Single violation), bail before the
2958 // value is parsed so the prior value is preserved.
2959 if (flag->GetError() != Error::None)
2960 {
2961 return false;
2962 }
2963#endif
2964 const std::string value(argit + 1, std::end(argchunk));
2965 std::vector<std::string> values;
2966 const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2967 allowSeparateShortValue, allowJoinedShortValue,
2968 !value.empty(), value, !value.empty(), values);
2969
2970 if (!errorMessage.empty())
2971 {
2972#ifndef ARGS_NOEXCEPT
2973 throw ParseError(errorMessage);
2974#else
2975 error = Error::Parse;
2976 errorMsg = errorMessage;
2977 return false;
2978#endif
2979 }
2980
2981 if (!readCompletion)
2982 {
2983 flag->ParseValue(values);
2984#ifdef ARGS_NOEXCEPT
2985 // See ParseLong: ensure a flag-level error from
2986 // ParseValue (Help, Parse, Map) halts parsing so
2987 // it cannot be shadowed by a later parser error.
2988 if (flag->GetError() != Error::None)
2989 {
2990 return false;
2991 }
2992#endif
2993 }
2994
2995 if (flag->KickOut())
2996 {
2997 ++it;
2998 return false;
2999 }
3000
3001 if (!values.empty())
3002 {
3003 break;
3004 }
3005 } else
3006 {
3007 const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
3008#ifndef ARGS_NOEXCEPT
3009 throw ParseError(errorMessage);
3010#else
3011 error = Error::Parse;
3012 errorMsg = errorMessage;
3013 return false;
3014#endif
3015 }
3016 }
3017
3018 return true;
3019 }
3020
3021 bool AddCompletionReply(const std::string &cur, const std::string &choice)
3022 {
3023 if (cur.empty() || choice.find(cur) == 0)
3024 {
3025 if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
3026 {
3027 completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size()));
3028 } else
3029 {
3030 completion->reply.push_back(choice);
3031 }
3032 return true;
3033 }
3034
3035 return false;
3036 }
3037
3038 template <typename It>
3039 bool Complete(It it, It end, bool terminated)
3040 {
3041 auto nextIt = it;
3042 if (!readCompletion || (++nextIt != end))
3043 {
3044 return false;
3045 }
3046
3047 const auto &chunk = *it;
3048 auto pos = GetNextPositional();
3049 std::vector<Command *> commands = GetCommands();
3050 const auto optionType = ParseOption(chunk, true);
3051
3052 // Once the terminator has been seen the parser treats every
3053 // following chunk as positional, so only positional choices are
3054 // valid completions here. Suggesting flags or commands past the
3055 // terminator offers candidates the parser would then reject.
3056 if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
3057 {
3058 for (auto &cmd : commands)
3059 {
3060 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3061 {
3062 AddCompletionReply(chunk, cmd->Name());
3063 }
3064 }
3065 } else
3066 {
3067 bool hasPositionalCompletion = true;
3068
3069 if (!terminated && !commands.empty())
3070 {
3071 for (auto &cmd : commands)
3072 {
3073 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3074 {
3075 AddCompletionReply(chunk, cmd->Name());
3076 }
3077 }
3078 } else if (pos)
3079 {
3080 if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3081 {
3082 auto choices = pos->HelpChoices(helpParams);
3083 hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
3084 for (auto &choice : choices)
3085 {
3086 AddCompletionReply(chunk, choice);
3087 }
3088 }
3089 }
3090
3091 if (!terminated && hasPositionalCompletion)
3092 {
3093 auto flags = GetAllFlags();
3094 for (auto flag : flags)
3095 {
3096 if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
3097 {
3098 continue;
3099 }
3100
3101 auto &matcher = flag->GetMatcher();
3102 if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
3103 {
3104 for (auto &flagName : matcher.GetFlagStrings())
3105 {
3106 if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
3107 {
3108 break;
3109 }
3110 }
3111 }
3112 }
3113
3114 if (optionType == OptionType::LongFlag && allowJoinedLongValue)
3115 {
3116 const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
3117 // Only attempt joined-value completion when the
3118 // separator lies at or past the long prefix, so
3119 // there is a (possibly empty) flag name between
3120 // them. With a custom longseparator that overlaps
3121 // the prefix (e.g. LongSeparator("-") under the
3122 // default "--" prefix), an attacker-controlled
3123 // completion word like "--x" puts the separator
3124 // inside the prefix, making `arg` shorter than
3125 // longprefix. arg.substr(longprefix.size()) would
3126 // then throw std::out_of_range, which escapes the
3127 // parser as a non-args exception (bypassing the
3128 // documented catch(args::Error) idiom) and is
3129 // thrown even under ARGS_NOEXCEPT.
3130 if (separator != chunk.npos && separator >= longprefix.size())
3131 {
3132 std::string arg(chunk, 0, separator);
3133 if (auto flag = this->Match(arg.substr(longprefix.size())))
3134 {
3135 for (auto &choice : flag->HelpChoices(helpParams))
3136 {
3137 AddCompletionReply(chunk, arg + longseparator + choice);
3138 }
3139 }
3140 }
3141 } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
3142 {
3143 if (chunk.size() > shortprefix.size() + 1)
3144 {
3145 auto arg = chunk.at(shortprefix.size());
3146 //TODO: support -abcVALUE where a and b take no value
3147 if (auto flag = this->Match(arg))
3148 {
3149 for (auto &choice : flag->HelpChoices(helpParams))
3150 {
3151 AddCompletionReply(chunk, shortprefix + arg + choice);
3152 }
3153 }
3154 }
3155 }
3156 }
3157 }
3158
3159#ifndef ARGS_NOEXCEPT
3160 throw Completion(completion->Get());
3161#else
3162 return true;
3163#endif
3164 }
3165
3166 template <typename It>
3167 It Parse(It begin, It end)
3168 {
3169 bool terminated = false;
3170 std::vector<Command *> commands = GetCommands();
3171
3172 // Check all arg chunks
3173 for (auto it = begin; it != end; ++it)
3174 {
3175 if (Complete(it, end, terminated))
3176 {
3177 return end;
3178 }
3179
3180 const auto &chunk = *it;
3181
3182 if (!terminated && chunk == terminator)
3183 {
3184 terminated = true;
3185 } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
3186 {
3187 if (!ParseLong(it, end))
3188 {
3189 return it;
3190 }
3191 } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
3192 {
3193 if (!ParseShort(it, end))
3194 {
3195 return it;
3196 }
3197 } else if (!terminated && !commands.empty())
3198 {
3199 auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
3200 if (itCommand == commands.end())
3201 {
3202 const std::string errorMessage("Unknown command: " + chunk);
3203#ifndef ARGS_NOEXCEPT
3204 throw ParseError(errorMessage);
3205#else
3206 error = Error::Parse;
3207 errorMsg = errorMessage;
3208 return it;
3209#endif
3210 }
3211
3212 SelectCommand(*itCommand);
3213
3214 if (const auto &coroutine = GetCoroutine())
3215 {
3216 ++it;
3217 RaiiSubparser coro(*this, std::vector<std::string>(it, end));
3218 coroutine(coro.Parser());
3219#ifdef ARGS_NOEXCEPT
3220 error = GetError();
3221 if (error != Error::None)
3222 {
3223 return end;
3224 }
3225
3226 if (!coro.Parser().IsParsed())
3227 {
3228 error = Error::Usage;
3229 return end;
3230 }
3231#else
3232 if (!coro.Parser().IsParsed())
3233 {
3234 throw UsageError("Subparser::Parse was not called");
3235 }
3236#endif
3237
3238 break;
3239 }
3240
3241 commands = GetCommands();
3242 } else
3243 {
3244 auto pos = GetNextPositional();
3245 if (pos)
3246 {
3247 pos->ParseValue(chunk);
3248#ifdef ARGS_NOEXCEPT
3249 if (pos->GetError() != Error::None)
3250 {
3251 return it;
3252 }
3253#endif
3254
3255 if (pos->KickOut())
3256 {
3257 return ++it;
3258 }
3259 } else
3260 {
3261 const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
3262#ifndef ARGS_NOEXCEPT
3263 throw ParseError(errorMessage);
3264#else
3265 error = Error::Parse;
3266 errorMsg = errorMessage;
3267 return it;
3268#endif
3269 }
3270 }
3271
3272 if (!readCompletion && completion != nullptr && completion->Matched())
3273 {
3274#ifdef ARGS_NOEXCEPT
3275 if (completion->GetError() != Error::None)
3276 {
3277 error = completion->GetError();
3278 if (errorMsg.empty())
3279 {
3280 errorMsg = completion->GetErrorMsg();
3281 }
3282 return it;
3283 }
3284
3285 error = Error::Completion;
3286#endif
3287 readCompletion = true;
3288 ++it;
3289 const auto argsLeft = static_cast<size_t>(std::distance(it, end));
3290 if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
3291 {
3292#ifndef ARGS_NOEXCEPT
3293 throw Completion("");
3294#else
3295 return end;
3296#endif
3297 }
3298
3299 ++it;
3300 std::vector<std::string> curArgs;
3301 curArgs.reserve(completion->cword);
3302 auto curIt = it;
3303 for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
3304 {
3305 curArgs.push_back(*curIt);
3306 }
3307
3308 if (completion->syntax == "bash")
3309 {
3310 // bash tokenizes --flag=value as --flag=value
3311 // Security fix: Use size_t arithmetic throughout to avoid conversion issues
3312 for (size_t idx = 0; idx < curArgs.size(); )
3313 {
3314 if (idx > 0 && curArgs[idx] == "=")
3315 {
3316 size_t prev_idx = idx - 1; // Safe since we checked idx > 0
3317 curArgs[prev_idx] += "=";
3318 size_t next_idx = 0;
3319 if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
3320 {
3321 curArgs[prev_idx] += curArgs[next_idx];
3322 // Erase the '=' token and the following value token.
3323 size_t erase_end = 0;
3324 if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
3325 {
3326 typedef std::vector<std::string>::difference_type diff_t;
3327 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx),
3328 curArgs.begin() + static_cast<diff_t>(erase_end));
3329 }
3330 } else
3331 {
3332 // Safe erase of single '=' token at the end
3333 typedef std::vector<std::string>::difference_type diff_t;
3334 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx));
3335 }
3336 // Do not increment idx - next element slides into current position
3337 } else
3338 {
3339 ++idx;
3340 }
3341 }
3342
3343 }
3344#ifndef ARGS_NOEXCEPT
3345 try
3346 {
3347 Parse(curArgs.begin(), curArgs.end());
3348 throw Completion("");
3349 }
3350 catch (Completion &)
3351 {
3352 throw;
3353 }
3354 catch (args::Error&)
3355 {
3356 throw Completion("");
3357 }
3358#else
3359 // Discard the nested Parse's return value: it points
3360 // into the local curArgs vector, which is destroyed
3361 // when this function returns, leaving the caller with
3362 // a dangling iterator that would be compared against
3363 // the outer `end` in ParseCLI. Return the outer
3364 // `end` instead so the iterator stays in the caller's
3365 // container.
3366 Parse(curArgs.begin(), curArgs.end());
3367 error = Error::Completion;
3368 errorMsg.clear();
3369 return end;
3370#endif
3371 }
3372 }
3373
3374 Validate(shortprefix, longprefix);
3375 return end;
3376 }
3377
3378 public:
3379 HelpParams helpParams;
3380
3381 ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
3382 {
3383 Description(description_);
3384 Epilog(epilog_);
3385 LongPrefix("--");
3386 ShortPrefix("-");
3387 LongSeparator("=");
3388 Terminator("--");
3389 SetArgumentSeparations(true, true, true, true);
3390 matched = true;
3391 }
3392
3393 void AddCompletion(CompletionFlag &completionFlag)
3394 {
3395 completion = &completionFlag;
3396 Add(completionFlag);
3397 }
3398
3401 const std::string &Prog() const
3402 { return helpParams.programName; }
3405 void Prog(const std::string &prog_)
3406 { this->helpParams.programName = prog_; }
3407
3410 const std::string &LongPrefix() const
3411 { return longprefix; }
3414 void LongPrefix(const std::string &longprefix_)
3415 {
3416 this->longprefix = longprefix_;
3417 this->helpParams.longPrefix = longprefix_;
3418 }
3419
3422 const std::string &ShortPrefix() const
3423 { return shortprefix; }
3426 void ShortPrefix(const std::string &shortprefix_)
3427 {
3428 this->shortprefix = shortprefix_;
3429 this->helpParams.shortPrefix = shortprefix_;
3430 }
3431
3434 const std::string &LongSeparator() const
3435 { return longseparator; }
3438 void LongSeparator(const std::string &longseparator_)
3439 {
3440 if (longseparator_.empty())
3441 {
3442 const std::string errorMessage("longseparator can not be set to empty");
3443#ifdef ARGS_NOEXCEPT
3444 error = Error::Usage;
3445 errorMsg = errorMessage;
3446#else
3447 throw UsageError(errorMessage);
3448#endif
3449 } else
3450 {
3451 this->longseparator = longseparator_;
3452 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3453 }
3454 }
3455
3458 const std::string &Terminator() const
3459 { return terminator; }
3462 void Terminator(const std::string &terminator_)
3463 { this->terminator = terminator_; }
3464
3470 bool &allowJoinedShortValue_,
3471 bool &allowJoinedLongValue_,
3472 bool &allowSeparateShortValue_,
3473 bool &allowSeparateLongValue_) const
3474 {
3475 allowJoinedShortValue_ = this->allowJoinedShortValue;
3476 allowJoinedLongValue_ = this->allowJoinedLongValue;
3477 allowSeparateShortValue_ = this->allowSeparateShortValue;
3478 allowSeparateLongValue_ = this->allowSeparateLongValue;
3479 }
3480
3489 const bool allowJoinedShortValue_,
3490 const bool allowJoinedLongValue_,
3491 const bool allowSeparateShortValue_,
3492 const bool allowSeparateLongValue_)
3493 {
3494 this->allowJoinedShortValue = allowJoinedShortValue_;
3495 this->allowJoinedLongValue = allowJoinedLongValue_;
3496 this->allowSeparateShortValue = allowSeparateShortValue_;
3497 this->allowSeparateLongValue = allowSeparateLongValue_;
3498
3499 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3500 this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
3501 }
3502
3505 void Help(std::ostream &help_) const
3506 {
3507 auto &command = SelectedCommand();
3508 const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
3509 const auto desc_indent = helpParams.descriptionindent;
3510 const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0;
3511 const auto description_text = Wrap(commandDescription, effective_desc_width);
3512 const auto epilog_text = Wrap(command.Epilog(), effective_desc_width);
3513
3514 const bool hasoptions = command.HasFlag();
3515 const bool hasarguments = command.HasPositional();
3516
3517 std::vector<std::string> prognameline;
3518 prognameline.push_back(helpParams.usageString);
3519 prognameline.push_back(Prog());
3520 auto commandProgLine = command.GetProgramLine(helpParams);
3521 prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
3522
3523 const auto prog_sum = helpParams.progindent + helpParams.progtailindent;
3524 const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0;
3525 const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0;
3526 const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
3527 effective_prog_width,
3528 effective_prog_first);
3529 auto progit = std::begin(proglines);
3530 if (progit != std::end(proglines))
3531 {
3532 help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
3533 ++progit;
3534 }
3535 for (; progit != std::end(proglines); ++progit)
3536 {
3537 help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
3538 }
3539
3540 help_ << '\n';
3541
3542 if (!description_text.empty())
3543 {
3544 for (const auto &line: description_text)
3545 {
3546 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3547 }
3548 help_ << "\n";
3549 }
3550
3551 bool lastDescriptionIsNewline = false;
3552
3553 if (!helpParams.optionsString.empty())
3554 {
3555 help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
3556 }
3557
3558 for (const auto &desc: command.GetDescription(helpParams, 0))
3559 {
3560 lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
3561 const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
3562 const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter;
3563 const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0;
3564 const auto flags = Wrap(std::get<0>(desc), effective_flag_width);
3565 const auto info_sum = helpParams.helpindent + groupindent;
3566 const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0;
3567 const auto info = Wrap(std::get<1>(desc), effective_info_width);
3568
3569 std::string::size_type flagssize = 0;
3570 for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
3571 {
3572 if (flagsit != std::begin(flags))
3573 {
3574 help_ << '\n';
3575 }
3576 help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
3577 flagssize = Glyphs(*flagsit);
3578 }
3579
3580 auto infoit = std::begin(info);
3581 // groupindent is on both sides of this inequality, and therefore can be removed
3582 if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
3583 {
3584 help_ << '\n';
3585 } else
3586 {
3587 // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
3588 const auto indent_sum = helpParams.flagindent + flagssize;
3589 const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0;
3590 help_ << std::string(effective_space, ' ') << *infoit << '\n';
3591 ++infoit;
3592 }
3593 for (; infoit != std::end(info); ++infoit)
3594 {
3595 help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
3596 }
3597 }
3598 if (hasoptions && hasarguments && helpParams.showTerminator)
3599 {
3600 lastDescriptionIsNewline = false;
3601 const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0;
3602 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))
3603 {
3604 help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
3605 }
3606 }
3607
3608 if (!lastDescriptionIsNewline)
3609 {
3610 help_ << "\n";
3611 }
3612
3613 for (const auto &line: epilog_text)
3614 {
3615 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3616 }
3617 }
3618
3623 std::string Help() const
3624 {
3625 std::ostringstream help_;
3626 Help(help_);
3627 return help_.str();
3628 }
3629
3630 virtual void Reset() noexcept override
3631 {
3632 Command::Reset();
3633 matched = true;
3634 readCompletion = false;
3635 }
3636
3643 template <typename It>
3644 It ParseArgs(It begin, It end)
3645 {
3646 // Reset all Matched statuses and errors
3647 Reset();
3648#ifdef ARGS_NOEXCEPT
3649 error = GetError();
3650 if (error != Error::None)
3651 {
3652 return end;
3653 }
3654#endif
3655 return Parse(begin, end);
3656 }
3657
3663 template <typename T>
3664 auto ParseArgs(const T &args) -> decltype(std::begin(args))
3665 {
3666 return ParseArgs(std::begin(args), std::end(args));
3667 }
3668
3675 bool ParseCLI(const int argc, const char * const * argv)
3676 {
3677 if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty())
3678 {
3679 Prog(argv[0]);
3680 }
3681
3682 std::vector<std::string> args;
3683 if (argc > 1 && argv != nullptr)
3684 {
3685 args.assign(argv + 1, argv + argc);
3686 }
3687
3688 return ParseArgs(args) == std::end(args);
3689 }
3690
3691 template <typename T>
3692 bool ParseCLI(const T &args)
3693 {
3694 return ParseArgs(args) == std::end(args);
3695 }
3696 };
3697
3698 inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
3699 : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
3700 {
3701 command.subparser = &parser;
3702 }
3703
3704 inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
3705 {
3706 command.subparser = &parser;
3707 }
3708
3709 inline void Subparser::Parse()
3710 {
3711 isParsed = true;
3712 Reset();
3713 command.subparserDescription = GetDescription(helpParams, 0);
3714 command.subparserHasFlag = HasFlag();
3715 command.subparserHasPositional = HasPositional();
3716 command.subparserHasCommand = HasCommand();
3717 command.subparserProgramLine = GetProgramLine(helpParams);
3718 if (parser == nullptr)
3719 {
3720#ifndef ARGS_NOEXCEPT
3721 throw args::SubparserError();
3722#else
3723 error = Error::Subparser;
3724 return;
3725#endif
3726 }
3727
3728 auto it = parser->Parse(args.begin(), args.end());
3729 command.Validate(parser->ShortPrefix(), parser->LongPrefix());
3730 kicked.assign(it, args.end());
3731
3732#ifdef ARGS_NOEXCEPT
3733 command.subparserError = GetError();
3734#endif
3735 }
3736
3737 inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
3738 {
3739 parser.Help(os);
3740 return os;
3741 }
3742
3745 class Flag : public FlagBase
3746 {
3747 public:
3748 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
3749 {
3750 group_.Add(*this);
3751 }
3752
3753 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)
3754 {
3755 }
3756
3757 virtual ~Flag() {}
3758
3761 bool Get() const
3762 {
3763 return Matched();
3764 }
3765
3766 virtual Nargs NumberOfArguments() const noexcept override
3767 {
3768 return 0;
3769 }
3770
3771 virtual void ParseValue(const std::vector<std::string>&) override
3772 {
3773 }
3774 };
3775
3780 class HelpFlag : public Flag
3781 {
3782 public:
3783 HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
3784
3785 virtual ~HelpFlag() {}
3786
3787 virtual void ParseValue(const std::vector<std::string> &)
3788 {
3789#ifdef ARGS_NOEXCEPT
3790 error = Error::Help;
3791 errorMsg = Name();
3792#else
3793 throw Help(Name());
3794#endif
3795 }
3796
3799 bool Get() const noexcept
3800 {
3801 return Matched();
3802 }
3803 };
3804
3807 class CounterFlag : public Flag
3808 {
3809 private:
3810 const int startcount;
3811 int count;
3812
3813 public:
3814 CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
3815 Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
3816
3817 virtual ~CounterFlag() {}
3818
3819 virtual FlagBase *Match(const EitherFlag &arg) override
3820 {
3821 auto me = FlagBase::Match(arg);
3822 if (me)
3823 {
3824#ifdef ARGS_NOEXCEPT
3825 // Suppress increment when FlagBase::Match recorded an
3826 // error on this same call (e.g. Options::Single violated).
3827 // In non-noexcept mode that path would have thrown before
3828 // reaching here and the count would not have advanced.
3829 if (GetError() != Error::None)
3830 {
3831 return me;
3832 }
3833#endif
3834 ++count;
3835 }
3836 return me;
3837 }
3838
3841 int &Get() noexcept
3842 {
3843 return count;
3844 }
3845
3846 int &operator *() noexcept {
3847 return count;
3848 }
3849
3850 const int &operator *() const noexcept {
3851 return count;
3852 }
3853
3854 virtual void Reset() noexcept override
3855 {
3856 FlagBase::Reset();
3857 count = startcount;
3858 }
3859 };
3860
3863 class ActionFlag : public FlagBase
3864 {
3865 private:
3866 std::function<void(const std::vector<std::string> &)> action;
3867 Nargs nargs;
3868
3869 public:
3870 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_ = {}):
3871 FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
3872 {
3873 group_.Add(*this);
3874 }
3875
3876 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
3877 FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
3878 {
3879 group_.Add(*this);
3880 action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
3881 }
3882
3883 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
3884 FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
3885 {
3886 group_.Add(*this);
3887 action = [action_](const std::vector<std::string> &) { return action_(); };
3888 }
3889
3890 virtual Nargs NumberOfArguments() const noexcept override
3891 { return nargs; }
3892
3893 virtual void ParseValue(const std::vector<std::string> &value) override
3894 { action(value); }
3895 };
3896
3904 {
3905 private:
3906 template <typename T>
3907 static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type
3908 HasUnsignedNegativeSign(const std::string &value)
3909 {
3910 const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c)
3911 {
3912 return std::isspace(static_cast<unsigned char>(c)) != 0;
3913 });
3914
3915 return firstNonSpace != value.end() && *firstNonSpace == '-';
3916 }
3917
3918 template <typename T>
3919 static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type
3920 HasUnsignedNegativeSign(const std::string &)
3921 {
3922 return false;
3923 }
3924
3925 public:
3926 template <typename T>
3927 typename std::enable_if<
3928 std::is_integral<T>::value &&
3929 !std::is_same<T, bool>::value &&
3930 !std::is_same<T, char>::value &&
3931 !std::is_same<T, signed char>::value &&
3932 !std::is_same<T, unsigned char>::value,
3933 bool>::type
3934 ParseNumericValue(const std::string &value, T &destination)
3935 {
3936 if (HasUnsignedNegativeSign<T>(value))
3937 {
3938 return false;
3939 }
3940
3941 const char *begin = value.c_str();
3942 // The true end of the value, derived from its length rather than
3943 // from the first NUL. strtoull/strtoll treat the buffer as a C
3944 // string and stop at an embedded '\0', so checking `*end == '\0'`
3945 // for "no trailing data" is defeated by a value like "12\0junk":
3946 // end lands on the embedded NUL and the junk after it is silently
3947 // accepted. Comparing against `stop` validates the whole string
3948 // and matches the istringstream-based reader used for other types.
3949 const char *const stop = begin + value.size();
3950
3951 // C++11-compatible: use strtoull/strtoll. Hardening retained from
3952 // the original from_chars draft (errno save/restore, ERANGE check,
3953 // narrowing range check, trailing-whitespace tolerance). No
3954 // unconditional dependency on <charconv> / C++17.
3955 const int saved_errno = errno;
3956 errno = 0;
3957
3958 char *end = nullptr;
3959
3960 if (std::is_unsigned<T>::value)
3961 {
3962 const unsigned long long parsed = std::strtoull(begin, &end, 0);
3963 if (end == begin)
3964 {
3965 errno = saved_errno;
3966 return false;
3967 }
3968 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3969 {
3970 ++end;
3971 }
3972 if (end != stop || errno == ERANGE ||
3973 parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
3974 {
3975 errno = saved_errno;
3976 return false;
3977 }
3978
3979 destination = static_cast<T>(parsed);
3980 }
3981 else
3982 {
3983 const long long parsed = std::strtoll(begin, &end, 0);
3984 if (end == begin)
3985 {
3986 errno = saved_errno;
3987 return false;
3988 }
3989 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3990 {
3991 ++end;
3992 }
3993 if (end != stop || errno == ERANGE ||
3994 parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
3995 parsed > static_cast<long long>(std::numeric_limits<T>::max()))
3996 {
3997 errno = saved_errno;
3998 return false;
3999 }
4000
4001 destination = static_cast<T>(parsed);
4002 }
4003
4004 errno = saved_errno;
4005 return true;
4006 }
4007
4008 template <typename T>
4009 typename std::enable_if<
4010 !std::is_integral<T>::value ||
4011 std::is_same<T, bool>::value ||
4012 std::is_same<T, char>::value ||
4013 std::is_same<T, signed char>::value ||
4014 std::is_same<T, unsigned char>::value,
4015 bool>::type
4016 ParseNumericValue(const std::string &value, T &destination)
4017 {
4018 std::istringstream ss(value);
4019 // Pin parsing to the C locale so that the decimal separator and
4020 // thousands grouping behavior do not silently depend on whatever
4021 // std::locale::global was last set to elsewhere in the process.
4022 // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in
4023 // any locale whose numpunct facet treats ',' as the decimal point.
4024 ss.imbue(std::locale::classic());
4025 ss >> destination;
4026 if (ss.fail())
4027 {
4028 return false;
4029 }
4030
4031 // Check for trailing garbage by attempting to extract any remaining characters.
4032 // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit
4033 // on EOF, causing false rejection of valid input.
4034 char extra = '\0';
4035 ss >> std::ws >> extra;
4036 // If extraction succeeded, there's trailing garbage (return false).
4037 // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true).
4038 // If extraction failed for other reasons, it's invalid (return false).
4039 if (ss.fail())
4040 {
4041 // Clear the failbit to check if EOF is the only issue
4042 ss.clear(ss.rdstate() & ~std::ios::failbit);
4043 return ss.eof();
4044 }
4045 // Extraction succeeded, meaning there's trailing garbage
4046 return false;
4047 }
4048
4049 template <typename T>
4050 typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
4051 operator ()(const std::string &name, const std::string &value, T &destination)
4052 {
4053 const bool success = ParseNumericValue(value, destination);
4054 if (!success)
4055 {
4056#ifdef ARGS_NOEXCEPT
4057 (void)name;
4058 return false;
4059#else
4060 std::ostringstream problem;
4061 problem << "Argument '" << name << "' received invalid value type '" << value << "'";
4062 throw ParseError(problem.str());
4063#endif
4064 }
4065 return true;
4066 }
4067
4068 template <typename T>
4069 typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
4070 operator()(const std::string &, const std::string &value, T &destination)
4071 {
4072 destination = value;
4073 return true;
4074 }
4075 };
4076
4082 template <
4083 typename T,
4084 typename Reader = ValueReader>
4086 {
4087 protected:
4088 T value;
4089 T defaultValue;
4090
4091 virtual std::string GetDefaultString(const HelpParams&) const override
4092 {
4093 return detail::ToString(defaultValue);
4094 }
4095
4096 private:
4097 Reader reader;
4098
4099 public:
4100
4101 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_)
4102 {
4103 group_.Add(*this);
4104 }
4105
4106 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)
4107 {
4108 }
4109
4110 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
4111 {
4112 }
4113
4114 virtual ~ValueFlag() {}
4115
4116 virtual void ParseValue(const std::vector<std::string> &values_) override
4117 {
4118 const std::string &value_ = values_.at(0);
4119
4120#ifdef ARGS_NOEXCEPT
4121 if (!reader(name, value_, this->value))
4122 {
4123 error = Error::Parse;
4124 }
4125#else
4126 reader(name, value_, this->value);
4127#endif
4128 }
4129
4130 virtual void Reset() noexcept override
4131 {
4132 ValueFlagBase::Reset();
4133 value = defaultValue;
4134 }
4135
4138 T &Get() noexcept
4139 {
4140 return value;
4141 }
4142
4145 T &operator *() noexcept
4146 {
4147 return value;
4148 }
4149
4152 const T &operator *() const noexcept
4153 {
4154 return value;
4155 }
4156
4159 T *operator ->() noexcept
4160 {
4161 return &value;
4162 }
4163
4166 const T *operator ->() const noexcept
4167 {
4168 return &value;
4169 }
4170
4173 const T &GetDefault() noexcept
4174 {
4175 return defaultValue;
4176 }
4177 };
4178
4184 template <
4185 typename T,
4186 typename Reader = ValueReader>
4187 class ImplicitValueFlag : public ValueFlag<T, Reader>
4188 {
4189 protected:
4190 T implicitValue;
4191
4192 public:
4193
4194 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
4195 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
4196 {
4197 }
4198
4199 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
4200 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
4201 {
4202 }
4203
4204 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
4205 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
4206 {
4207 }
4208
4209 virtual ~ImplicitValueFlag() {}
4210
4211 virtual Nargs NumberOfArguments() const noexcept override
4212 {
4213 return {0, 1};
4214 }
4215
4216 virtual void ParseValue(const std::vector<std::string> &value_) override
4217 {
4218 if (value_.empty())
4219 {
4220 this->value = implicitValue;
4221 } else
4222 {
4224 }
4225 }
4226 };
4227
4232 template <typename T>
4233 class ConstantFlag : public Flag
4234 {
4235 T value;
4236
4237 public:
4238
4239 ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_, const T& value_):
4240 Flag(group_, name_, help_, std::move(matcher_), options_),
4241 value(value_)
4242 {}
4243
4244 ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T& value_, bool extraError_ = false):
4245 Flag(group_, name_, help_, std::move(matcher_), extraError_),
4246 value(value_)
4247 {}
4248
4249 T operator * () const noexcept
4250 {
4251 return value;
4252 }
4253
4254 T Get() const noexcept
4255 {
4256 return value;
4257 }
4258
4259 const T *operator -> () const noexcept
4260 {
4261 return &value;
4262 }
4263 };
4264
4271 template <
4272 typename T,
4273 template <typename...> class List = detail::vector,
4274 typename Reader = ValueReader>
4276 {
4277 protected:
4278
4279 List<T> values;
4280 const List<T> defaultValues;
4281 Nargs nargs;
4282 Reader reader;
4283
4284 public:
4285
4286 typedef List<T> Container;
4287 typedef T value_type;
4288 typedef typename Container::allocator_type allocator_type;
4289 typedef typename Container::pointer pointer;
4290 typedef typename Container::const_pointer const_pointer;
4291 typedef T& reference;
4292 typedef const T& const_reference;
4293 typedef typename Container::size_type size_type;
4294 typedef typename Container::difference_type difference_type;
4295 typedef typename Container::iterator iterator;
4296 typedef typename Container::const_iterator const_iterator;
4297 typedef std::reverse_iterator<iterator> reverse_iterator;
4298 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4299
4300 NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
4301 : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
4302 {
4303 group_.Add(*this);
4304 }
4305
4306 virtual ~NargsValueFlag() {}
4307
4308 virtual Nargs NumberOfArguments() const noexcept override
4309 {
4310 return nargs;
4311 }
4312
4313 virtual void ParseValue(const std::vector<std::string> &values_) override
4314 {
4315 values.clear();
4316
4317 for (const std::string &value : values_)
4318 {
4319 T v {};
4320#ifdef ARGS_NOEXCEPT
4321 if (!reader(name, value, v))
4322 {
4323 error = Error::Parse;
4324 return;
4325 }
4326#else
4327 reader(name, value, v);
4328#endif
4329 values.insert(std::end(values), v);
4330 }
4331 }
4332
4333 List<T> &Get() noexcept
4334 {
4335 return values;
4336 }
4337
4340 List<T> &operator *() noexcept
4341 {
4342 return values;
4343 }
4344
4347 const List<T> &operator *() const noexcept
4348 {
4349 return values;
4350 }
4351
4354 List<T> *operator ->() noexcept
4355 {
4356 return &values;
4357 }
4358
4361 const List<T> *operator ->() const noexcept
4362 {
4363 return &values;
4364 }
4365
4366 iterator begin() noexcept
4367 {
4368 return values.begin();
4369 }
4370
4371 const_iterator begin() const noexcept
4372 {
4373 return values.begin();
4374 }
4375
4376 const_iterator cbegin() const noexcept
4377 {
4378 return values.cbegin();
4379 }
4380
4381 iterator end() noexcept
4382 {
4383 return values.end();
4384 }
4385
4386 const_iterator end() const noexcept
4387 {
4388 return values.end();
4389 }
4390
4391 const_iterator cend() const noexcept
4392 {
4393 return values.cend();
4394 }
4395
4396 virtual void Reset() noexcept override
4397 {
4398 FlagBase::Reset();
4399 values = defaultValues;
4400 }
4401
4402 virtual FlagBase *Match(const EitherFlag &arg) override
4403 {
4404 const bool wasMatched = Matched();
4405 auto me = FlagBase::Match(arg);
4406 if (me && !wasMatched)
4407 {
4408 values.clear();
4409 }
4410 return me;
4411 }
4412 };
4413
4420 template <
4421 typename T,
4422 template <typename...> class List = detail::vector,
4423 typename Reader = ValueReader>
4425 {
4426 private:
4427 using Container = List<T>;
4428 Container values;
4429 const Container defaultValues;
4430 Reader reader;
4431
4432 public:
4433
4434 typedef T value_type;
4435 typedef typename Container::allocator_type allocator_type;
4436 typedef typename Container::pointer pointer;
4437 typedef typename Container::const_pointer const_pointer;
4438 typedef T& reference;
4439 typedef const T& const_reference;
4440 typedef typename Container::size_type size_type;
4441 typedef typename Container::difference_type difference_type;
4442 typedef typename Container::iterator iterator;
4443 typedef typename Container::const_iterator const_iterator;
4444 typedef std::reverse_iterator<iterator> reverse_iterator;
4445 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4446
4447 ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
4448 ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
4449 {
4450 group_.Add(*this);
4451 }
4452
4453 virtual ~ValueFlagList() {}
4454
4455 virtual void ParseValue(const std::vector<std::string> &values_) override
4456 {
4457 const std::string &value_ = values_.at(0);
4458
4459 T v{};
4460#ifdef ARGS_NOEXCEPT
4461 if (!reader(name, value_, v))
4462 {
4463 error = Error::Parse;
4464 return;
4465 }
4466#else
4467 reader(name, value_, v);
4468#endif
4469 values.insert(std::end(values), v);
4470 }
4471
4474 Container &Get() noexcept
4475 {
4476 return values;
4477 }
4478
4481 Container &operator *() noexcept
4482 {
4483 return values;
4484 }
4485
4488 const Container &operator *() const noexcept
4489 {
4490 return values;
4491 }
4492
4495 Container *operator ->() noexcept
4496 {
4497 return &values;
4498 }
4499
4502 const Container *operator ->() const noexcept
4503 {
4504 return &values;
4505 }
4506
4507 virtual std::string Name() const override
4508 {
4509 return name + std::string("...");
4510 }
4511
4512 virtual void Reset() noexcept override
4513 {
4514 ValueFlagBase::Reset();
4515 values = defaultValues;
4516 }
4517
4518 virtual FlagBase *Match(const EitherFlag &arg) override
4519 {
4520 const bool wasMatched = Matched();
4521 auto me = FlagBase::Match(arg);
4522 if (me && !wasMatched)
4523 {
4524 values.clear();
4525 }
4526 return me;
4527 }
4528
4529 iterator begin() noexcept
4530 {
4531 return values.begin();
4532 }
4533
4534 const_iterator begin() const noexcept
4535 {
4536 return values.begin();
4537 }
4538
4539 const_iterator cbegin() const noexcept
4540 {
4541 return values.cbegin();
4542 }
4543
4544 iterator end() noexcept
4545 {
4546 return values.end();
4547 }
4548
4549 const_iterator end() const noexcept
4550 {
4551 return values.end();
4552 }
4553
4554 const_iterator cend() const noexcept
4555 {
4556 return values.cend();
4557 }
4558 };
4559
4567 template <
4568 typename K,
4569 typename T,
4570 typename Reader = ValueReader,
4571 template <typename...> class Map = detail::unordered_map>
4572 class MapFlag : public ValueFlagBase
4573 {
4574 private:
4575 const Map<K, T> map;
4576 T value;
4577 const T defaultValue;
4578 Reader reader;
4579
4580 protected:
4581 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4582 {
4583 return detail::MapKeysToStrings(map);
4584 }
4585
4586 public:
4587
4588 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_)
4589 {
4590 group_.Add(*this);
4591 }
4592
4593 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)
4594 {
4595 }
4596
4597 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_)
4598 {
4599 }
4600
4601 virtual ~MapFlag() {}
4602
4603 virtual void ParseValue(const std::vector<std::string> &values_) override
4604 {
4605 const std::string &value_ = values_.at(0);
4606
4607 K key{};
4608#ifdef ARGS_NOEXCEPT
4609 if (!reader(name, value_, key))
4610 {
4611 error = Error::Parse;
4612 return;
4613 }
4614#else
4615 reader(name, value_, key);
4616#endif
4617 auto it = map.find(key);
4618 if (it == std::end(map))
4619 {
4620 std::ostringstream problem;
4621 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4622#ifdef ARGS_NOEXCEPT
4623 error = Error::Map;
4624 errorMsg = problem.str();
4625#else
4626 throw MapError(problem.str());
4627#endif
4628 } else
4629 {
4630 this->value = it->second;
4631 }
4632 }
4633
4636 T &Get() noexcept
4637 {
4638 return value;
4639 }
4640
4643 T &operator *() noexcept
4644 {
4645 return value;
4646 }
4647
4650 const T &operator *() const noexcept
4651 {
4652 return value;
4653 }
4654
4657 T *operator ->() noexcept
4658 {
4659 return &value;
4660 }
4661
4664 const T *operator ->() const noexcept
4665 {
4666 return &value;
4667 }
4668
4669 virtual void Reset() noexcept override
4670 {
4671 ValueFlagBase::Reset();
4672 value = defaultValue;
4673 }
4674 };
4675
4684 template <
4685 typename K,
4686 typename T,
4687 template <typename...> class List = detail::vector,
4688 typename Reader = ValueReader,
4689 template <typename...> class Map = detail::unordered_map>
4691 {
4692 private:
4693 using Container = List<T>;
4694 const Map<K, T> map;
4695 Container values;
4696 const Container defaultValues;
4697 Reader reader;
4698
4699 protected:
4700 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4701 {
4702 return detail::MapKeysToStrings(map);
4703 }
4704
4705 public:
4706 typedef T value_type;
4707 typedef typename Container::allocator_type allocator_type;
4708 typedef typename Container::pointer pointer;
4709 typedef typename Container::const_pointer const_pointer;
4710 typedef T& reference;
4711 typedef const T& const_reference;
4712 typedef typename Container::size_type size_type;
4713 typedef typename Container::difference_type difference_type;
4714 typedef typename Container::iterator iterator;
4715 typedef typename Container::const_iterator const_iterator;
4716 typedef std::reverse_iterator<iterator> reverse_iterator;
4717 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4718
4719 MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4720 ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4721 {
4722 group_.Add(*this);
4723 }
4724
4725 virtual ~MapFlagList() {}
4726
4727 virtual void ParseValue(const std::vector<std::string> &values_) override
4728 {
4729 const std::string &value_ = values_.at(0);
4730
4731 K key{};
4732#ifdef ARGS_NOEXCEPT
4733 if (!reader(name, value_, key))
4734 {
4735 error = Error::Parse;
4736 return;
4737 }
4738#else
4739 reader(name, value_, key);
4740#endif
4741 auto it = map.find(key);
4742 if (it == std::end(map))
4743 {
4744 std::ostringstream problem;
4745 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4746#ifdef ARGS_NOEXCEPT
4747 error = Error::Map;
4748 errorMsg = problem.str();
4749#else
4750 throw MapError(problem.str());
4751#endif
4752 } else
4753 {
4754 this->values.emplace_back(it->second);
4755 }
4756 }
4757
4760 Container &Get() noexcept
4761 {
4762 return values;
4763 }
4764
4767 Container &operator *() noexcept
4768 {
4769 return values;
4770 }
4771
4774 const Container &operator *() const noexcept
4775 {
4776 return values;
4777 }
4778
4781 Container *operator ->() noexcept
4782 {
4783 return &values;
4784 }
4785
4788 const Container *operator ->() const noexcept
4789 {
4790 return &values;
4791 }
4792
4793 virtual std::string Name() const override
4794 {
4795 return name + std::string("...");
4796 }
4797
4798 virtual void Reset() noexcept override
4799 {
4800 ValueFlagBase::Reset();
4801 values = defaultValues;
4802 }
4803
4804 virtual FlagBase *Match(const EitherFlag &arg) override
4805 {
4806 const bool wasMatched = Matched();
4807 auto me = FlagBase::Match(arg);
4808 if (me && !wasMatched)
4809 {
4810 values.clear();
4811 }
4812 return me;
4813 }
4814
4815 iterator begin() noexcept
4816 {
4817 return values.begin();
4818 }
4819
4820 const_iterator begin() const noexcept
4821 {
4822 return values.begin();
4823 }
4824
4825 const_iterator cbegin() const noexcept
4826 {
4827 return values.cbegin();
4828 }
4829
4830 iterator end() noexcept
4831 {
4832 return values.end();
4833 }
4834
4835 const_iterator end() const noexcept
4836 {
4837 return values.end();
4838 }
4839
4840 const_iterator cend() const noexcept
4841 {
4842 return values.cend();
4843 }
4844 };
4845
4851 template <
4852 typename T,
4853 typename Reader = ValueReader>
4855 {
4856 private:
4857 T value;
4858 const T defaultValue;
4859 Reader reader;
4860 public:
4861 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_)
4862 {
4863 group_.Add(*this);
4864 }
4865
4866 Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
4867 {
4868 }
4869
4870 virtual ~Positional() {}
4871
4872 virtual void ParseValue(const std::string &value_) override
4873 {
4874#ifdef ARGS_NOEXCEPT
4875 if (!reader(name, value_, this->value))
4876 {
4877 error = Error::Parse;
4878 return;
4879 }
4880#else
4881 reader(name, value_, this->value);
4882#endif
4883 ready = false;
4884 matched = true;
4885 }
4886
4889 T &Get() noexcept
4890 {
4891 return value;
4892 }
4893
4896 T &operator *() noexcept
4897 {
4898 return value;
4899 }
4900
4903 const T &operator *() const noexcept
4904 {
4905 return value;
4906 }
4907
4910 T *operator ->() noexcept
4911 {
4912 return &value;
4913 }
4914
4917 const T *operator ->() const noexcept
4918 {
4919 return &value;
4920 }
4921
4922 virtual void Reset() noexcept override
4923 {
4924 PositionalBase::Reset();
4925 value = defaultValue;
4926 }
4927 };
4928
4935 template <
4936 typename T,
4937 template <typename...> class List = detail::vector,
4938 typename Reader = ValueReader>
4940 {
4941 private:
4942 using Container = List<T>;
4943 Container values;
4944 const Container defaultValues;
4945 Reader reader;
4946
4947 public:
4948 typedef T value_type;
4949 typedef typename Container::allocator_type allocator_type;
4950 typedef typename Container::pointer pointer;
4951 typedef typename Container::const_pointer const_pointer;
4952 typedef T& reference;
4953 typedef const T& const_reference;
4954 typedef typename Container::size_type size_type;
4955 typedef typename Container::difference_type difference_type;
4956 typedef typename Container::iterator iterator;
4957 typedef typename Container::const_iterator const_iterator;
4958 typedef std::reverse_iterator<iterator> reverse_iterator;
4959 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4960
4961 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_)
4962 {
4963 group_.Add(*this);
4964 }
4965
4966 PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
4967 {
4968 }
4969
4970 virtual ~PositionalList() {}
4971
4972 virtual void ParseValue(const std::string &value_) override
4973 {
4974 T v{};
4975#ifdef ARGS_NOEXCEPT
4976 if (!reader(name, value_, v))
4977 {
4978 error = Error::Parse;
4979 return;
4980 }
4981#else
4982 reader(name, value_, v);
4983#endif
4984 values.insert(std::end(values), v);
4985 matched = true;
4986 }
4987
4988 virtual std::string Name() const override
4989 {
4990 return name + std::string("...");
4991 }
4992
4995 Container &Get() noexcept
4996 {
4997 return values;
4998 }
4999
5002 Container &operator *() noexcept
5003 {
5004 return values;
5005 }
5006
5009 const Container &operator *() const noexcept
5010 {
5011 return values;
5012 }
5013
5016 Container *operator ->() noexcept
5017 {
5018 return &values;
5019 }
5020
5023 const Container *operator ->() const noexcept
5024 {
5025 return &values;
5026 }
5027
5028 virtual void Reset() noexcept override
5029 {
5030 PositionalBase::Reset();
5031 values = defaultValues;
5032 }
5033
5034 virtual PositionalBase *GetNextPositional() override
5035 {
5036 const bool wasMatched = Matched();
5037 auto me = PositionalBase::GetNextPositional();
5038 if (me && !wasMatched)
5039 {
5040 values.clear();
5041 }
5042 return me;
5043 }
5044
5045 iterator begin() noexcept
5046 {
5047 return values.begin();
5048 }
5049
5050 const_iterator begin() const noexcept
5051 {
5052 return values.begin();
5053 }
5054
5055 const_iterator cbegin() const noexcept
5056 {
5057 return values.cbegin();
5058 }
5059
5060 iterator end() noexcept
5061 {
5062 return values.end();
5063 }
5064
5065 const_iterator end() const noexcept
5066 {
5067 return values.end();
5068 }
5069
5070 const_iterator cend() const noexcept
5071 {
5072 return values.cend();
5073 }
5074 };
5075
5083 template <
5084 typename K,
5085 typename T,
5086 typename Reader = ValueReader,
5087 template <typename...> class Map = detail::unordered_map>
5089 {
5090 private:
5091 const Map<K, T> map;
5092 T value;
5093 const T defaultValue;
5094 Reader reader;
5095
5096 protected:
5097 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5098 {
5099 return detail::MapKeysToStrings(map);
5100 }
5101
5102 public:
5103
5104 MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
5105 PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
5106 {
5107 group_.Add(*this);
5108 }
5109
5110 virtual ~MapPositional() {}
5111
5112 virtual void ParseValue(const std::string &value_) override
5113 {
5114 K key{};
5115#ifdef ARGS_NOEXCEPT
5116 if (!reader(name, value_, key))
5117 {
5118 error = Error::Parse;
5119 return;
5120 }
5121#else
5122 reader(name, value_, key);
5123#endif
5124 auto it = map.find(key);
5125 if (it == std::end(map))
5126 {
5127 std::ostringstream problem;
5128 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5129#ifdef ARGS_NOEXCEPT
5130 error = Error::Map;
5131 errorMsg = problem.str();
5132#else
5133 throw MapError(problem.str());
5134#endif
5135 } else
5136 {
5137 this->value = it->second;
5138 ready = false;
5139 matched = true;
5140 }
5141 }
5142
5145 T &Get() noexcept
5146 {
5147 return value;
5148 }
5149
5152 T &operator *() noexcept
5153 {
5154 return value;
5155 }
5156
5159 const T &operator *() const noexcept
5160 {
5161 return value;
5162 }
5163
5166 T *operator ->() noexcept
5167 {
5168 return &value;
5169 }
5170
5173 const T *operator ->() const noexcept
5174 {
5175 return &value;
5176 }
5177
5178 virtual void Reset() noexcept override
5179 {
5180 PositionalBase::Reset();
5181 value = defaultValue;
5182 }
5183 };
5184
5193 template <
5194 typename K,
5195 typename T,
5196 template <typename...> class List = detail::vector,
5197 typename Reader = ValueReader,
5198 template <typename...> class Map = detail::unordered_map>
5200 {
5201 private:
5202 using Container = List<T>;
5203
5204 const Map<K, T> map;
5205 Container values;
5206 const Container defaultValues;
5207 Reader reader;
5208
5209 protected:
5210 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5211 {
5212 return detail::MapKeysToStrings(map);
5213 }
5214
5215 public:
5216 typedef T value_type;
5217 typedef typename Container::allocator_type allocator_type;
5218 typedef typename Container::pointer pointer;
5219 typedef typename Container::const_pointer const_pointer;
5220 typedef T& reference;
5221 typedef const T& const_reference;
5222 typedef typename Container::size_type size_type;
5223 typedef typename Container::difference_type difference_type;
5224 typedef typename Container::iterator iterator;
5225 typedef typename Container::const_iterator const_iterator;
5226 typedef std::reverse_iterator<iterator> reverse_iterator;
5227 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
5228
5229 MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
5230 PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
5231 {
5232 group_.Add(*this);
5233 }
5234
5235 virtual ~MapPositionalList() {}
5236
5237 virtual void ParseValue(const std::string &value_) override
5238 {
5239 K key{};
5240#ifdef ARGS_NOEXCEPT
5241 if (!reader(name, value_, key))
5242 {
5243 error = Error::Parse;
5244 return;
5245 }
5246#else
5247 reader(name, value_, key);
5248#endif
5249 auto it = map.find(key);
5250 if (it == std::end(map))
5251 {
5252 std::ostringstream problem;
5253 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5254#ifdef ARGS_NOEXCEPT
5255 error = Error::Map;
5256 errorMsg = problem.str();
5257#else
5258 throw MapError(problem.str());
5259#endif
5260 } else
5261 {
5262 this->values.emplace_back(it->second);
5263 matched = true;
5264 }
5265 }
5266
5269 Container &Get() noexcept
5270 {
5271 return values;
5272 }
5273
5276 Container &operator *() noexcept
5277 {
5278 return values;
5279 }
5280
5283 const Container &operator *() const noexcept
5284 {
5285 return values;
5286 }
5287
5290 Container *operator ->() noexcept
5291 {
5292 return &values;
5293 }
5294
5297 const Container *operator ->() const noexcept
5298 {
5299 return &values;
5300 }
5301
5302 virtual std::string Name() const override
5303 {
5304 return name + std::string("...");
5305 }
5306
5307 virtual void Reset() noexcept override
5308 {
5309 PositionalBase::Reset();
5310 values = defaultValues;
5311 }
5312
5313 virtual PositionalBase *GetNextPositional() override
5314 {
5315 const bool wasMatched = Matched();
5316 auto me = PositionalBase::GetNextPositional();
5317 if (me && !wasMatched)
5318 {
5319 values.clear();
5320 }
5321 return me;
5322 }
5323
5324 iterator begin() noexcept
5325 {
5326 return values.begin();
5327 }
5328
5329 const_iterator begin() const noexcept
5330 {
5331 return values.begin();
5332 }
5333
5334 const_iterator cbegin() const noexcept
5335 {
5336 return values.cbegin();
5337 }
5338
5339 iterator end() noexcept
5340 {
5341 return values.end();
5342 }
5343
5344 const_iterator end() const noexcept
5345 {
5346 return values.end();
5347 }
5348
5349 const_iterator cend() const noexcept
5350 {
5351 return values.cend();
5352 }
5353 };
5354}
5355
5356#pragma pop_macro("min")
5357#pragma pop_macro("max")
5358#endif
A flag class that calls a function when it's matched.
Definition args.hxx:3864
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3890
virtual void ParseValue(const std::vector< std::string > &value) override
Parse values of this option.
Definition args.hxx:3893
The main user facing command line argument parser class.
Definition args.hxx:2690
const std::string & ShortPrefix() const
The prefix for short flags.
Definition args.hxx:3422
void SetArgumentSeparations(const bool allowJoinedShortValue_, const bool allowJoinedLongValue_, const bool allowSeparateShortValue_, const bool allowSeparateLongValue_)
Change allowed option separation.
Definition args.hxx:3488
void Prog(const std::string &prog_)
The program name for help generation.
Definition args.hxx:3405
It ParseArgs(It begin, It end)
Parse all arguments.
Definition args.hxx:3644
const std::string & Prog() const
The program name for help generation.
Definition args.hxx:3401
void LongPrefix(const std::string &longprefix_)
The prefix for long flags.
Definition args.hxx:3414
void LongSeparator(const std::string &longseparator_)
The separator for long flags.
Definition args.hxx:3438
void Help(std::ostream &help_) const
Pass the help menu into an ostream.
Definition args.hxx:3505
const std::string & LongPrefix() const
The prefix for long flags.
Definition args.hxx:3410
const std::string & LongSeparator() const
The separator for long flags.
Definition args.hxx:3434
bool ParseCLI(const int argc, const char *const *argv)
Convenience function to parse the CLI from argc and argv.
Definition args.hxx:3675
const std::string & Terminator() const
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3458
auto ParseArgs(const T &args) -> decltype(std::begin(args))
Parse all arguments.
Definition args.hxx:3664
void Terminator(const std::string &terminator_)
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3462
void GetArgumentSeparations(bool &allowJoinedShortValue_, bool &allowJoinedLongValue_, bool &allowSeparateShortValue_, bool &allowSeparateLongValue_) const
Get the current argument separation parameters.
Definition args.hxx:3469
std::string Help() const
Generate a help menu as a string.
Definition args.hxx:3623
void ShortPrefix(const std::string &shortprefix_)
The prefix for short flags.
Definition args.hxx:3426
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:2778
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:2150
const std::string & Name() const
The name of command.
Definition args.hxx:2285
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:2386
const std::string & ProglinePostfix() const
The description that appears on the prog line after options.
Definition args.hxx:2255
void Description(const std::string &description_)
The description that appears above options.
Definition args.hxx:2270
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:2422
const std::string & Description() const
The description that appears above options.
Definition args.hxx:2265
const std::string & Help() const
The description of command.
Definition args.hxx:2290
void Epilog(const std::string &epilog_)
The description that appears below options.
Definition args.hxx:2280
void ProglinePostfix(const std::string &proglinePostfix_)
The description that appears on the prog line after options.
Definition args.hxx:2260
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:2322
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:2427
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:2491
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:2417
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:2516
const std::string & Epilog() const
The description that appears below options.
Definition args.hxx:2275
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:2303
void RequireCommand(bool value)
If value is true, parser will fail if no command was parsed.
Definition args.hxx:2297
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:4234
A flag class that simply counts the number of times it's matched.
Definition args.hxx:3808
int & Get() noexcept
Get the count.
Definition args.hxx:3841
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:3746
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3766
virtual void ParseValue(const std::vector< std::string > &) override
Parse values of this option.
Definition args.hxx:3771
bool Get() const
Get whether this was matched.
Definition args.hxx:3761
Class for using global options in ArgumentParser.
Definition args.hxx:2069
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:1803
std::vector< Base * >::size_type MatchedChildren() const
Count the number of matched children this group has.
Definition args.hxx:1810
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:1794
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:1898
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:1950
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:1785
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:1854
void DetectDuplicateFlags(std::unordered_set< char > &usedShortFlags, std::unordered_set< std::string > &usedLongFlags)
Used by parameterless DetectDuplicateFlags.
Definition args.hxx:1969
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:1734
bool Get() const
Get validation.
Definition args.hxx:1861
const std::vector< Base * > & Children() const
Get all this group's children.
Definition args.hxx:1724
std::vector< Base * > GetMatchedChildren() const
Get the list of children which were matched.
Definition args.hxx:1819
void DetectDuplicateFlags()
Detect duplicate flags.
Definition args.hxx:1960
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:1835
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:1868
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:1769
Help flag class.
Definition args.hxx:3781
bool Get() const noexcept
Get whether this was matched.
Definition args.hxx:3799
virtual void ParseValue(const std::vector< std::string > &)
Parse values of this option.
Definition args.hxx:3787
An exception that indicates that the user has requested help.
Definition args.hxx:541
An optional argument-accepting flag class.
Definition args.hxx:4188
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:4216
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4211
Errors in map lookups.
Definition args.hxx:523
A mapping value flag list class.
Definition args.hxx:4691
Container * operator->() noexcept
Get the values.
Definition args.hxx:4781
Container & Get() noexcept
Get the value.
Definition args.hxx:4760
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4727
Container & operator*() noexcept
Get the value.
Definition args.hxx:4767
A mapping value flag class.
Definition args.hxx:4573
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4603
T & Get() noexcept
Get the value.
Definition args.hxx:4636
T * operator->() noexcept
Get the value.
Definition args.hxx:4657
T & operator*() noexcept
Get the value.
Definition args.hxx:4643
A positional argument mapping list class.
Definition args.hxx:5200
Container & operator*() noexcept
Get the value.
Definition args.hxx:5276
Container * operator->() noexcept
Get the values.
Definition args.hxx:5290
Container & Get() noexcept
Get the value.
Definition args.hxx:5269
A positional argument mapping class.
Definition args.hxx:5089
T * operator->() noexcept
Get the value.
Definition args.hxx:5166
T & Get() noexcept
Get the value.
Definition args.hxx:5145
T & operator*() noexcept
Get the value.
Definition args.hxx:5152
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:4276
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4308
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4313
List< T > * operator->() noexcept
Get the values.
Definition args.hxx:4354
List< T > & operator*() noexcept
Get the value.
Definition args.hxx:4340
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:4940
Container & operator*() noexcept
Get the value.
Definition args.hxx:5002
Container & Get() noexcept
Get the values.
Definition args.hxx:4995
Container * operator->() noexcept
Get the values.
Definition args.hxx:5016
A positional argument class.
Definition args.hxx:4855
T & operator*() noexcept
Get the value.
Definition args.hxx:4896
T & Get() noexcept
Get the value.
Definition args.hxx:4889
T * operator->() noexcept
Get the value.
Definition args.hxx:4910
Errors that when a required flag is omitted.
Definition args.hxx:514
Utility class for building subparsers with coroutines/callbacks.
Definition args.hxx:2095
void Parse()
Continue parsing arguments for new command.
Definition args.hxx:3709
const std::vector< std::string > & KickedOut() const noexcept
Returns a vector of kicked out arguments.
Definition args.hxx:2139
bool IsParsed() const
(INTERNAL) Determines whether Parse was called or not.
Definition args.hxx:2126
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:4425
Container * operator->() noexcept
Get the values.
Definition args.hxx:4495
Container & Get() noexcept
Get the values.
Definition args.hxx:4474
Container & operator*() noexcept
Get the value.
Definition args.hxx:4481
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4455
An argument-accepting flag class.
Definition args.hxx:4086
T & operator*() noexcept
Get the value.
Definition args.hxx:4145
T * operator->() noexcept
Get the value.
Definition args.hxx:4159
T & Get() noexcept
Get the value.
Definition args.hxx:4138
const T & GetDefault() noexcept
Get the default value.
Definition args.hxx:4173
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4116
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:3904