args 6.6.1
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.1"
41#define ARGS_VERSION_MAJOR 6
42#define ARGS_VERSION_MINOR 6
43#define ARGS_VERSION_PATCH 1
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 // Keep emplace_back for sequences: insert can require move assignment
1282 // even when appending a copyable value at the end of a vector.
1283 template <typename Container, typename T>
1284 auto AppendMappedValue(Container &container, const T &value, int)
1285 -> decltype(container.emplace_back(value), void())
1286 {
1287 container.emplace_back(value);
1288 }
1289
1290 template <typename Container, typename T>
1291 auto AppendMappedValue(Container &container, const T &value, long)
1292 -> decltype(container.insert(std::end(container), value), void())
1293 {
1294 container.insert(std::end(container), value);
1295 }
1296
1297 template <typename T>
1298 std::vector<std::string> MapKeysToStrings(const T &map)
1299 {
1300 std::vector<std::string> res;
1301 using K = typename std::decay<decltype(std::begin(map)->first)>::type;
1302 if (IsConvertableToString<K>::value)
1303 {
1304 for (const auto &p : map)
1305 {
1306 res.push_back(detail::ToString(p.first));
1307 }
1308
1309 std::sort(res.begin(), res.end());
1310 }
1311 return res;
1312 }
1313 }
1314
1317 class FlagBase : public NamedBase
1318 {
1319 protected:
1320 const Matcher matcher;
1321
1322 virtual std::string GetNameString(const HelpParams &params) const override
1323 {
1324 const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
1325 std::string flags;
1326 const auto flagStrings = matcher.GetFlagStrings();
1327 const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
1328 for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
1329 {
1330 auto &flag = *it;
1331 if (it != flagStrings.begin())
1332 {
1333 flags += ", ";
1334 }
1335
1336 flags += flag.isShort ? params.shortPrefix : params.longPrefix;
1337 flags += flag.str();
1338
1339 if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
1340 {
1341 flags += flag.isShort ? params.shortSeparator : params.longSeparator;
1342 flags += params.valueOpen + postfix + params.valueClose;
1343 }
1344 }
1345
1346 return flags;
1347 }
1348
1349 public:
1350 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_)) {}
1351
1352 FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {}
1353
1354 virtual ~FlagBase() {}
1355
1356 virtual bool IsFlag() const override
1357 {
1358 return true;
1359 }
1360
1361 virtual FlagBase *Match(const EitherFlag &flag) override
1362 {
1363 if (matcher.Match(flag))
1364 {
1365 if ((GetOptions() & Options::Single) != Options::None && matched)
1366 {
1367 std::ostringstream problem;
1368 problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once";
1369#ifdef ARGS_NOEXCEPT
1370 error = Error::Extra;
1371 errorMsg = problem.str();
1372#else
1373 throw ExtraError(problem.str());
1374#endif
1375 }
1376 matched = true;
1377 return this;
1378 }
1379 return nullptr;
1380 }
1381
1382 virtual std::vector<FlagBase*> GetAllFlags() override
1383 {
1384 return { this };
1385 }
1386
1387 const Matcher &GetMatcher() const
1388 {
1389 return matcher;
1390 }
1391
1392 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1393 {
1394 if (!Matched() && IsRequired())
1395 {
1396 std::ostringstream problem;
1397 problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required";
1398#ifdef ARGS_NOEXCEPT
1399 error = Error::Required;
1400 errorMsg = problem.str();
1401#else
1402 throw RequiredError(problem.str());
1403#endif
1404 }
1405 }
1406
1407 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1408 {
1409 if (!params.proglineShowFlags)
1410 {
1411 return {};
1412 }
1413
1414 const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name();
1415 const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny();
1416 std::string res = flag.str(params.shortPrefix, params.longPrefix);
1417 if (!postfix.empty())
1418 {
1419 res += params.proglineValueOpen + postfix + params.proglineValueClose;
1420 }
1421
1422 return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
1423 : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
1424 }
1425
1426 virtual bool HasFlag() const override
1427 {
1428 return true;
1429 }
1430
1431#ifdef ARGS_NOEXCEPT
1433 bool usageError = false;
1434 void SetUsageError()
1435 {
1436 usageError = true;
1437 }
1438 void ClearUsageError()
1439 {
1440 usageError = false;
1441 }
1442 virtual Error GetError() const override
1443 {
1444 if(usageError)
1445 {
1446 return Error::Usage;
1447 }
1448 const auto nargs = NumberOfArguments();
1449 if (nargs.min > nargs.max)
1450 {
1451 return Error::Usage;
1452 }
1453
1454 const auto matcherError = matcher.GetError();
1455 if (matcherError != Error::None)
1456 {
1457 return matcherError;
1458 }
1459
1460 return error;
1461 }
1462#endif
1463
1468 virtual Nargs NumberOfArguments() const noexcept = 0;
1469
1474 virtual void ParseValue(const std::vector<std::string> &value) = 0;
1475 };
1476
1480 {
1481 public:
1482 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {}
1483 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {}
1484 virtual ~ValueFlagBase() {}
1485
1486 virtual Nargs NumberOfArguments() const noexcept override
1487 {
1488 return 1;
1489 }
1490 };
1491
1493 {
1494 public:
1495 std::vector<std::string> reply;
1496 size_t cword = 0;
1497 std::string syntax;
1498
1499 template <typename GroupClass>
1500 CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden)
1501 {
1502 group_.AddCompletion(*this);
1503 }
1504
1505 virtual ~CompletionFlag() {}
1506
1507 virtual Nargs NumberOfArguments() const noexcept override
1508 {
1509 return 2;
1510 }
1511
1512 virtual void ParseValue(const std::vector<std::string> &value_) override
1513 {
1514 syntax = value_.at(0);
1515 const std::string &raw = value_.at(1);
1516 bool failed = false;
1517
1518 const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c)
1519 {
1520 return std::isspace(static_cast<unsigned char>(c)) != 0;
1521 });
1522
1523 // Reject explicit signs: cword must be a plain non-negative
1524 // decimal index. istringstream would otherwise silently
1525 // accept "+1".
1526 if (firstNonSpace != raw.end() && (*firstNonSpace == '-' || *firstNonSpace == '+'))
1527 {
1528 failed = true;
1529 }
1530
1531 size_t parsed = 0;
1532 if (!failed)
1533 {
1534 std::istringstream ss(raw);
1535 // Use the C locale so that the cword index parses
1536 // consistently regardless of any std::locale::global call
1537 // elsewhere in the process. A locale with a non-empty
1538 // grouping facet would otherwise reject digit-only inputs
1539 // like "12" when grouping rules expect separators.
1540 ss.imbue(std::locale::classic());
1541 ss >> parsed;
1542 if (ss.fail())
1543 {
1544 failed = true;
1545 }
1546 else
1547 {
1548 char extra;
1549 if (ss >> extra)
1550 {
1551 failed = true;
1552 }
1553 else if (!ss.eof())
1554 {
1555 failed = true;
1556 }
1557 }
1558 }
1559
1560 if (failed)
1561 {
1562#ifdef ARGS_NOEXCEPT
1563 error = Error::Parse;
1564 errorMsg = "Argument 'completion' received invalid value type '" + raw + "'";
1565#else
1566 std::ostringstream problem;
1567 problem << "Argument 'completion' received invalid value type '" << raw << "'";
1568 throw ParseError(problem.str());
1569#endif
1570 return;
1571 }
1572
1573 cword = parsed;
1574 }
1575
1578 std::string Get() noexcept
1579 {
1580 return detail::Join(reply, "\n");
1581 }
1582
1583 virtual void Reset() noexcept override
1584 {
1585 ValueFlagBase::Reset();
1586 cword = 0;
1587 syntax.clear();
1588 reply.clear();
1589 }
1590 };
1591
1592
1596 {
1597 protected:
1598 bool ready;
1599
1600 public:
1601 PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {}
1602 virtual ~PositionalBase() {}
1603
1604 bool Ready()
1605 {
1606 return ready;
1607 }
1608
1609 virtual void ParseValue(const std::string &value_) = 0;
1610
1611 virtual void Reset() noexcept override
1612 {
1613 matched = false;
1614 ready = true;
1615#ifdef ARGS_NOEXCEPT
1616 error = Error::None;
1617 errorMsg.clear();
1618#endif
1619 }
1620
1621 virtual PositionalBase *GetNextPositional() override
1622 {
1623 return Ready() ? this : nullptr;
1624 }
1625
1626 virtual bool HasPositional() const override
1627 {
1628 return true;
1629 }
1630
1631 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1632 {
1633 return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose
1634 : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose };
1635 }
1636
1637 virtual void Validate(const std::string &, const std::string &) const override
1638 {
1639 if (IsRequired() && !Matched())
1640 {
1641 std::ostringstream problem;
1642 problem << "Option '" << Name() << "' is required";
1643#ifdef ARGS_NOEXCEPT
1644 error = Error::Required;
1645 errorMsg = problem.str();
1646#else
1647 throw RequiredError(problem.str());
1648#endif
1649 }
1650 }
1651 };
1652
1655 class Group : public Base
1656 {
1657 private:
1658 Group* parent;
1659 std::vector<Base*> children;
1660 std::function<bool(const Group &)> validator;
1661
1662 public:
1666 {
1667 static bool Xor(const Group &group)
1668 {
1669 return group.MatchedChildren() == 1;
1670 }
1671
1672 static bool AtLeastOne(const Group &group)
1673 {
1674 return group.MatchedChildren() >= 1;
1675 }
1676
1677 static bool AtMostOne(const Group &group)
1678 {
1679 return group.MatchedChildren() <= 1;
1680 }
1681
1682 static bool All(const Group &group)
1683 {
1684 return group.Children().size() == group.MatchedChildren();
1685 }
1686
1687 static bool AllOrNone(const Group &group)
1688 {
1689 return (All(group) || None(group));
1690 }
1691
1692 static bool AllChildGroups(const Group &group)
1693 {
1694 return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool {
1695 return child->IsGroup() && !child->Matched();
1696 });
1697 }
1698
1699 static bool DontCare(const Group &)
1700 {
1701 return true;
1702 }
1703
1704 static bool CareTooMuch(const Group &)
1705 {
1706 return false;
1707 }
1708
1709 static bool None(const Group &group)
1710 {
1711 return group.MatchedChildren() == 0;
1712 }
1713 };
1715 Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_)
1716 {
1717 parent = nullptr;
1718 }
1720 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_)
1721 {
1722 group_.Add(*this);
1723 parent = &group_;
1724 }
1725 virtual ~Group() {}
1726
1729 void Add(Base &child)
1730 {
1731 children.emplace_back(&child);
1732
1733 if(child.IsFlag()) {
1734#ifndef ARGS_NOEXCEPT
1735 // Detection runs from the child's own constructor, so a
1736 // duplicate throws before that constructor completes and the
1737 // child's storage is released while the stack unwinds. Undo
1738 // the registration first, or a caller that catches the error
1739 // leaves this group holding a pointer to a dead object.
1740 try
1741 {
1742 SignalDetectDuplicates();
1743 }
1744 catch (...)
1745 {
1746 children.pop_back();
1747 throw;
1748 }
1749#else
1750 SignalDetectDuplicates();
1751#endif
1752 }
1753 }
1754
1757 const std::vector<Base *> &Children() const
1758 {
1759 return children;
1760 }
1761
1767 virtual FlagBase *Match(const EitherFlag &flag) override
1768 {
1769 for (Base *child: Children())
1770 {
1771 if (FlagBase *match = child->Match(flag))
1772 {
1773 return match;
1774 }
1775 }
1776 return nullptr;
1777 }
1778
1779 virtual std::vector<FlagBase*> GetAllFlags() override
1780 {
1781 std::vector<FlagBase*> res;
1782 for (Base *child: Children())
1783 {
1784 auto childRes = child->GetAllFlags();
1785 res.insert(res.end(), childRes.begin(), childRes.end());
1786 }
1787 return res;
1788 }
1789
1790 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1791 {
1792 for (Base *child: Children())
1793 {
1794 child->Validate(shortPrefix, longPrefix);
1795 }
1796 }
1797
1803 {
1804 for (Base *child: Children())
1805 {
1806 if (auto next = child->GetNextPositional())
1807 {
1808 return next;
1809 }
1810 }
1811 return nullptr;
1812 }
1813
1818 virtual bool HasFlag() const override
1819 {
1820 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
1821 }
1822
1827 virtual bool HasPositional() const override
1828 {
1829 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
1830 }
1831
1836 virtual bool HasCommand() const override
1837 {
1838 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
1839 }
1840
1843 std::vector<Base *>::size_type MatchedChildren() const
1844 {
1845 // Cast to avoid warnings from -Wsign-conversion
1846 return static_cast<std::vector<Base *>::size_type>(
1847 std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
1848 }
1849
1852 std::vector<Base *> GetMatchedChildren() const
1853 {
1854 // Could be replaced by C++ 20 filter, or a custom iterator.
1855 std::vector<Base*> matched_children;
1856 std::copy_if(children.begin(), children.end(), std::back_inserter(matched_children), [](Base* b){
1857 return b->Matched();
1858 });
1859 return matched_children;
1860 }
1861
1867 template <typename ChildType>
1868 std::vector<ChildType *> GetFilteredChildren(bool matching = false) const
1869 {
1870 std::vector<ChildType *> filtered_children;
1871 for(Base *child : children) {
1872 if(!matching || child->Matched())
1873 {
1874 ChildType* cast_result = dynamic_cast<ChildType*>(child);
1875 if(cast_result != nullptr)
1876 {
1877 filtered_children.push_back(cast_result);
1878 }
1879
1880 }
1881 }
1882 return filtered_children;
1883 }
1884
1887 virtual bool Matched() const noexcept override
1888 {
1889 return validator(*this);
1890 }
1891
1894 bool Get() const
1895 {
1896 return Matched();
1897 }
1898
1901 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
1902 {
1903 std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
1904
1905 // Push that group description on the back if not empty
1906 unsigned addindent = 0;
1907 if (!help.empty())
1908 {
1909 descriptions.emplace_back(help, "", indent);
1910 addindent = 1;
1911 }
1912
1913 for (Base *child: Children())
1914 {
1915 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
1916 {
1917 continue;
1918 }
1919
1920 auto groupDescriptions = child->GetDescription(params, indent + addindent);
1921 descriptions.insert(
1922 std::end(descriptions),
1923 std::make_move_iterator(std::begin(groupDescriptions)),
1924 std::make_move_iterator(std::end(groupDescriptions)));
1925 }
1926 return descriptions;
1927 }
1928
1931 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1932 {
1933 std::vector <std::string> names;
1934 for (Base *child: Children())
1935 {
1936 if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
1937 {
1938 continue;
1939 }
1940
1941 auto groupNames = child->GetProgramLine(params);
1942 names.insert(
1943 std::end(names),
1944 std::make_move_iterator(std::begin(groupNames)),
1945 std::make_move_iterator(std::end(groupNames)));
1946 }
1947 return names;
1948 }
1949
1950 virtual std::vector<Command*> GetCommands() override
1951 {
1952 std::vector<Command*> res;
1953 for (const auto &child : Children())
1954 {
1955 auto subparsers = child->GetCommands();
1956 res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
1957 }
1958 return res;
1959 }
1960
1961 virtual bool IsGroup() const override
1962 {
1963 return true;
1964 }
1965
1966 virtual void Reset() noexcept override
1967 {
1968 Base::Reset();
1969
1970 for (auto &child: Children())
1971 {
1972 child->Reset();
1973 }
1974#ifdef ARGS_NOEXCEPT
1975 error = Error::None;
1976 errorMsg.clear();
1977#endif
1978 }
1979
1984 {
1985 if(parent != nullptr) parent->SignalDetectDuplicates();
1986 else DetectDuplicateFlags();
1987 }
1988
1994 {
1995 std::unordered_set<char> usedShortFlags;
1996 std::unordered_set<std::string> usedLongFlags;
1997 DetectDuplicateFlags(usedShortFlags, usedLongFlags);
1998 }
1999
2002 void DetectDuplicateFlags(std::unordered_set<char> &usedShortFlags, std::unordered_set<std::string> &usedLongFlags)
2003 {
2004 for (Base *child: Children())
2005 {
2006 if(auto flag = dynamic_cast<FlagBase*>(child))
2007 {
2008 // Check for duplicate flags, setting a usage error on the
2009 // flag if a duplicate is detected.
2010 for(EitherFlag flagString: flag->GetMatcher().GetFlagStrings())
2011 {
2012 if(flagString.isShort)
2013 {
2014 if(usedShortFlags.count(flagString.shortFlag))
2015 {
2016#ifdef ARGS_NOEXCEPT
2017 flag->SetUsageError();
2018#else
2019 throw ParseError("duplicate short flag detected");
2020#endif
2021 }
2022 else
2023 {
2024 usedShortFlags.insert(flagString.shortFlag);
2025 }
2026 }
2027 else
2028 {
2029 if(usedLongFlags.count(flagString.longFlag))
2030 {
2031#ifdef ARGS_NOEXCEPT
2032 flag->SetUsageError();
2033#else
2034 throw ParseError("duplicate long flag detected");
2035#endif
2036 }
2037 else
2038 {
2039 usedLongFlags.insert(flagString.longFlag);
2040 }
2041 }
2042 }
2043 }
2044 else if(auto group = dynamic_cast<Group*>(child))
2045 {
2046 // A command opens its own flag namespace and runs its
2047 // own duplicate detection as a separate root, so a flag
2048 // reused either side of a command boundary is not a
2049 // genuine duplicate. Only descend into plain groups
2050 // here; IsGroup() is false for a Command.
2051 if(group->IsGroup())
2052 {
2053 group->DetectDuplicateFlags(usedShortFlags, usedLongFlags);
2054 }
2055 }
2056 }
2057 }
2058
2059#ifdef ARGS_NOEXCEPT
2061 virtual Error GetError() const override
2062 {
2063 if (error != Error::None)
2064 {
2065 return error;
2066 }
2067
2068 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2069 if (it == Children().end())
2070 {
2071 return Error::None;
2072 } else
2073 {
2074 return (*it)->GetError();
2075 }
2076 }
2077
2079 virtual std::string GetErrorMsg() const override
2080 {
2081 if (error != Error::None)
2082 {
2083 return errorMsg;
2084 }
2085
2086 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
2087 if (it == Children().end())
2088 {
2089 return "";
2090 } else
2091 {
2092 return (*it)->GetErrorMsg();
2093 }
2094 }
2095#endif
2096
2097 };
2098
2101 class GlobalOptions : public Group
2102 {
2103 public:
2104 GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
2105 {
2106 Add(options_);
2107 }
2108 };
2109
2127 class Subparser : public Group
2128 {
2129 private:
2130 std::vector<std::string> args;
2131 std::vector<std::string> kicked;
2132 ArgumentParser *parser = nullptr;
2133 const HelpParams &helpParams;
2134 const Command &command;
2135 bool isParsed = false;
2136
2137 public:
2138 Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
2139 : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
2140 {
2141 }
2142
2143 Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
2144 {
2145 }
2146
2147 Subparser(const Subparser&) = delete;
2148 Subparser(Subparser&&) = delete;
2149 Subparser &operator = (const Subparser&) = delete;
2150 Subparser &operator = (Subparser&&) = delete;
2151
2152 const Command &GetCommand()
2153 {
2154 return command;
2155 }
2156
2159 bool IsParsed() const
2160 {
2161 return isParsed;
2162 }
2163
2166 void Parse();
2167
2172 const std::vector<std::string> &KickedOut() const noexcept
2173 {
2174 return kicked;
2175 }
2176 };
2177
2182 class Command : public Group
2183 {
2184 private:
2185 friend class Subparser;
2186
2187 std::string name;
2188 std::string help;
2189 std::string description;
2190 std::string epilog;
2191 std::string proglinePostfix;
2192
2193 std::function<void(Subparser&)> parserCoroutine;
2194 bool commandIsRequired = true;
2195 Command *selectedCommand = nullptr;
2196
2197 mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
2198 mutable std::vector<std::string> subparserProgramLine;
2199 mutable bool subparserHasFlag = false;
2200 mutable bool subparserHasPositional = false;
2201 mutable bool subparserHasCommand = false;
2202#ifdef ARGS_NOEXCEPT
2203 mutable Error subparserError = Error::None;
2204#endif
2205 mutable Subparser *subparser = nullptr;
2206
2207 protected:
2208
2209 class RaiiSubparser
2210 {
2211 public:
2212 RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
2213 RaiiSubparser(const Command &command_, const HelpParams &params_);
2214
2215 ~RaiiSubparser()
2216 {
2217 command.subparser = oldSubparser;
2218 }
2219
2220 Subparser &Parser()
2221 {
2222 return parser;
2223 }
2224
2225 private:
2226 const Command &command;
2227 Subparser parser;
2228 Subparser *oldSubparser;
2229 };
2230
2231 Command() = default;
2232
2233 std::function<void(Subparser&)> &GetCoroutine()
2234 {
2235 return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
2236 }
2237
2238 Command &SelectedCommand()
2239 {
2240 Command *res = this;
2241 while (res->selectedCommand != nullptr)
2242 {
2243 res = res->selectedCommand;
2244 }
2245
2246 return *res;
2247 }
2248
2249 const Command &SelectedCommand() const
2250 {
2251 const Command *res = this;
2252 while (res->selectedCommand != nullptr)
2253 {
2254 res = res->selectedCommand;
2255 }
2256
2257 return *res;
2258 }
2259
2260 void UpdateSubparserHelp(const HelpParams &params) const
2261 {
2262 if (parserCoroutine)
2263 {
2264 RaiiSubparser coro(*this, params);
2265#ifndef ARGS_NOEXCEPT
2266 try
2267 {
2268 parserCoroutine(coro.Parser());
2269 }
2270 catch (args::SubparserError&)
2271 {
2272 }
2273#else
2274 parserCoroutine(coro.Parser());
2275#endif
2276 }
2277 }
2278
2279 public:
2280 Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
2281 : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
2282 {
2283 base_.Add(*this);
2284 }
2285
2288 const std::string &ProglinePostfix() const
2289 { return proglinePostfix; }
2290
2293 void ProglinePostfix(const std::string &proglinePostfix_)
2294 { this->proglinePostfix = proglinePostfix_; }
2295
2298 const std::string &Description() const
2299 { return description; }
2303 void Description(const std::string &description_)
2304 { this->description = description_; }
2305
2308 const std::string &Epilog() const
2309 { return epilog; }
2310
2313 void Epilog(const std::string &epilog_)
2314 { this->epilog = epilog_; }
2315
2318 const std::string &Name() const
2319 { return name; }
2320
2323 const std::string &Help() const
2324 { return help; }
2325
2330 void RequireCommand(bool value)
2331 { commandIsRequired = value; }
2332
2333 virtual bool IsGroup() const override
2334 { return false; }
2335
2336 virtual bool Matched() const noexcept override
2337 { return Base::Matched(); }
2338
2339 operator bool() const noexcept
2340 { return Matched(); }
2341
2342 void Match() noexcept
2343 { matched = true; }
2344
2345 void SelectCommand(Command *c) noexcept
2346 {
2347 selectedCommand = c;
2348
2349 if (c != nullptr)
2350 {
2351 c->Match();
2352 }
2353 }
2354
2355 virtual FlagBase *Match(const EitherFlag &flag) override
2356 {
2357 if (selectedCommand != nullptr)
2358 {
2359 if (auto *res = selectedCommand->Match(flag))
2360 {
2361 return res;
2362 }
2363
2364 for (auto *child: Children())
2365 {
2366 if ((child->GetOptions() & Options::Global) != Options::None)
2367 {
2368 if (auto *res = child->Match(flag))
2369 {
2370 return res;
2371 }
2372 }
2373 }
2374
2375 return nullptr;
2376 }
2377
2378 if (subparser != nullptr)
2379 {
2380 return subparser->Match(flag);
2381 }
2382
2383 return Matched() ? Group::Match(flag) : nullptr;
2384 }
2385
2386 virtual std::vector<FlagBase*> GetAllFlags() override
2387 {
2388 std::vector<FlagBase*> res;
2389
2390 if (!Matched())
2391 {
2392 return res;
2393 }
2394
2395 for (auto *child: Children())
2396 {
2397 if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
2398 {
2399 auto childFlags = child->GetAllFlags();
2400 res.insert(res.end(), childFlags.begin(), childFlags.end());
2401 }
2402 }
2403
2404 if (selectedCommand != nullptr)
2405 {
2406 auto childFlags = selectedCommand->GetAllFlags();
2407 res.insert(res.end(), childFlags.begin(), childFlags.end());
2408 }
2409
2410 if (subparser != nullptr)
2411 {
2412 auto childFlags = subparser->GetAllFlags();
2413 res.insert(res.end(), childFlags.begin(), childFlags.end());
2414 }
2415
2416 return res;
2417 }
2418
2420 {
2421 if (selectedCommand != nullptr)
2422 {
2423 if (auto *res = selectedCommand->GetNextPositional())
2424 {
2425 return res;
2426 }
2427
2428 for (auto *child: Children())
2429 {
2430 if ((child->GetOptions() & Options::Global) != Options::None)
2431 {
2432 if (auto *res = child->GetNextPositional())
2433 {
2434 return res;
2435 }
2436 }
2437 }
2438
2439 return nullptr;
2440 }
2441
2442 if (subparser != nullptr)
2443 {
2444 return subparser->GetNextPositional();
2445 }
2446
2447 return Matched() ? Group::GetNextPositional() : nullptr;
2448 }
2449
2450 virtual bool HasFlag() const override
2451 {
2452 return subparserHasFlag || Group::HasFlag();
2453 }
2454
2455 virtual bool HasPositional() const override
2456 {
2457 return subparserHasPositional || Group::HasPositional();
2458 }
2459
2460 virtual bool HasCommand() const override
2461 {
2462 return true;
2463 }
2464
2465 std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
2466 {
2467 UpdateSubparserHelp(params);
2468
2469 std::vector<std::string> res;
2470
2471 if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
2472 {
2473 res.push_back(params.proglineOptions);
2474 }
2475
2476 auto group_res = Group::GetProgramLine(params);
2477 std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res));
2478
2479 res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
2480
2481 if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
2482 {
2483 res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
2484 }
2485
2486 if (!Name().empty())
2487 {
2488 res.insert(res.begin(), Name());
2489 }
2490
2491 if (!ProglinePostfix().empty())
2492 {
2493 std::string line;
2494 for (auto c : ProglinePostfix())
2495 {
2496 if (std::isspace(static_cast<unsigned char>(c)))
2497 {
2498 if (!line.empty())
2499 {
2500 res.push_back(line);
2501 line.clear();
2502 }
2503
2504 if (c == '\n')
2505 {
2506 res.push_back("\n");
2507 }
2508 }
2509 else
2510 {
2511 line += c;
2512 }
2513 }
2514
2515 if (!line.empty())
2516 {
2517 res.push_back(line);
2518 }
2519 }
2520
2521 return res;
2522 }
2523
2524 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
2525 {
2526 if (!Matched())
2527 {
2528 return {};
2529 }
2530
2531 return GetCommandProgramLine(params);
2532 }
2533
2534 virtual std::vector<Command*> GetCommands() override
2535 {
2536 if (selectedCommand != nullptr)
2537 {
2538 return selectedCommand->GetCommands();
2539 }
2540
2541 if (Matched())
2542 {
2543 return Group::GetCommands();
2544 }
2545
2546 return { this };
2547 }
2548
2549 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
2550 {
2551 std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
2552 unsigned addindent = 0;
2553
2554 UpdateSubparserHelp(params);
2555
2556 if (!Matched())
2557 {
2558 if (params.showCommandFullHelp)
2559 {
2560 std::ostringstream s;
2561 bool empty = true;
2562 for (const auto &progline: GetCommandProgramLine(params))
2563 {
2564 if (!empty)
2565 {
2566 s << ' ';
2567 }
2568 else
2569 {
2570 empty = false;
2571 }
2572
2573 s << progline;
2574 }
2575
2576 descriptions.emplace_back(s.str(), "", indent);
2577 }
2578 else
2579 {
2580 descriptions.emplace_back(Name(), help, indent);
2581 }
2582
2583 if (!params.showCommandChildren && !params.showCommandFullHelp)
2584 {
2585 return descriptions;
2586 }
2587
2588 addindent = 1;
2589 }
2590
2591 if (params.showCommandFullHelp && !Matched())
2592 {
2593 descriptions.emplace_back("", "", indent + addindent);
2594 descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
2595 descriptions.emplace_back("", "", indent + addindent);
2596 }
2597
2598 for (Base *child: Children())
2599 {
2600 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
2601 {
2602 continue;
2603 }
2604
2605 auto groupDescriptions = child->GetDescription(params, indent + addindent);
2606 descriptions.insert(
2607 std::end(descriptions),
2608 std::make_move_iterator(std::begin(groupDescriptions)),
2609 std::make_move_iterator(std::end(groupDescriptions)));
2610 }
2611
2612 for (auto childDescription: subparserDescription)
2613 {
2614 std::get<2>(childDescription) += indent + addindent;
2615 descriptions.push_back(std::move(childDescription));
2616 }
2617
2618 if (params.showCommandFullHelp && !Matched())
2619 {
2620 descriptions.emplace_back("", "", indent + addindent);
2621 if (!Epilog().empty())
2622 {
2623 descriptions.emplace_back(Epilog(), "", indent + addindent);
2624 descriptions.emplace_back("", "", indent + addindent);
2625 }
2626 }
2627
2628 return descriptions;
2629 }
2630
2631 virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
2632 {
2633 if (!Matched())
2634 {
2635 return;
2636 }
2637
2638 auto onValidationError = [&]
2639 {
2640 std::ostringstream problem;
2641 problem << "Group validation failed somewhere!";
2642#ifdef ARGS_NOEXCEPT
2643 error = Error::Validation;
2644 errorMsg = problem.str();
2645#else
2646 throw ValidationError(problem.str());
2647#endif
2648 };
2649
2650 for (Base *child: Children())
2651 {
2652 if (child->IsGroup() && !child->Matched())
2653 {
2654 onValidationError();
2655 }
2656
2657 child->Validate(shortprefix, longprefix);
2658 }
2659
2660 if (subparser != nullptr)
2661 {
2662 subparser->Validate(shortprefix, longprefix);
2663 if (!subparser->Matched())
2664 {
2665 onValidationError();
2666 }
2667 }
2668
2669 if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
2670 {
2671 std::ostringstream problem;
2672 problem << "Command is required";
2673#ifdef ARGS_NOEXCEPT
2674 error = Error::Validation;
2675 errorMsg = problem.str();
2676#else
2677 throw ValidationError(problem.str());
2678#endif
2679 }
2680 }
2681
2682 virtual void Reset() noexcept override
2683 {
2684 Group::Reset();
2685 selectedCommand = nullptr;
2686 subparserProgramLine.clear();
2687 subparserDescription.clear();
2688 subparserHasFlag = false;
2689 subparserHasPositional = false;
2690 subparserHasCommand = false;
2691#ifdef ARGS_NOEXCEPT
2692 subparserError = Error::None;
2693#endif
2694 }
2695
2696#ifdef ARGS_NOEXCEPT
2698 virtual Error GetError() const override
2699 {
2700 if (!Matched())
2701 {
2702 return Error::None;
2703 }
2704
2705 if (error != Error::None)
2706 {
2707 return error;
2708 }
2709
2710 if (subparserError != Error::None)
2711 {
2712 return subparserError;
2713 }
2714
2715 return Group::GetError();
2716 }
2717#endif
2718 };
2719
2723 {
2724 friend class Subparser;
2725
2726 private:
2727 std::string longprefix;
2728 std::string shortprefix;
2729
2730 std::string longseparator;
2731
2732 std::string terminator;
2733
2734 bool allowJoinedShortValue = true;
2735 bool allowJoinedLongValue = true;
2736 bool allowSeparateShortValue = true;
2737 bool allowSeparateLongValue = true;
2738
2739 bool readCompletion = false;
2740 CompletionFlag *completion = nullptr;
2741
2742 protected:
2743 enum class OptionType
2744 {
2745 LongFlag,
2746 ShortFlag,
2748 };
2749
2750 OptionType ParseOption(const std::string &s, bool allowEmpty = false)
2751 {
2752 const bool matchesLong = s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length());
2753 const bool matchesShort = s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length());
2754
2755 // A chunk can start with both prefixes when one is a prefix of
2756 // the other, or when the long prefix is empty (every string
2757 // starts with it). Resolve to the longer, more specific prefix:
2758 // this keeps the default "--"/"-" preference for long flags
2759 // while letting a short flag be recognised under an empty long
2760 // prefix instead of being swallowed as a nameless long flag.
2761 if (matchesLong && matchesShort)
2762 {
2763 return longprefix.length() >= shortprefix.length() ? OptionType::LongFlag : OptionType::ShortFlag;
2764 }
2765
2766 if (matchesLong)
2767 {
2768 return OptionType::LongFlag;
2769 }
2770
2771 if (matchesShort)
2772 {
2773 return OptionType::ShortFlag;
2774 }
2775
2776 return OptionType::Positional;
2777 }
2778
2779 template <typename It>
2780 bool Complete(FlagBase &flag, It it, It end)
2781 {
2782 auto nextIt = it;
2783 if (!readCompletion || (++nextIt != end))
2784 {
2785 return false;
2786 }
2787
2788 const auto &chunk = *it;
2789 for (auto &choice : flag.HelpChoices(helpParams))
2790 {
2791 AddCompletionReply(chunk, choice);
2792 }
2793
2794#ifndef ARGS_NOEXCEPT
2795 throw Completion(completion->Get());
2796#else
2797 return true;
2798#endif
2799 }
2800
2810 template <typename It>
2811 std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
2812 const bool allowSeparate, const bool allowJoined,
2813 const bool hasJoined, const std::string &joinedArg,
2814 const bool canDiscardJoined, std::vector<std::string> &values)
2815 {
2816 values.clear();
2817
2818 Nargs nargs = flag.NumberOfArguments();
2819
2820 if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined))
2821 {
2822 return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2823 }
2824
2825 if (hasJoined)
2826 {
2827 if (!canDiscardJoined || (allowJoined && nargs.max != 0))
2828 {
2829 values.push_back(joinedArg);
2830 }
2831 } else if (!allowSeparate)
2832 {
2833 if (nargs.min != 0)
2834 {
2835 return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2836 }
2837 } else if (allowSeparate && (!hasJoined || !values.empty())) {
2838 // Only gather separate values when they are allowed. A joined
2839 // value that was discarded rather than taken (short chunks such
2840 // as -nf when joined short values are off) means this flag isn't
2841 // taking an argument here, so the rest of the chunk is flags.
2842 auto valueIt = it;
2843 ++valueIt;
2844
2845 while (valueIt != end &&
2846 *valueIt != terminator &&
2847 values.size() < nargs.max &&
2848 (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2849 {
2850 if (Complete(flag, valueIt, end))
2851 {
2852 // Park `it` on the completion position rather than
2853 // `end`. In ARGS_NOEXCEPT mode Complete returns
2854 // true (no throw), so the caller's for-loop will
2855 // run its ++it after we return; advancing an
2856 // already-end iterator is undefined behavior and
2857 // causes a subsequent out-of-bounds read of the
2858 // arg vector. Since Complete only fires when
2859 // ++nextIt == end, valueIt is the last element,
2860 // and ++(it=valueIt) safely lands on end.
2861 it = valueIt;
2862 return "";
2863 }
2864
2865 values.push_back(*valueIt);
2866 ++it;
2867 ++valueIt;
2868 }
2869 }
2870
2871 if (values.size() > nargs.max)
2872 {
2873 return "Passed an argument into a non-argument flag: " + arg;
2874 } else if (values.size() < nargs.min)
2875 {
2876 if (nargs.min == 1 && nargs.max == 1)
2877 {
2878 return "Flag '" + arg + "' requires an argument but received none";
2879 } else if (nargs.min == 1)
2880 {
2881 return "Flag '" + arg + "' requires at least one argument but received none";
2882 } else if (nargs.min != nargs.max)
2883 {
2884 return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2885 " arguments but received " + std::to_string(values.size());
2886 } else
2887 {
2888 return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2889 " arguments but received " + std::to_string(values.size());
2890 }
2891 }
2892
2893 return {};
2894 }
2895
2896 template <typename It>
2897 bool ParseLong(It &it, It end)
2898 {
2899 const auto &chunk = *it;
2900 const auto argchunk = chunk.substr(longprefix.size());
2901 // Try to separate it, in case of a separator:
2902 const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2903 // If the separator is in the argument, separate it.
2904 const auto arg = (separator != argchunk.npos ?
2905 std::string(argchunk, 0, separator)
2906 : argchunk);
2907 const auto joined = (separator != argchunk.npos ?
2908 argchunk.substr(separator + longseparator.size())
2909 : std::string());
2910
2911 if (auto flag = Match(arg))
2912 {
2913#ifdef ARGS_NOEXCEPT
2914 // Match() may set the flag's error (e.g. Error::Extra when
2915 // Options::Single is violated). In non-noexcept mode that
2916 // path throws and parsing stops before the value is read;
2917 // in noexcept mode we must mirror that and skip the value
2918 // parsing so the previously-stored value is preserved.
2919 if (flag->GetError() != Error::None)
2920 {
2921 return false;
2922 }
2923#endif
2924 std::vector<std::string> values;
2925 const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2926 separator != argchunk.npos, joined, false, values);
2927 if (!errorMessage.empty())
2928 {
2929#ifndef ARGS_NOEXCEPT
2930 throw ParseError(errorMessage);
2931#else
2932 error = Error::Parse;
2933 errorMsg = errorMessage;
2934 return false;
2935#endif
2936 }
2937
2938 if (!readCompletion)
2939 {
2940 flag->ParseValue(values);
2941#ifdef ARGS_NOEXCEPT
2942 // Non-noexcept ParseValue paths throw on Help, reader
2943 // failure, or Map miss, which halts parsing. Mirror
2944 // that here so a later parser-level error (e.g. an
2945 // unknown flag) cannot shadow the flag's error in
2946 // ArgumentParser::GetError().
2947 if (flag->GetError() != Error::None)
2948 {
2949 return false;
2950 }
2951#endif
2952 }
2953
2954 if (flag->KickOut())
2955 {
2956 ++it;
2957 return false;
2958 }
2959 } else
2960 {
2961 const std::string errorMessage("Flag could not be matched: " + arg);
2962#ifndef ARGS_NOEXCEPT
2963 throw ParseError(errorMessage);
2964#else
2965 error = Error::Parse;
2966 errorMsg = errorMessage;
2967 return false;
2968#endif
2969 }
2970
2971 return true;
2972 }
2973
2974 template <typename It>
2975 bool ParseShort(It &it, It end)
2976 {
2977 const auto &chunk = *it;
2978 const auto argchunk = chunk.substr(shortprefix.size());
2979 for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2980 {
2981 const auto arg = *argit;
2982
2983 if (auto flag = Match(arg))
2984 {
2985#ifdef ARGS_NOEXCEPT
2986 // See ParseLong: if Match recorded an error
2987 // (e.g. Options::Single violation), bail before the
2988 // value is parsed so the prior value is preserved.
2989 if (flag->GetError() != Error::None)
2990 {
2991 return false;
2992 }
2993#endif
2994 const std::string value(argit + 1, std::end(argchunk));
2995 std::vector<std::string> values;
2996 const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2997 allowSeparateShortValue, allowJoinedShortValue,
2998 !value.empty(), value, !value.empty(), values);
2999
3000 if (!errorMessage.empty())
3001 {
3002#ifndef ARGS_NOEXCEPT
3003 throw ParseError(errorMessage);
3004#else
3005 error = Error::Parse;
3006 errorMsg = errorMessage;
3007 return false;
3008#endif
3009 }
3010
3011 if (!readCompletion)
3012 {
3013 flag->ParseValue(values);
3014#ifdef ARGS_NOEXCEPT
3015 // See ParseLong: ensure a flag-level error from
3016 // ParseValue (Help, Parse, Map) halts parsing so
3017 // it cannot be shadowed by a later parser error.
3018 if (flag->GetError() != Error::None)
3019 {
3020 return false;
3021 }
3022#endif
3023 }
3024
3025 if (flag->KickOut())
3026 {
3027 ++it;
3028 return false;
3029 }
3030
3031 if (!values.empty())
3032 {
3033 break;
3034 }
3035 } else
3036 {
3037 const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
3038#ifndef ARGS_NOEXCEPT
3039 throw ParseError(errorMessage);
3040#else
3041 error = Error::Parse;
3042 errorMsg = errorMessage;
3043 return false;
3044#endif
3045 }
3046 }
3047
3048 return true;
3049 }
3050
3051 bool AddCompletionReply(const std::string &cur, const std::string &choice)
3052 {
3053 if (cur.empty() || choice.find(cur) == 0)
3054 {
3055 if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
3056 {
3057 completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size()));
3058 } else
3059 {
3060 completion->reply.push_back(choice);
3061 }
3062 return true;
3063 }
3064
3065 return false;
3066 }
3067
3068 template <typename It>
3069 bool Complete(It it, It end, bool terminated)
3070 {
3071 auto nextIt = it;
3072 if (!readCompletion || (++nextIt != end))
3073 {
3074 return false;
3075 }
3076
3077 const auto &chunk = *it;
3078 auto pos = GetNextPositional();
3079 std::vector<Command *> commands = GetCommands();
3080 const auto optionType = ParseOption(chunk, true);
3081
3082 // Once the terminator has been seen the parser treats every
3083 // following chunk as positional, so only positional choices are
3084 // valid completions here. Suggesting flags or commands past the
3085 // terminator offers candidates the parser would then reject.
3086 if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
3087 {
3088 for (auto &cmd : commands)
3089 {
3090 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3091 {
3092 AddCompletionReply(chunk, cmd->Name());
3093 }
3094 }
3095 } else
3096 {
3097 bool hasPositionalCompletion = true;
3098
3099 if (!terminated && !commands.empty())
3100 {
3101 for (auto &cmd : commands)
3102 {
3103 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3104 {
3105 AddCompletionReply(chunk, cmd->Name());
3106 }
3107 }
3108 } else if (pos)
3109 {
3110 if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
3111 {
3112 auto choices = pos->HelpChoices(helpParams);
3113 hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
3114 for (auto &choice : choices)
3115 {
3116 AddCompletionReply(chunk, choice);
3117 }
3118 }
3119 }
3120
3121 if (!terminated && hasPositionalCompletion)
3122 {
3123 auto flags = GetAllFlags();
3124 for (auto flag : flags)
3125 {
3126 if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
3127 {
3128 continue;
3129 }
3130
3131 auto &matcher = flag->GetMatcher();
3132 if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
3133 {
3134 for (auto &flagName : matcher.GetFlagStrings())
3135 {
3136 if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
3137 {
3138 break;
3139 }
3140 }
3141 }
3142 }
3143
3144 if (optionType == OptionType::LongFlag && allowJoinedLongValue)
3145 {
3146 const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
3147 // Only attempt joined-value completion when the
3148 // separator lies at or past the long prefix, so
3149 // there is a (possibly empty) flag name between
3150 // them. With a custom longseparator that overlaps
3151 // the prefix (e.g. LongSeparator("-") under the
3152 // default "--" prefix), an attacker-controlled
3153 // completion word like "--x" puts the separator
3154 // inside the prefix, making `arg` shorter than
3155 // longprefix. arg.substr(longprefix.size()) would
3156 // then throw std::out_of_range, which escapes the
3157 // parser as a non-args exception (bypassing the
3158 // documented catch(args::Error) idiom) and is
3159 // thrown even under ARGS_NOEXCEPT.
3160 if (separator != chunk.npos && separator >= longprefix.size())
3161 {
3162 std::string arg(chunk, 0, separator);
3163 if (auto flag = this->Match(arg.substr(longprefix.size())))
3164 {
3165 for (auto &choice : flag->HelpChoices(helpParams))
3166 {
3167 AddCompletionReply(chunk, arg + longseparator + choice);
3168 }
3169 }
3170 }
3171 } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
3172 {
3173 if (chunk.size() > shortprefix.size() + 1)
3174 {
3175 auto arg = chunk.at(shortprefix.size());
3176 //TODO: support -abcVALUE where a and b take no value
3177 if (auto flag = this->Match(arg))
3178 {
3179 for (auto &choice : flag->HelpChoices(helpParams))
3180 {
3181 AddCompletionReply(chunk, shortprefix + arg + choice);
3182 }
3183 }
3184 }
3185 }
3186 }
3187 }
3188
3189#ifndef ARGS_NOEXCEPT
3190 throw Completion(completion->Get());
3191#else
3192 return true;
3193#endif
3194 }
3195
3196 template <typename It>
3197 It Parse(It begin, It end)
3198 {
3199 bool terminated = false;
3200 std::vector<Command *> commands = GetCommands();
3201
3202 // Check all arg chunks
3203 for (auto it = begin; it != end; ++it)
3204 {
3205 if (Complete(it, end, terminated))
3206 {
3207 return end;
3208 }
3209
3210 const auto &chunk = *it;
3211
3212 if (!terminated && chunk == terminator)
3213 {
3214 terminated = true;
3215 } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
3216 {
3217 if (!ParseLong(it, end))
3218 {
3219 return it;
3220 }
3221 } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
3222 {
3223 if (!ParseShort(it, end))
3224 {
3225 return it;
3226 }
3227 } else if (!terminated && !commands.empty())
3228 {
3229 auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
3230 if (itCommand == commands.end())
3231 {
3232 const std::string errorMessage("Unknown command: " + chunk);
3233#ifndef ARGS_NOEXCEPT
3234 throw ParseError(errorMessage);
3235#else
3236 error = Error::Parse;
3237 errorMsg = errorMessage;
3238 return it;
3239#endif
3240 }
3241
3242 SelectCommand(*itCommand);
3243
3244 if (const auto &coroutine = GetCoroutine())
3245 {
3246 ++it;
3247 RaiiSubparser coro(*this, std::vector<std::string>(it, end));
3248 coroutine(coro.Parser());
3249#ifdef ARGS_NOEXCEPT
3250 error = GetError();
3251 if (error != Error::None)
3252 {
3253 return end;
3254 }
3255
3256 if (!coro.Parser().IsParsed())
3257 {
3258 error = Error::Usage;
3259 return end;
3260 }
3261#else
3262 if (!coro.Parser().IsParsed())
3263 {
3264 throw UsageError("Subparser::Parse was not called");
3265 }
3266#endif
3267
3268 break;
3269 }
3270
3271 commands = GetCommands();
3272 } else
3273 {
3274 auto pos = GetNextPositional();
3275 if (pos)
3276 {
3277 pos->ParseValue(chunk);
3278#ifdef ARGS_NOEXCEPT
3279 if (pos->GetError() != Error::None)
3280 {
3281 return it;
3282 }
3283#endif
3284
3285 if (pos->KickOut())
3286 {
3287 return ++it;
3288 }
3289 } else
3290 {
3291 const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
3292#ifndef ARGS_NOEXCEPT
3293 throw ParseError(errorMessage);
3294#else
3295 error = Error::Parse;
3296 errorMsg = errorMessage;
3297 return it;
3298#endif
3299 }
3300 }
3301
3302 if (!readCompletion && completion != nullptr && completion->Matched())
3303 {
3304#ifdef ARGS_NOEXCEPT
3305 if (completion->GetError() != Error::None)
3306 {
3307 error = completion->GetError();
3308 if (errorMsg.empty())
3309 {
3310 errorMsg = completion->GetErrorMsg();
3311 }
3312 return it;
3313 }
3314
3315 error = Error::Completion;
3316#endif
3317 readCompletion = true;
3318 ++it;
3319 const auto argsLeft = static_cast<size_t>(std::distance(it, end));
3320 if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
3321 {
3322#ifndef ARGS_NOEXCEPT
3323 throw Completion("");
3324#else
3325 return end;
3326#endif
3327 }
3328
3329 ++it;
3330 std::vector<std::string> curArgs;
3331 curArgs.reserve(completion->cword);
3332 auto curIt = it;
3333 for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
3334 {
3335 curArgs.push_back(*curIt);
3336 }
3337
3338 if (completion->syntax == "bash")
3339 {
3340 // bash tokenizes --flag=value as --flag=value
3341 // Security fix: Use size_t arithmetic throughout to avoid conversion issues
3342 for (size_t idx = 0; idx < curArgs.size(); )
3343 {
3344 if (idx > 0 && curArgs[idx] == "=")
3345 {
3346 size_t prev_idx = idx - 1; // Safe since we checked idx > 0
3347 curArgs[prev_idx] += "=";
3348 size_t next_idx = 0;
3349 if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
3350 {
3351 curArgs[prev_idx] += curArgs[next_idx];
3352 // Erase the '=' token and the following value token.
3353 size_t erase_end = 0;
3354 if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
3355 {
3356 typedef std::vector<std::string>::difference_type diff_t;
3357 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx),
3358 curArgs.begin() + static_cast<diff_t>(erase_end));
3359 }
3360 } else
3361 {
3362 // Safe erase of single '=' token at the end
3363 typedef std::vector<std::string>::difference_type diff_t;
3364 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx));
3365 }
3366 // Do not increment idx - next element slides into current position
3367 } else
3368 {
3369 ++idx;
3370 }
3371 }
3372
3373 }
3374#ifndef ARGS_NOEXCEPT
3375 try
3376 {
3377 Parse(curArgs.begin(), curArgs.end());
3378 throw Completion("");
3379 }
3380 catch (Completion &)
3381 {
3382 throw;
3383 }
3384 catch (args::Error&)
3385 {
3386 throw Completion("");
3387 }
3388#else
3389 // Discard the nested Parse's return value: it points
3390 // into the local curArgs vector, which is destroyed
3391 // when this function returns, leaving the caller with
3392 // a dangling iterator that would be compared against
3393 // the outer `end` in ParseCLI. Return the outer
3394 // `end` instead so the iterator stays in the caller's
3395 // container.
3396 Parse(curArgs.begin(), curArgs.end());
3397 error = Error::Completion;
3398 errorMsg.clear();
3399 return end;
3400#endif
3401 }
3402 }
3403
3404 Validate(shortprefix, longprefix);
3405 return end;
3406 }
3407
3408 public:
3409 HelpParams helpParams;
3410
3411 ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
3412 {
3413 Description(description_);
3414 Epilog(epilog_);
3415 LongPrefix("--");
3416 ShortPrefix("-");
3417 LongSeparator("=");
3418 Terminator("--");
3419 SetArgumentSeparations(true, true, true, true);
3420 matched = true;
3421 }
3422
3423 void AddCompletion(CompletionFlag &completionFlag)
3424 {
3425 // Only take the pointer once registration has succeeded: Add()
3426 // throws on a duplicate flag, and the flag it was handed is
3427 // gone by the time that error reaches the caller.
3428 Add(completionFlag);
3429 completion = &completionFlag;
3430 }
3431
3434 const std::string &Prog() const
3435 { return helpParams.programName; }
3438 void Prog(const std::string &prog_)
3439 { this->helpParams.programName = prog_; }
3440
3443 const std::string &LongPrefix() const
3444 { return longprefix; }
3447 void LongPrefix(const std::string &longprefix_)
3448 {
3449 this->longprefix = longprefix_;
3450 this->helpParams.longPrefix = longprefix_;
3451 }
3452
3455 const std::string &ShortPrefix() const
3456 { return shortprefix; }
3459 void ShortPrefix(const std::string &shortprefix_)
3460 {
3461 this->shortprefix = shortprefix_;
3462 this->helpParams.shortPrefix = shortprefix_;
3463 }
3464
3467 const std::string &LongSeparator() const
3468 { return longseparator; }
3471 void LongSeparator(const std::string &longseparator_)
3472 {
3473 if (longseparator_.empty())
3474 {
3475 const std::string errorMessage("longseparator can not be set to empty");
3476#ifdef ARGS_NOEXCEPT
3477 error = Error::Usage;
3478 errorMsg = errorMessage;
3479#else
3480 throw UsageError(errorMessage);
3481#endif
3482 } else
3483 {
3484 this->longseparator = longseparator_;
3485 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3486 }
3487 }
3488
3491 const std::string &Terminator() const
3492 { return terminator; }
3495 void Terminator(const std::string &terminator_)
3496 { this->terminator = terminator_; }
3497
3503 bool &allowJoinedShortValue_,
3504 bool &allowJoinedLongValue_,
3505 bool &allowSeparateShortValue_,
3506 bool &allowSeparateLongValue_) const
3507 {
3508 allowJoinedShortValue_ = this->allowJoinedShortValue;
3509 allowJoinedLongValue_ = this->allowJoinedLongValue;
3510 allowSeparateShortValue_ = this->allowSeparateShortValue;
3511 allowSeparateLongValue_ = this->allowSeparateLongValue;
3512 }
3513
3522 const bool allowJoinedShortValue_,
3523 const bool allowJoinedLongValue_,
3524 const bool allowSeparateShortValue_,
3525 const bool allowSeparateLongValue_)
3526 {
3527 this->allowJoinedShortValue = allowJoinedShortValue_;
3528 this->allowJoinedLongValue = allowJoinedLongValue_;
3529 this->allowSeparateShortValue = allowSeparateShortValue_;
3530 this->allowSeparateLongValue = allowSeparateLongValue_;
3531
3532 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3533 this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
3534 }
3535
3538 void Help(std::ostream &help_) const
3539 {
3540 auto &command = SelectedCommand();
3541 const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
3542 const auto desc_indent = helpParams.descriptionindent;
3543 const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0;
3544 const auto description_text = Wrap(commandDescription, effective_desc_width);
3545 const auto epilog_text = Wrap(command.Epilog(), effective_desc_width);
3546
3547 const bool hasoptions = command.HasFlag();
3548 const bool hasarguments = command.HasPositional();
3549
3550 std::vector<std::string> prognameline;
3551 prognameline.push_back(helpParams.usageString);
3552 prognameline.push_back(Prog());
3553 auto commandProgLine = command.GetProgramLine(helpParams);
3554 prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
3555
3556 const auto prog_sum = helpParams.progindent + helpParams.progtailindent;
3557 const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0;
3558 const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0;
3559 const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
3560 effective_prog_width,
3561 effective_prog_first);
3562 auto progit = std::begin(proglines);
3563 if (progit != std::end(proglines))
3564 {
3565 help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
3566 ++progit;
3567 }
3568 for (; progit != std::end(proglines); ++progit)
3569 {
3570 help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
3571 }
3572
3573 help_ << '\n';
3574
3575 if (!description_text.empty())
3576 {
3577 for (const auto &line: description_text)
3578 {
3579 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3580 }
3581 help_ << "\n";
3582 }
3583
3584 bool lastDescriptionIsNewline = false;
3585
3586 if (!helpParams.optionsString.empty())
3587 {
3588 help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
3589 }
3590
3591 for (const auto &desc: command.GetDescription(helpParams, 0))
3592 {
3593 lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
3594 const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
3595 const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter;
3596 const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0;
3597 const auto flags = Wrap(std::get<0>(desc), effective_flag_width);
3598 const auto info_sum = helpParams.helpindent + groupindent;
3599 const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0;
3600 const auto info = Wrap(std::get<1>(desc), effective_info_width);
3601
3602 std::string::size_type flagssize = 0;
3603 for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
3604 {
3605 if (flagsit != std::begin(flags))
3606 {
3607 help_ << '\n';
3608 }
3609 help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
3610 flagssize = Glyphs(*flagsit);
3611 }
3612
3613 auto infoit = std::begin(info);
3614 // groupindent is on both sides of this inequality, and therefore can be removed
3615 if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
3616 {
3617 help_ << '\n';
3618 } else
3619 {
3620 // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
3621 const auto indent_sum = helpParams.flagindent + flagssize;
3622 const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0;
3623 help_ << std::string(effective_space, ' ') << *infoit << '\n';
3624 ++infoit;
3625 }
3626 for (; infoit != std::end(info); ++infoit)
3627 {
3628 help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
3629 }
3630 }
3631 if (hasoptions && hasarguments && helpParams.showTerminator)
3632 {
3633 lastDescriptionIsNewline = false;
3634 const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0;
3635 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))
3636 {
3637 help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
3638 }
3639 }
3640
3641 if (!lastDescriptionIsNewline)
3642 {
3643 help_ << "\n";
3644 }
3645
3646 for (const auto &line: epilog_text)
3647 {
3648 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3649 }
3650 }
3651
3656 std::string Help() const
3657 {
3658 std::ostringstream help_;
3659 Help(help_);
3660 return help_.str();
3661 }
3662
3663 virtual void Reset() noexcept override
3664 {
3665 Command::Reset();
3666 matched = true;
3667 readCompletion = false;
3668 }
3669
3676 template <typename It>
3677 It ParseArgs(It begin, It end)
3678 {
3679 // Reset all Matched statuses and errors
3680 Reset();
3681#ifdef ARGS_NOEXCEPT
3682 error = GetError();
3683 if (error != Error::None)
3684 {
3685 return end;
3686 }
3687#endif
3688 return Parse(begin, end);
3689 }
3690
3696 template <typename T>
3697 auto ParseArgs(const T &args) -> decltype(std::begin(args))
3698 {
3699 return ParseArgs(std::begin(args), std::end(args));
3700 }
3701
3708 bool ParseCLI(const int argc, const char * const * argv)
3709 {
3710 if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty())
3711 {
3712 Prog(argv[0]);
3713 }
3714
3715 std::vector<std::string> args;
3716 if (argc > 1 && argv != nullptr)
3717 {
3718 args.assign(argv + 1, argv + argc);
3719 }
3720
3721 return ParseArgs(args) == std::end(args);
3722 }
3723
3724 template <typename T>
3725 bool ParseCLI(const T &args)
3726 {
3727 return ParseArgs(args) == std::end(args);
3728 }
3729 };
3730
3731 inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
3732 : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
3733 {
3734 command.subparser = &parser;
3735 }
3736
3737 inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
3738 {
3739 command.subparser = &parser;
3740 }
3741
3742 inline void Subparser::Parse()
3743 {
3744 isParsed = true;
3745 Reset();
3746 command.subparserDescription = GetDescription(helpParams, 0);
3747 command.subparserHasFlag = HasFlag();
3748 command.subparserHasPositional = HasPositional();
3749 command.subparserHasCommand = HasCommand();
3750 command.subparserProgramLine = GetProgramLine(helpParams);
3751 if (parser == nullptr)
3752 {
3753#ifndef ARGS_NOEXCEPT
3754 throw args::SubparserError();
3755#else
3756 error = Error::Subparser;
3757 return;
3758#endif
3759 }
3760
3761 auto it = parser->Parse(args.begin(), args.end());
3762 command.Validate(parser->ShortPrefix(), parser->LongPrefix());
3763 kicked.assign(it, args.end());
3764
3765#ifdef ARGS_NOEXCEPT
3766 command.subparserError = GetError();
3767#endif
3768 }
3769
3770 inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
3771 {
3772 parser.Help(os);
3773 return os;
3774 }
3775
3778 class Flag : public FlagBase
3779 {
3780 public:
3781 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
3782 {
3783 group_.Add(*this);
3784 }
3785
3786 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)
3787 {
3788 }
3789
3790 virtual ~Flag() {}
3791
3794 bool Get() const
3795 {
3796 return Matched();
3797 }
3798
3799 virtual Nargs NumberOfArguments() const noexcept override
3800 {
3801 return 0;
3802 }
3803
3804 virtual void ParseValue(const std::vector<std::string>&) override
3805 {
3806 }
3807 };
3808
3813 class HelpFlag : public Flag
3814 {
3815 public:
3816 HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
3817
3818 virtual ~HelpFlag() {}
3819
3820 virtual void ParseValue(const std::vector<std::string> &)
3821 {
3822#ifdef ARGS_NOEXCEPT
3823 error = Error::Help;
3824 errorMsg = Name();
3825#else
3826 throw Help(Name());
3827#endif
3828 }
3829
3832 bool Get() const noexcept
3833 {
3834 return Matched();
3835 }
3836 };
3837
3840 class CounterFlag : public Flag
3841 {
3842 private:
3843 const int startcount;
3844 int count;
3845
3846 public:
3847 CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
3848 Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
3849
3850 virtual ~CounterFlag() {}
3851
3852 virtual FlagBase *Match(const EitherFlag &arg) override
3853 {
3854 auto me = FlagBase::Match(arg);
3855 if (me)
3856 {
3857#ifdef ARGS_NOEXCEPT
3858 // Suppress increment when FlagBase::Match recorded an
3859 // error on this same call (e.g. Options::Single violated).
3860 // In non-noexcept mode that path would have thrown before
3861 // reaching here and the count would not have advanced.
3862 if (GetError() != Error::None)
3863 {
3864 return me;
3865 }
3866#endif
3867 ++count;
3868 }
3869 return me;
3870 }
3871
3874 int &Get() noexcept
3875 {
3876 return count;
3877 }
3878
3879 int &operator *() noexcept {
3880 return count;
3881 }
3882
3883 const int &operator *() const noexcept {
3884 return count;
3885 }
3886
3887 virtual void Reset() noexcept override
3888 {
3889 FlagBase::Reset();
3890 count = startcount;
3891 }
3892 };
3893
3896 class ActionFlag : public FlagBase
3897 {
3898 private:
3899 std::function<void(const std::vector<std::string> &)> action;
3900 Nargs nargs;
3901
3902 public:
3903 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_ = {}):
3904 FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
3905 {
3906 group_.Add(*this);
3907 }
3908
3909 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
3910 FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
3911 {
3912 group_.Add(*this);
3913 action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
3914 }
3915
3916 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
3917 FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
3918 {
3919 group_.Add(*this);
3920 action = [action_](const std::vector<std::string> &) { return action_(); };
3921 }
3922
3923 virtual Nargs NumberOfArguments() const noexcept override
3924 { return nargs; }
3925
3926 virtual void ParseValue(const std::vector<std::string> &value) override
3927 { action(value); }
3928 };
3929
3937 {
3938 private:
3939 template <typename T>
3940 static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type
3941 HasUnsignedNegativeSign(const std::string &value)
3942 {
3943 const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c)
3944 {
3945 return std::isspace(static_cast<unsigned char>(c)) != 0;
3946 });
3947
3948 return firstNonSpace != value.end() && *firstNonSpace == '-';
3949 }
3950
3951 template <typename T>
3952 static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type
3953 HasUnsignedNegativeSign(const std::string &)
3954 {
3955 return false;
3956 }
3957
3958 public:
3959 template <typename T>
3960 typename std::enable_if<
3961 std::is_integral<T>::value &&
3962 !std::is_same<T, bool>::value &&
3963 !std::is_same<T, char>::value &&
3964 !std::is_same<T, signed char>::value &&
3965 !std::is_same<T, unsigned char>::value,
3966 bool>::type
3967 ParseNumericValue(const std::string &value, T &destination)
3968 {
3969 if (HasUnsignedNegativeSign<T>(value))
3970 {
3971 return false;
3972 }
3973
3974 const char *begin = value.c_str();
3975 // The true end of the value, derived from its length rather than
3976 // from the first NUL. strtoull/strtoll treat the buffer as a C
3977 // string and stop at an embedded '\0', so checking `*end == '\0'`
3978 // for "no trailing data" is defeated by a value like "12\0junk":
3979 // end lands on the embedded NUL and the junk after it is silently
3980 // accepted. Comparing against `stop` validates the whole string
3981 // and matches the istringstream-based reader used for other types.
3982 const char *const stop = begin + value.size();
3983
3984 // C++11-compatible: use strtoull/strtoll. Hardening retained from
3985 // the original from_chars draft (errno save/restore, ERANGE check,
3986 // narrowing range check, trailing-whitespace tolerance). No
3987 // unconditional dependency on <charconv> / C++17.
3988 const int saved_errno = errno;
3989 errno = 0;
3990
3991 char *end = nullptr;
3992
3993 if (std::is_unsigned<T>::value)
3994 {
3995 const unsigned long long parsed = std::strtoull(begin, &end, 0);
3996 if (end == begin)
3997 {
3998 errno = saved_errno;
3999 return false;
4000 }
4001 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
4002 {
4003 ++end;
4004 }
4005 if (end != stop || errno == ERANGE ||
4006 parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
4007 {
4008 errno = saved_errno;
4009 return false;
4010 }
4011
4012 destination = static_cast<T>(parsed);
4013 }
4014 else
4015 {
4016 const long long parsed = std::strtoll(begin, &end, 0);
4017 if (end == begin)
4018 {
4019 errno = saved_errno;
4020 return false;
4021 }
4022 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
4023 {
4024 ++end;
4025 }
4026 if (end != stop || errno == ERANGE ||
4027 parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
4028 parsed > static_cast<long long>(std::numeric_limits<T>::max()))
4029 {
4030 errno = saved_errno;
4031 return false;
4032 }
4033
4034 destination = static_cast<T>(parsed);
4035 }
4036
4037 errno = saved_errno;
4038 return true;
4039 }
4040
4041 template <typename T>
4042 typename std::enable_if<
4043 !std::is_integral<T>::value ||
4044 std::is_same<T, bool>::value ||
4045 std::is_same<T, char>::value ||
4046 std::is_same<T, signed char>::value ||
4047 std::is_same<T, unsigned char>::value,
4048 bool>::type
4049 ParseNumericValue(const std::string &value, T &destination)
4050 {
4051 std::istringstream ss(value);
4052 // Pin parsing to the C locale so that the decimal separator and
4053 // thousands grouping behavior do not silently depend on whatever
4054 // std::locale::global was last set to elsewhere in the process.
4055 // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in
4056 // any locale whose numpunct facet treats ',' as the decimal point.
4057 ss.imbue(std::locale::classic());
4058 ss >> destination;
4059 if (ss.fail())
4060 {
4061 return false;
4062 }
4063
4064 // Check for trailing garbage by attempting to extract any remaining characters.
4065 // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit
4066 // on EOF, causing false rejection of valid input.
4067 char extra = '\0';
4068 ss >> std::ws >> extra;
4069 // If extraction succeeded, there's trailing garbage (return false).
4070 // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true).
4071 // If extraction failed for other reasons, it's invalid (return false).
4072 if (ss.fail())
4073 {
4074 // Clear the failbit to check if EOF is the only issue
4075 ss.clear(ss.rdstate() & ~std::ios::failbit);
4076 return ss.eof();
4077 }
4078 // Extraction succeeded, meaning there's trailing garbage
4079 return false;
4080 }
4081
4082 template <typename T>
4083 typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
4084 operator ()(const std::string &name, const std::string &value, T &destination)
4085 {
4086 const bool success = ParseNumericValue(value, destination);
4087 if (!success)
4088 {
4089#ifdef ARGS_NOEXCEPT
4090 (void)name;
4091 return false;
4092#else
4093 std::ostringstream problem;
4094 problem << "Argument '" << name << "' received invalid value type '" << value << "'";
4095 throw ParseError(problem.str());
4096#endif
4097 }
4098 return true;
4099 }
4100
4101 template <typename T>
4102 typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
4103 operator()(const std::string &, const std::string &value, T &destination)
4104 {
4105 destination = value;
4106 return true;
4107 }
4108 };
4109
4115 template <
4116 typename T,
4117 typename Reader = ValueReader>
4119 {
4120 protected:
4121 T value;
4122 T defaultValue;
4123
4124 virtual std::string GetDefaultString(const HelpParams&) const override
4125 {
4126 return detail::ToString(defaultValue);
4127 }
4128
4129 private:
4130 Reader reader;
4131
4132 public:
4133
4134 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_)
4135 {
4136 group_.Add(*this);
4137 }
4138
4139 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)
4140 {
4141 }
4142
4143 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
4144 {
4145 }
4146
4147 virtual ~ValueFlag() {}
4148
4149 virtual void ParseValue(const std::vector<std::string> &values_) override
4150 {
4151 const std::string &value_ = values_.at(0);
4152
4153#ifdef ARGS_NOEXCEPT
4154 if (!reader(name, value_, this->value))
4155 {
4156 error = Error::Parse;
4157 }
4158#else
4159 reader(name, value_, this->value);
4160#endif
4161 }
4162
4163 virtual void Reset() noexcept override
4164 {
4165 ValueFlagBase::Reset();
4166 value = defaultValue;
4167 }
4168
4171 T &Get() noexcept
4172 {
4173 return value;
4174 }
4175
4178 T &operator *() noexcept
4179 {
4180 return value;
4181 }
4182
4185 const T &operator *() const noexcept
4186 {
4187 return value;
4188 }
4189
4192 T *operator ->() noexcept
4193 {
4194 return &value;
4195 }
4196
4199 const T *operator ->() const noexcept
4200 {
4201 return &value;
4202 }
4203
4206 const T &GetDefault() noexcept
4207 {
4208 return defaultValue;
4209 }
4210 };
4211
4217 template <
4218 typename T,
4219 typename Reader = ValueReader>
4220 class ImplicitValueFlag : public ValueFlag<T, Reader>
4221 {
4222 protected:
4223 T implicitValue;
4224
4225 public:
4226
4227 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
4228 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
4229 {
4230 }
4231
4232 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
4233 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
4234 {
4235 }
4236
4237 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
4238 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
4239 {
4240 }
4241
4242 virtual ~ImplicitValueFlag() {}
4243
4244 virtual Nargs NumberOfArguments() const noexcept override
4245 {
4246 return {0, 1};
4247 }
4248
4249 virtual void ParseValue(const std::vector<std::string> &value_) override
4250 {
4251 if (value_.empty())
4252 {
4253 this->value = implicitValue;
4254 } else
4255 {
4257 }
4258 }
4259 };
4260
4265 template <typename T>
4266 class ConstantFlag : public Flag
4267 {
4268 T value;
4269
4270 public:
4271
4272 ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_, const T& value_):
4273 Flag(group_, name_, help_, std::move(matcher_), options_),
4274 value(value_)
4275 {}
4276
4277 ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T& value_, bool extraError_ = false):
4278 Flag(group_, name_, help_, std::move(matcher_), extraError_),
4279 value(value_)
4280 {}
4281
4282 T operator * () const noexcept
4283 {
4284 return value;
4285 }
4286
4287 T Get() const noexcept
4288 {
4289 return value;
4290 }
4291
4292 const T *operator -> () const noexcept
4293 {
4294 return &value;
4295 }
4296 };
4297
4304 template <
4305 typename T,
4306 template <typename...> class List = detail::vector,
4307 typename Reader = ValueReader>
4309 {
4310 protected:
4311
4312 List<T> values;
4313 const List<T> defaultValues;
4314 Nargs nargs;
4315 Reader reader;
4316
4317 public:
4318
4319 typedef List<T> Container;
4320 typedef T value_type;
4321 typedef typename Container::allocator_type allocator_type;
4322 typedef typename Container::pointer pointer;
4323 typedef typename Container::const_pointer const_pointer;
4324 typedef T& reference;
4325 typedef const T& const_reference;
4326 typedef typename Container::size_type size_type;
4327 typedef typename Container::difference_type difference_type;
4328 typedef typename Container::iterator iterator;
4329 typedef typename Container::const_iterator const_iterator;
4330 typedef std::reverse_iterator<iterator> reverse_iterator;
4331 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4332
4333 NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
4334 : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
4335 {
4336 group_.Add(*this);
4337 }
4338
4339 virtual ~NargsValueFlag() {}
4340
4341 virtual Nargs NumberOfArguments() const noexcept override
4342 {
4343 return nargs;
4344 }
4345
4346 virtual void ParseValue(const std::vector<std::string> &values_) override
4347 {
4348 values.clear();
4349
4350 for (const std::string &value : values_)
4351 {
4352 T v {};
4353#ifdef ARGS_NOEXCEPT
4354 if (!reader(name, value, v))
4355 {
4356 error = Error::Parse;
4357 return;
4358 }
4359#else
4360 reader(name, value, v);
4361#endif
4362 values.insert(std::end(values), v);
4363 }
4364 }
4365
4366 List<T> &Get() noexcept
4367 {
4368 return values;
4369 }
4370
4373 List<T> &operator *() noexcept
4374 {
4375 return values;
4376 }
4377
4380 const List<T> &operator *() const noexcept
4381 {
4382 return values;
4383 }
4384
4387 List<T> *operator ->() noexcept
4388 {
4389 return &values;
4390 }
4391
4394 const List<T> *operator ->() const noexcept
4395 {
4396 return &values;
4397 }
4398
4399 iterator begin() noexcept
4400 {
4401 return values.begin();
4402 }
4403
4404 const_iterator begin() const noexcept
4405 {
4406 return values.begin();
4407 }
4408
4409 const_iterator cbegin() const noexcept
4410 {
4411 return values.cbegin();
4412 }
4413
4414 iterator end() noexcept
4415 {
4416 return values.end();
4417 }
4418
4419 const_iterator end() const noexcept
4420 {
4421 return values.end();
4422 }
4423
4424 const_iterator cend() const noexcept
4425 {
4426 return values.cend();
4427 }
4428
4429 virtual void Reset() noexcept override
4430 {
4431 FlagBase::Reset();
4432 values = defaultValues;
4433 }
4434
4435 virtual FlagBase *Match(const EitherFlag &arg) override
4436 {
4437 const bool wasMatched = Matched();
4438 auto me = FlagBase::Match(arg);
4439 if (me && !wasMatched)
4440 {
4441 values.clear();
4442 }
4443 return me;
4444 }
4445 };
4446
4453 template <
4454 typename T,
4455 template <typename...> class List = detail::vector,
4456 typename Reader = ValueReader>
4458 {
4459 private:
4460 using Container = List<T>;
4461 Container values;
4462 const Container defaultValues;
4463 Reader reader;
4464
4465 public:
4466
4467 typedef T value_type;
4468 typedef typename Container::allocator_type allocator_type;
4469 typedef typename Container::pointer pointer;
4470 typedef typename Container::const_pointer const_pointer;
4471 typedef T& reference;
4472 typedef const T& const_reference;
4473 typedef typename Container::size_type size_type;
4474 typedef typename Container::difference_type difference_type;
4475 typedef typename Container::iterator iterator;
4476 typedef typename Container::const_iterator const_iterator;
4477 typedef std::reverse_iterator<iterator> reverse_iterator;
4478 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4479
4480 ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
4481 ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
4482 {
4483 group_.Add(*this);
4484 }
4485
4486 virtual ~ValueFlagList() {}
4487
4488 virtual void ParseValue(const std::vector<std::string> &values_) override
4489 {
4490 const std::string &value_ = values_.at(0);
4491
4492 T v{};
4493#ifdef ARGS_NOEXCEPT
4494 if (!reader(name, value_, v))
4495 {
4496 error = Error::Parse;
4497 return;
4498 }
4499#else
4500 reader(name, value_, v);
4501#endif
4502 values.insert(std::end(values), v);
4503 }
4504
4507 Container &Get() noexcept
4508 {
4509 return values;
4510 }
4511
4514 Container &operator *() noexcept
4515 {
4516 return values;
4517 }
4518
4521 const Container &operator *() const noexcept
4522 {
4523 return values;
4524 }
4525
4528 Container *operator ->() noexcept
4529 {
4530 return &values;
4531 }
4532
4535 const Container *operator ->() const noexcept
4536 {
4537 return &values;
4538 }
4539
4540 virtual std::string Name() const override
4541 {
4542 return name + std::string("...");
4543 }
4544
4545 virtual void Reset() noexcept override
4546 {
4547 ValueFlagBase::Reset();
4548 values = defaultValues;
4549 }
4550
4551 virtual FlagBase *Match(const EitherFlag &arg) override
4552 {
4553 const bool wasMatched = Matched();
4554 auto me = FlagBase::Match(arg);
4555 if (me && !wasMatched)
4556 {
4557 values.clear();
4558 }
4559 return me;
4560 }
4561
4562 iterator begin() noexcept
4563 {
4564 return values.begin();
4565 }
4566
4567 const_iterator begin() const noexcept
4568 {
4569 return values.begin();
4570 }
4571
4572 const_iterator cbegin() const noexcept
4573 {
4574 return values.cbegin();
4575 }
4576
4577 iterator end() noexcept
4578 {
4579 return values.end();
4580 }
4581
4582 const_iterator end() const noexcept
4583 {
4584 return values.end();
4585 }
4586
4587 const_iterator cend() const noexcept
4588 {
4589 return values.cend();
4590 }
4591 };
4592
4600 template <
4601 typename K,
4602 typename T,
4603 typename Reader = ValueReader,
4604 template <typename...> class Map = detail::unordered_map>
4605 class MapFlag : public ValueFlagBase
4606 {
4607 private:
4608 const Map<K, T> map;
4609 T value;
4610 const T defaultValue;
4611 Reader reader;
4612
4613 protected:
4614 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4615 {
4616 return detail::MapKeysToStrings(map);
4617 }
4618
4619 public:
4620
4621 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_)
4622 {
4623 group_.Add(*this);
4624 }
4625
4626 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)
4627 {
4628 }
4629
4630 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_)
4631 {
4632 }
4633
4634 virtual ~MapFlag() {}
4635
4636 virtual void ParseValue(const std::vector<std::string> &values_) override
4637 {
4638 const std::string &value_ = values_.at(0);
4639
4640 K key{};
4641#ifdef ARGS_NOEXCEPT
4642 if (!reader(name, value_, key))
4643 {
4644 error = Error::Parse;
4645 return;
4646 }
4647#else
4648 reader(name, value_, key);
4649#endif
4650 auto it = map.find(key);
4651 if (it == std::end(map))
4652 {
4653 std::ostringstream problem;
4654 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4655#ifdef ARGS_NOEXCEPT
4656 error = Error::Map;
4657 errorMsg = problem.str();
4658#else
4659 throw MapError(problem.str());
4660#endif
4661 } else
4662 {
4663 this->value = it->second;
4664 }
4665 }
4666
4669 T &Get() noexcept
4670 {
4671 return value;
4672 }
4673
4676 T &operator *() noexcept
4677 {
4678 return value;
4679 }
4680
4683 const T &operator *() const noexcept
4684 {
4685 return value;
4686 }
4687
4690 T *operator ->() noexcept
4691 {
4692 return &value;
4693 }
4694
4697 const T *operator ->() const noexcept
4698 {
4699 return &value;
4700 }
4701
4702 virtual void Reset() noexcept override
4703 {
4704 ValueFlagBase::Reset();
4705 value = defaultValue;
4706 }
4707 };
4708
4717 template <
4718 typename K,
4719 typename T,
4720 template <typename...> class List = detail::vector,
4721 typename Reader = ValueReader,
4722 template <typename...> class Map = detail::unordered_map>
4724 {
4725 private:
4726 using Container = List<T>;
4727 const Map<K, T> map;
4728 Container values;
4729 const Container defaultValues;
4730 Reader reader;
4731
4732 protected:
4733 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4734 {
4735 return detail::MapKeysToStrings(map);
4736 }
4737
4738 public:
4739 typedef T value_type;
4740 typedef typename Container::allocator_type allocator_type;
4741 typedef typename Container::pointer pointer;
4742 typedef typename Container::const_pointer const_pointer;
4743 typedef T& reference;
4744 typedef const T& const_reference;
4745 typedef typename Container::size_type size_type;
4746 typedef typename Container::difference_type difference_type;
4747 typedef typename Container::iterator iterator;
4748 typedef typename Container::const_iterator const_iterator;
4749 typedef std::reverse_iterator<iterator> reverse_iterator;
4750 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4751
4752 MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4753 ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4754 {
4755 group_.Add(*this);
4756 }
4757
4758 virtual ~MapFlagList() {}
4759
4760 virtual void ParseValue(const std::vector<std::string> &values_) override
4761 {
4762 const std::string &value_ = values_.at(0);
4763
4764 K key{};
4765#ifdef ARGS_NOEXCEPT
4766 if (!reader(name, value_, key))
4767 {
4768 error = Error::Parse;
4769 return;
4770 }
4771#else
4772 reader(name, value_, key);
4773#endif
4774 auto it = map.find(key);
4775 if (it == std::end(map))
4776 {
4777 std::ostringstream problem;
4778 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4779#ifdef ARGS_NOEXCEPT
4780 error = Error::Map;
4781 errorMsg = problem.str();
4782#else
4783 throw MapError(problem.str());
4784#endif
4785 } else
4786 {
4787 detail::AppendMappedValue(values, it->second, 0);
4788 }
4789 }
4790
4793 Container &Get() noexcept
4794 {
4795 return values;
4796 }
4797
4800 Container &operator *() noexcept
4801 {
4802 return values;
4803 }
4804
4807 const Container &operator *() const noexcept
4808 {
4809 return values;
4810 }
4811
4814 Container *operator ->() noexcept
4815 {
4816 return &values;
4817 }
4818
4821 const Container *operator ->() const noexcept
4822 {
4823 return &values;
4824 }
4825
4826 virtual std::string Name() const override
4827 {
4828 return name + std::string("...");
4829 }
4830
4831 virtual void Reset() noexcept override
4832 {
4833 ValueFlagBase::Reset();
4834 values = defaultValues;
4835 }
4836
4837 virtual FlagBase *Match(const EitherFlag &arg) override
4838 {
4839 const bool wasMatched = Matched();
4840 auto me = FlagBase::Match(arg);
4841 if (me && !wasMatched)
4842 {
4843 values.clear();
4844 }
4845 return me;
4846 }
4847
4848 iterator begin() noexcept
4849 {
4850 return values.begin();
4851 }
4852
4853 const_iterator begin() const noexcept
4854 {
4855 return values.begin();
4856 }
4857
4858 const_iterator cbegin() const noexcept
4859 {
4860 return values.cbegin();
4861 }
4862
4863 iterator end() noexcept
4864 {
4865 return values.end();
4866 }
4867
4868 const_iterator end() const noexcept
4869 {
4870 return values.end();
4871 }
4872
4873 const_iterator cend() const noexcept
4874 {
4875 return values.cend();
4876 }
4877 };
4878
4884 template <
4885 typename T,
4886 typename Reader = ValueReader>
4888 {
4889 private:
4890 T value;
4891 const T defaultValue;
4892 Reader reader;
4893 public:
4894 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_)
4895 {
4896 group_.Add(*this);
4897 }
4898
4899 Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
4900 {
4901 }
4902
4903 virtual ~Positional() {}
4904
4905 virtual void ParseValue(const std::string &value_) override
4906 {
4907#ifdef ARGS_NOEXCEPT
4908 if (!reader(name, value_, this->value))
4909 {
4910 error = Error::Parse;
4911 return;
4912 }
4913#else
4914 reader(name, value_, this->value);
4915#endif
4916 ready = false;
4917 matched = true;
4918 }
4919
4922 T &Get() noexcept
4923 {
4924 return value;
4925 }
4926
4929 T &operator *() noexcept
4930 {
4931 return value;
4932 }
4933
4936 const T &operator *() const noexcept
4937 {
4938 return value;
4939 }
4940
4943 T *operator ->() noexcept
4944 {
4945 return &value;
4946 }
4947
4950 const T *operator ->() const noexcept
4951 {
4952 return &value;
4953 }
4954
4955 virtual void Reset() noexcept override
4956 {
4957 PositionalBase::Reset();
4958 value = defaultValue;
4959 }
4960 };
4961
4968 template <
4969 typename T,
4970 template <typename...> class List = detail::vector,
4971 typename Reader = ValueReader>
4973 {
4974 private:
4975 using Container = List<T>;
4976 Container values;
4977 const Container defaultValues;
4978 Reader reader;
4979
4980 public:
4981 typedef T value_type;
4982 typedef typename Container::allocator_type allocator_type;
4983 typedef typename Container::pointer pointer;
4984 typedef typename Container::const_pointer const_pointer;
4985 typedef T& reference;
4986 typedef const T& const_reference;
4987 typedef typename Container::size_type size_type;
4988 typedef typename Container::difference_type difference_type;
4989 typedef typename Container::iterator iterator;
4990 typedef typename Container::const_iterator const_iterator;
4991 typedef std::reverse_iterator<iterator> reverse_iterator;
4992 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4993
4994 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_)
4995 {
4996 group_.Add(*this);
4997 }
4998
4999 PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
5000 {
5001 }
5002
5003 virtual ~PositionalList() {}
5004
5005 virtual void ParseValue(const std::string &value_) override
5006 {
5007 T v{};
5008#ifdef ARGS_NOEXCEPT
5009 if (!reader(name, value_, v))
5010 {
5011 error = Error::Parse;
5012 return;
5013 }
5014#else
5015 reader(name, value_, v);
5016#endif
5017 values.insert(std::end(values), v);
5018 matched = true;
5019 }
5020
5021 virtual std::string Name() const override
5022 {
5023 return name + std::string("...");
5024 }
5025
5028 Container &Get() noexcept
5029 {
5030 return values;
5031 }
5032
5035 Container &operator *() noexcept
5036 {
5037 return values;
5038 }
5039
5042 const Container &operator *() const noexcept
5043 {
5044 return values;
5045 }
5046
5049 Container *operator ->() noexcept
5050 {
5051 return &values;
5052 }
5053
5056 const Container *operator ->() const noexcept
5057 {
5058 return &values;
5059 }
5060
5061 virtual void Reset() noexcept override
5062 {
5063 PositionalBase::Reset();
5064 values = defaultValues;
5065 }
5066
5067 virtual PositionalBase *GetNextPositional() override
5068 {
5069 const bool wasMatched = Matched();
5070 auto me = PositionalBase::GetNextPositional();
5071 if (me && !wasMatched)
5072 {
5073 values.clear();
5074 }
5075 return me;
5076 }
5077
5078 iterator begin() noexcept
5079 {
5080 return values.begin();
5081 }
5082
5083 const_iterator begin() const noexcept
5084 {
5085 return values.begin();
5086 }
5087
5088 const_iterator cbegin() const noexcept
5089 {
5090 return values.cbegin();
5091 }
5092
5093 iterator end() noexcept
5094 {
5095 return values.end();
5096 }
5097
5098 const_iterator end() const noexcept
5099 {
5100 return values.end();
5101 }
5102
5103 const_iterator cend() const noexcept
5104 {
5105 return values.cend();
5106 }
5107 };
5108
5116 template <
5117 typename K,
5118 typename T,
5119 typename Reader = ValueReader,
5120 template <typename...> class Map = detail::unordered_map>
5122 {
5123 private:
5124 const Map<K, T> map;
5125 T value;
5126 const T defaultValue;
5127 Reader reader;
5128
5129 protected:
5130 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5131 {
5132 return detail::MapKeysToStrings(map);
5133 }
5134
5135 public:
5136
5137 MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
5138 PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
5139 {
5140 group_.Add(*this);
5141 }
5142
5143 virtual ~MapPositional() {}
5144
5145 virtual void ParseValue(const std::string &value_) override
5146 {
5147 K key{};
5148#ifdef ARGS_NOEXCEPT
5149 if (!reader(name, value_, key))
5150 {
5151 error = Error::Parse;
5152 return;
5153 }
5154#else
5155 reader(name, value_, key);
5156#endif
5157 auto it = map.find(key);
5158 if (it == std::end(map))
5159 {
5160 std::ostringstream problem;
5161 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5162#ifdef ARGS_NOEXCEPT
5163 error = Error::Map;
5164 errorMsg = problem.str();
5165#else
5166 throw MapError(problem.str());
5167#endif
5168 } else
5169 {
5170 this->value = it->second;
5171 ready = false;
5172 matched = true;
5173 }
5174 }
5175
5178 T &Get() noexcept
5179 {
5180 return value;
5181 }
5182
5185 T &operator *() noexcept
5186 {
5187 return value;
5188 }
5189
5192 const T &operator *() const noexcept
5193 {
5194 return value;
5195 }
5196
5199 T *operator ->() noexcept
5200 {
5201 return &value;
5202 }
5203
5206 const T *operator ->() const noexcept
5207 {
5208 return &value;
5209 }
5210
5211 virtual void Reset() noexcept override
5212 {
5213 PositionalBase::Reset();
5214 value = defaultValue;
5215 }
5216 };
5217
5226 template <
5227 typename K,
5228 typename T,
5229 template <typename...> class List = detail::vector,
5230 typename Reader = ValueReader,
5231 template <typename...> class Map = detail::unordered_map>
5233 {
5234 private:
5235 using Container = List<T>;
5236
5237 const Map<K, T> map;
5238 Container values;
5239 const Container defaultValues;
5240 Reader reader;
5241
5242 protected:
5243 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5244 {
5245 return detail::MapKeysToStrings(map);
5246 }
5247
5248 public:
5249 typedef T value_type;
5250 typedef typename Container::allocator_type allocator_type;
5251 typedef typename Container::pointer pointer;
5252 typedef typename Container::const_pointer const_pointer;
5253 typedef T& reference;
5254 typedef const T& const_reference;
5255 typedef typename Container::size_type size_type;
5256 typedef typename Container::difference_type difference_type;
5257 typedef typename Container::iterator iterator;
5258 typedef typename Container::const_iterator const_iterator;
5259 typedef std::reverse_iterator<iterator> reverse_iterator;
5260 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
5261
5262 MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
5263 PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
5264 {
5265 group_.Add(*this);
5266 }
5267
5268 virtual ~MapPositionalList() {}
5269
5270 virtual void ParseValue(const std::string &value_) override
5271 {
5272 K key{};
5273#ifdef ARGS_NOEXCEPT
5274 if (!reader(name, value_, key))
5275 {
5276 error = Error::Parse;
5277 return;
5278 }
5279#else
5280 reader(name, value_, key);
5281#endif
5282 auto it = map.find(key);
5283 if (it == std::end(map))
5284 {
5285 std::ostringstream problem;
5286 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5287#ifdef ARGS_NOEXCEPT
5288 error = Error::Map;
5289 errorMsg = problem.str();
5290#else
5291 throw MapError(problem.str());
5292#endif
5293 } else
5294 {
5295 detail::AppendMappedValue(values, it->second, 0);
5296 matched = true;
5297 }
5298 }
5299
5302 Container &Get() noexcept
5303 {
5304 return values;
5305 }
5306
5309 Container &operator *() noexcept
5310 {
5311 return values;
5312 }
5313
5316 const Container &operator *() const noexcept
5317 {
5318 return values;
5319 }
5320
5323 Container *operator ->() noexcept
5324 {
5325 return &values;
5326 }
5327
5330 const Container *operator ->() const noexcept
5331 {
5332 return &values;
5333 }
5334
5335 virtual std::string Name() const override
5336 {
5337 return name + std::string("...");
5338 }
5339
5340 virtual void Reset() noexcept override
5341 {
5342 PositionalBase::Reset();
5343 values = defaultValues;
5344 }
5345
5346 virtual PositionalBase *GetNextPositional() override
5347 {
5348 const bool wasMatched = Matched();
5349 auto me = PositionalBase::GetNextPositional();
5350 if (me && !wasMatched)
5351 {
5352 values.clear();
5353 }
5354 return me;
5355 }
5356
5357 iterator begin() noexcept
5358 {
5359 return values.begin();
5360 }
5361
5362 const_iterator begin() const noexcept
5363 {
5364 return values.begin();
5365 }
5366
5367 const_iterator cbegin() const noexcept
5368 {
5369 return values.cbegin();
5370 }
5371
5372 iterator end() noexcept
5373 {
5374 return values.end();
5375 }
5376
5377 const_iterator end() const noexcept
5378 {
5379 return values.end();
5380 }
5381
5382 const_iterator cend() const noexcept
5383 {
5384 return values.cend();
5385 }
5386 };
5387}
5388
5389#pragma pop_macro("min")
5390#pragma pop_macro("max")
5391#endif
A flag class that calls a function when it's matched.
Definition args.hxx:3897
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3923
virtual void ParseValue(const std::vector< std::string > &value) override
Parse values of this option.
Definition args.hxx:3926
The main user facing command line argument parser class.
Definition args.hxx:2723
const std::string & ShortPrefix() const
The prefix for short flags.
Definition args.hxx:3455
void SetArgumentSeparations(const bool allowJoinedShortValue_, const bool allowJoinedLongValue_, const bool allowSeparateShortValue_, const bool allowSeparateLongValue_)
Change allowed option separation.
Definition args.hxx:3521
void Prog(const std::string &prog_)
The program name for help generation.
Definition args.hxx:3438
It ParseArgs(It begin, It end)
Parse all arguments.
Definition args.hxx:3677
const std::string & Prog() const
The program name for help generation.
Definition args.hxx:3434
void LongPrefix(const std::string &longprefix_)
The prefix for long flags.
Definition args.hxx:3447
void LongSeparator(const std::string &longseparator_)
The separator for long flags.
Definition args.hxx:3471
void Help(std::ostream &help_) const
Pass the help menu into an ostream.
Definition args.hxx:3538
const std::string & LongPrefix() const
The prefix for long flags.
Definition args.hxx:3443
const std::string & LongSeparator() const
The separator for long flags.
Definition args.hxx:3467
bool ParseCLI(const int argc, const char *const *argv)
Convenience function to parse the CLI from argc and argv.
Definition args.hxx:3708
const std::string & Terminator() const
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3491
auto ParseArgs(const T &args) -> decltype(std::begin(args))
Parse all arguments.
Definition args.hxx:3697
void Terminator(const std::string &terminator_)
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3495
void GetArgumentSeparations(bool &allowJoinedShortValue_, bool &allowJoinedLongValue_, bool &allowSeparateShortValue_, bool &allowSeparateLongValue_) const
Get the current argument separation parameters.
Definition args.hxx:3502
std::string Help() const
Generate a help menu as a string.
Definition args.hxx:3656
void ShortPrefix(const std::string &shortprefix_)
The prefix for short flags.
Definition args.hxx:3459
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:2811
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:2183
const std::string & Name() const
The name of command.
Definition args.hxx:2318
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:2419
const std::string & ProglinePostfix() const
The description that appears on the prog line after options.
Definition args.hxx:2288
void Description(const std::string &description_)
The description that appears above options.
Definition args.hxx:2303
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:2455
const std::string & Description() const
The description that appears above options.
Definition args.hxx:2298
const std::string & Help() const
The description of command.
Definition args.hxx:2323
void Epilog(const std::string &epilog_)
The description that appears below options.
Definition args.hxx:2313
void ProglinePostfix(const std::string &proglinePostfix_)
The description that appears on the prog line after options.
Definition args.hxx:2293
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:2355
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:2460
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:2524
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:2450
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:2549
const std::string & Epilog() const
The description that appears below options.
Definition args.hxx:2308
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:2336
void RequireCommand(bool value)
If value is true, parser will fail if no command was parsed.
Definition args.hxx:2330
Definition args.hxx:1493
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1507
std::string Get() noexcept
Get the completion reply.
Definition args.hxx:1578
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:1512
An exception that contains autocompletion reply.
Definition args.hxx:559
A boolean flag containing a retrievable constant.
Definition args.hxx:4267
A flag class that simply counts the number of times it's matched.
Definition args.hxx:3841
int & Get() noexcept
Get the count.
Definition args.hxx:3874
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:1318
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:3779
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3799
virtual void ParseValue(const std::vector< std::string > &) override
Parse values of this option.
Definition args.hxx:3804
bool Get() const
Get whether this was matched.
Definition args.hxx:3794
Class for using global options in ArgumentParser.
Definition args.hxx:2102
Class for all kinds of validating groups, including ArgumentParser.
Definition args.hxx:1656
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:1836
std::vector< Base * >::size_type MatchedChildren() const
Count the number of matched children this group has.
Definition args.hxx:1843
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:1827
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:1931
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:1720
void SignalDetectDuplicates()
Sends a signal to the root of the tree to begin checking for duplicates.
Definition args.hxx:1983
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:1818
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:1715
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:1887
void DetectDuplicateFlags(std::unordered_set< char > &usedShortFlags, std::unordered_set< std::string > &usedLongFlags)
Used by parameterless DetectDuplicateFlags.
Definition args.hxx:2002
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:1767
bool Get() const
Get validation.
Definition args.hxx:1894
const std::vector< Base * > & Children() const
Get all this group's children.
Definition args.hxx:1757
std::vector< Base * > GetMatchedChildren() const
Get the list of children which were matched.
Definition args.hxx:1852
void DetectDuplicateFlags()
Detect duplicate flags.
Definition args.hxx:1993
void Add(Base &child)
Append a child to this Group.
Definition args.hxx:1729
std::vector< ChildType * > GetFilteredChildren(bool matching=false) const
Gets the children which are a certain type.
Definition args.hxx:1868
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:1901
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:1802
Help flag class.
Definition args.hxx:3814
bool Get() const noexcept
Get whether this was matched.
Definition args.hxx:3832
virtual void ParseValue(const std::vector< std::string > &)
Parse values of this option.
Definition args.hxx:3820
An exception that indicates that the user has requested help.
Definition args.hxx:541
An optional argument-accepting flag class.
Definition args.hxx:4221
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:4249
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4244
Errors in map lookups.
Definition args.hxx:523
A mapping value flag list class.
Definition args.hxx:4724
Container * operator->() noexcept
Get the values.
Definition args.hxx:4814
Container & Get() noexcept
Get the value.
Definition args.hxx:4793
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4760
Container & operator*() noexcept
Get the value.
Definition args.hxx:4800
A mapping value flag class.
Definition args.hxx:4606
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4636
T & Get() noexcept
Get the value.
Definition args.hxx:4669
T * operator->() noexcept
Get the value.
Definition args.hxx:4690
T & operator*() noexcept
Get the value.
Definition args.hxx:4676
A positional argument mapping list class.
Definition args.hxx:5233
Container & operator*() noexcept
Get the value.
Definition args.hxx:5309
Container * operator->() noexcept
Get the values.
Definition args.hxx:5323
Container & Get() noexcept
Get the value.
Definition args.hxx:5302
A positional argument mapping class.
Definition args.hxx:5122
T * operator->() noexcept
Get the value.
Definition args.hxx:5199
T & Get() noexcept
Get the value.
Definition args.hxx:5178
T & operator*() noexcept
Get the value.
Definition args.hxx:5185
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:4309
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4341
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4346
List< T > * operator->() noexcept
Get the values.
Definition args.hxx:4387
List< T > & operator*() noexcept
Get the value.
Definition args.hxx:4373
Errors that occur during regular parsing.
Definition args.hxx:496
Base class for positional options.
Definition args.hxx:1596
A positional argument class that pushes the found values into a list.
Definition args.hxx:4973
Container & operator*() noexcept
Get the value.
Definition args.hxx:5035
Container & Get() noexcept
Get the values.
Definition args.hxx:5028
Container * operator->() noexcept
Get the values.
Definition args.hxx:5049
A positional argument class.
Definition args.hxx:4888
T & operator*() noexcept
Get the value.
Definition args.hxx:4929
T & Get() noexcept
Get the value.
Definition args.hxx:4922
T * operator->() noexcept
Get the value.
Definition args.hxx:4943
Errors that when a required flag is omitted.
Definition args.hxx:514
Utility class for building subparsers with coroutines/callbacks.
Definition args.hxx:2128
void Parse()
Continue parsing arguments for new command.
Definition args.hxx:3742
const std::vector< std::string > & KickedOut() const noexcept
Returns a vector of kicked out arguments.
Definition args.hxx:2172
bool IsParsed() const
(INTERNAL) Determines whether Parse was called or not.
Definition args.hxx:2159
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:1480
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1486
An argument-accepting flag class that pushes the found values into a list.
Definition args.hxx:4458
Container * operator->() noexcept
Get the values.
Definition args.hxx:4528
Container & Get() noexcept
Get the values.
Definition args.hxx:4507
Container & operator*() noexcept
Get the value.
Definition args.hxx:4514
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4488
An argument-accepting flag class.
Definition args.hxx:4119
T & operator*() noexcept
Get the value.
Definition args.hxx:4178
T * operator->() noexcept
Get the value.
Definition args.hxx:4192
T & Get() noexcept
Get the value.
Definition args.hxx:4171
const T & GetDefault() noexcept
Get the default value.
Definition args.hxx:4206
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4149
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:1666
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:3937