args 6.4.16
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.4.16"
41#define ARGS_VERSION_MAJOR 6
42#define ARGS_VERSION_MINOR 4
43#define ARGS_VERSION_PATCH 16
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 try
402 {
403 res.reserve(total);
404 }
405 catch (...) {
406 // Fall back to default allocation
407 }
408 }
409
410 bool first = true;
411 for (const auto &element : array)
412 {
413 if (!first)
414 {
415 res += delimiter;
416 }
417 res += element;
418 first = false;
419 }
420
421 return res;
422 }
423 }
424
434 inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
435 {
436 // Preserve existing line breaks
437 const auto newlineloc = in.find('\n');
438 if (newlineloc != in.npos)
439 {
440 auto first = Wrap(std::string(in, 0, newlineloc), width);
441 auto second = Wrap(std::string(in, newlineloc + 1), width);
442 first.insert(
443 std::end(first),
444 std::make_move_iterator(std::begin(second)),
445 std::make_move_iterator(std::end(second)));
446 return first;
447 }
448
449 std::istringstream stream(in);
450 std::string::size_type indent = 0;
451
452 for (auto c : in)
453 {
454 if (!std::isspace(static_cast<unsigned char>(c)))
455 {
456 break;
457 }
458 ++indent;
459 }
460
461 return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(),
462 width, firstlinewidth, indent);
463 }
464
465#ifdef ARGS_NOEXCEPT
467 enum class Error
468 {
469 None,
470 Usage,
471 Parse,
472 Validation,
473 Required,
474 Map,
475 Extra,
476 Help,
477 Subparser,
478 Completion,
479 };
480#else
483 class Error : public std::runtime_error
484 {
485 public:
486 Error(const std::string &problem) : std::runtime_error(problem) {}
487 virtual ~Error() {}
488 };
489
492 class UsageError : public Error
493 {
494 public:
495 UsageError(const std::string &problem) : Error(problem) {}
496 virtual ~UsageError() {}
497 };
498
501 class ParseError : public Error
502 {
503 public:
504 ParseError(const std::string &problem) : Error(problem) {}
505 virtual ~ParseError() {}
506 };
507
510 class ValidationError : public Error
511 {
512 public:
513 ValidationError(const std::string &problem) : Error(problem) {}
514 virtual ~ValidationError() {}
515 };
516
520 {
521 public:
522 RequiredError(const std::string &problem) : ValidationError(problem) {}
523 virtual ~RequiredError() {}
524 };
525
528 class MapError : public ParseError
529 {
530 public:
531 MapError(const std::string &problem) : ParseError(problem) {}
532 virtual ~MapError() {}
533 };
534
537 class ExtraError : public ParseError
538 {
539 public:
540 ExtraError(const std::string &problem) : ParseError(problem) {}
541 virtual ~ExtraError() {}
542 };
543
546 class Help : public Error
547 {
548 public:
549 Help(const std::string &flag) : Error(flag) {}
550 virtual ~Help() {}
551 };
552
555 class SubparserError : public Error
556 {
557 public:
558 SubparserError() : Error("") {}
559 virtual ~SubparserError() {}
560 };
561
564 class Completion : public Error
565 {
566 public:
567 Completion(const std::string &flag) : Error(flag) {}
568 virtual ~Completion() {}
569 };
570#endif
571
575 {
576 const bool isShort;
577 const char shortFlag;
578 const std::string longFlag;
579 EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {}
580 EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {}
581 EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {}
582
585 static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags)
586 {
587 std::unordered_set<std::string> longFlags;
588 for (const EitherFlag &flag: flags)
589 {
590 if (!flag.isShort)
591 {
592 longFlags.insert(flag.longFlag);
593 }
594 }
595 return longFlags;
596 }
597
600 static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags)
601 {
602 std::unordered_set<char> shortFlags;
603 for (const EitherFlag &flag: flags)
604 {
605 if (flag.isShort)
606 {
607 shortFlags.insert(flag.shortFlag);
608 }
609 }
610 return shortFlags;
611 }
612
613 std::string str() const
614 {
615 return isShort ? std::string(1, shortFlag) : longFlag;
616 }
617
618 std::string str(const std::string &shortPrefix, const std::string &longPrefix) const
619 {
620 return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag;
621 }
622 };
623
624
625
633 {
634 private:
635 const std::unordered_set<char> shortFlags;
636 const std::unordered_set<std::string> longFlags;
637
638 public:
643 template <typename ShortIt, typename LongIt>
644 Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) :
645 shortFlags(shortFlagsStart, shortFlagsEnd),
646 longFlags(longFlagsStart, longFlagsEnd)
647 {
648 if (shortFlags.empty() && longFlags.empty())
649 {
650#ifndef ARGS_NOEXCEPT
651 throw UsageError("empty Matcher");
652#endif
653 }
654 }
655
656#ifdef ARGS_NOEXCEPT
658 Error GetError() const noexcept
659 {
660 return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None;
661 }
662#endif
663
668 template <typename Short, typename Long>
669 Matcher(Short &&shortIn, Long &&longIn) :
670 Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn))
671 {}
672
685 Matcher(std::initializer_list<EitherFlag> in) :
686 Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {}
687
688 Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags))
689 {}
690
691 ~Matcher() {}
692
695 bool Match(const char flag) const
696 {
697 return shortFlags.find(flag) != shortFlags.end();
698 }
699
702 bool Match(const std::string &flag) const
703 {
704 return longFlags.find(flag) != longFlags.end();
705 }
706
709 bool Match(const EitherFlag &flag) const
710 {
711 return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag);
712 }
713
716 std::vector<EitherFlag> GetFlagStrings() const
717 {
718 std::vector<EitherFlag> flagStrings;
719 flagStrings.reserve(shortFlags.size() + longFlags.size());
720 for (const char flag: shortFlags)
721 {
722 flagStrings.emplace_back(flag);
723 }
724 for (const std::string &flag: longFlags)
725 {
726 flagStrings.emplace_back(flag);
727 }
728 return flagStrings;
729 }
730
734 {
735 if (!longFlags.empty())
736 {
737 return *longFlags.begin();
738 }
739
740 if (!shortFlags.empty())
741 {
742 return *shortFlags.begin();
743 }
744
745 // should be unreachable
746 return ' ';
747 }
748
752 {
753 if (!shortFlags.empty())
754 {
755 return *shortFlags.begin();
756 }
757
758 if (!longFlags.empty())
759 {
760 return *longFlags.begin();
761 }
762
763 // should be unreachable
764 return ' ';
765 }
766 };
767
770 enum class Options
771 {
774 None = 0x0,
775
778 Single = 0x01,
779
782 Required = 0x02,
783
786 HiddenFromUsage = 0x04,
787
791
794 Global = 0x10,
795
798 KickOut = 0x20,
799
803
807 };
808
809 inline Options operator | (Options lhs, Options rhs)
810 {
811 return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs));
812 }
813
814 inline Options operator & (Options lhs, Options rhs)
815 {
816 return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs));
817 }
818
819 class FlagBase;
820 class PositionalBase;
821 class Command;
822 class ArgumentParser;
823
827 {
830 unsigned int width = 80;
833 unsigned int progindent = 2;
836 unsigned int progtailindent = 4;
839 unsigned int descriptionindent = 4;
842 unsigned int flagindent = 6;
845 unsigned int helpindent = 40;
848 unsigned int eachgroupindent = 2;
849
852 unsigned int gutter = 1;
853
856 bool showTerminator = true;
857
861
865
868 std::string shortPrefix;
869
872 std::string longPrefix;
873
876 std::string shortSeparator;
877
880 std::string longSeparator;
881
884 std::string programName;
885
889
893
896 std::string proglineOptions = "{OPTIONS}";
897
900 std::string proglineCommand = "COMMAND";
901
904 std::string proglineValueOpen = " <";
905
908 std::string proglineValueClose = ">";
909
912 std::string proglineRequiredOpen = "";
913
916 std::string proglineRequiredClose = "";
917
920 std::string proglineNonrequiredOpen = "[";
921
924 std::string proglineNonrequiredClose = "]";
925
928 bool proglineShowFlags = false;
929
933
936 std::string usageString;
937
940 std::string optionsString = "OPTIONS:";
941
944 bool useValueNameOnce = false;
945
948 bool showValueName = true;
949
953
956 std::string valueOpen = "[";
957
960 std::string valueClose = "]";
961
964 bool addChoices = false;
965
968 std::string choiceString = "\nOne of: ";
969
972 bool addDefault = false;
973
976 std::string defaultString = "\nDefault: ";
977 };
978
983 struct Nargs
984 {
985 const size_t min;
986 const size_t max;
987
988 Nargs(size_t min_, size_t max_) : min{min_}, max{max_}
989 {
990#ifndef ARGS_NOEXCEPT
991 if (max < min)
992 {
993 throw UsageError("Nargs: max < min");
994 }
995#endif
996 }
997
998 Nargs(size_t num_) : min{num_}, max{num_}
999 {
1000 }
1001
1002 friend bool operator == (const Nargs &lhs, const Nargs &rhs)
1003 {
1004 return lhs.min == rhs.min && lhs.max == rhs.max;
1005 }
1006
1007 friend bool operator != (const Nargs &lhs, const Nargs &rhs)
1008 {
1009 return !(lhs == rhs);
1010 }
1011 };
1012
1015 class Base
1016 {
1017 private:
1018 Options options = {};
1019
1020 protected:
1021 bool matched = false;
1022 const std::string help;
1023#ifdef ARGS_NOEXCEPT
1025 mutable Error error = Error::None;
1026 mutable std::string errorMsg;
1027#endif
1028
1029 public:
1030 Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {}
1031 virtual ~Base() {}
1032
1033 Options GetOptions() const noexcept
1034 {
1035 return options;
1036 }
1037
1038 bool IsRequired() const noexcept
1039 {
1040 return (GetOptions() & Options::Required) != Options::None;
1041 }
1042
1043 virtual bool Matched() const noexcept
1044 {
1045 return matched;
1046 }
1047
1048 virtual void Validate(const std::string &, const std::string &) const
1049 {
1050 }
1051
1052 operator bool() const noexcept
1053 {
1054 return Matched();
1055 }
1056
1057 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const
1058 {
1059 std::tuple<std::string, std::string, unsigned> description;
1060 std::get<1>(description) = help;
1061 std::get<2>(description) = indentLevel;
1062 return { std::move(description) };
1063 }
1064
1065 virtual std::vector<Command*> GetCommands()
1066 {
1067 return {};
1068 }
1069
1070 virtual bool IsGroup() const
1071 {
1072 return false;
1073 }
1074
1075 virtual FlagBase *Match(const EitherFlag &)
1076 {
1077 return nullptr;
1078 }
1079
1080 virtual PositionalBase *GetNextPositional()
1081 {
1082 return nullptr;
1083 }
1084
1085 virtual std::vector<FlagBase*> GetAllFlags()
1086 {
1087 return {};
1088 }
1089
1090 virtual bool HasFlag() const
1091 {
1092 return false;
1093 }
1094
1095 virtual bool HasPositional() const
1096 {
1097 return false;
1098 }
1099
1100 virtual bool HasCommand() const
1101 {
1102 return false;
1103 }
1104
1105 virtual std::vector<std::string> GetProgramLine(const HelpParams &) const
1106 {
1107 return {};
1108 }
1109
1111 void KickOut(bool kickout_) noexcept
1112 {
1113 if (kickout_)
1114 {
1115 options = options | Options::KickOut;
1116 }
1117 else
1118 {
1119 options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut));
1120 }
1121 }
1122
1124 bool KickOut() const noexcept
1125 {
1126 return (options & Options::KickOut) != Options::None;
1127 }
1128
1129 virtual void Reset() noexcept
1130 {
1131 matched = false;
1132#ifdef ARGS_NOEXCEPT
1133 error = Error::None;
1134 errorMsg.clear();
1135#endif
1136 }
1137
1138#ifdef ARGS_NOEXCEPT
1140 virtual Error GetError() const
1141 {
1142 return error;
1143 }
1144
1146 virtual std::string GetErrorMsg() const
1147 {
1148 return errorMsg;
1149 }
1150#endif
1151 };
1152
1155 class NamedBase : public Base
1156 {
1157 protected:
1158 const std::string name;
1159 bool kickout = false;
1160 std::string defaultString;
1161 bool defaultStringManual = false;
1162 std::vector<std::string> choicesStrings;
1163 bool choicesStringManual = false;
1164
1165 virtual std::string GetDefaultString(const HelpParams&) const { return {}; }
1166
1167 virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; }
1168
1169 virtual std::string GetNameString(const HelpParams&) const { return Name(); }
1170
1171 void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const
1172 {
1173 if (isManual && !manual.empty())
1174 {
1175 dest += str;
1176 dest += manual;
1177 }
1178 else if (!isManual && isGenerated && !generated.empty())
1179 {
1180 dest += str;
1181 dest += generated;
1182 }
1183 }
1184
1185 public:
1186 NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {}
1187 virtual ~NamedBase() {}
1188
1192 void HelpDefault(const std::string &str)
1193 {
1194 defaultStringManual = true;
1195 defaultString = str;
1196 }
1197
1200 std::string HelpDefault(const HelpParams &params) const
1201 {
1202 return defaultStringManual ? defaultString : GetDefaultString(params);
1203 }
1204
1208 void HelpChoices(const std::vector<std::string> &array)
1209 {
1210 choicesStringManual = true;
1211 choicesStrings = array;
1212 }
1213
1216 std::vector<std::string> HelpChoices(const HelpParams &params) const
1217 {
1218 return choicesStringManual ? choicesStrings : GetChoicesStrings(params);
1219 }
1220
1221 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
1222 {
1223 std::tuple<std::string, std::string, unsigned> description;
1224 std::get<0>(description) = GetNameString(params);
1225 std::get<1>(description) = help;
1226 std::get<2>(description) = indentLevel;
1227
1228 AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString);
1229 AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString);
1230
1231 return { std::move(description) };
1232 }
1233
1234 virtual std::string Name() const
1235 {
1236 return name;
1237 }
1238 };
1239
1240 namespace detail
1241 {
1242 template<typename T>
1243 using vector = std::vector<T, std::allocator<T>>;
1244
1245 template<typename K, typename T>
1246 using unordered_map = std::unordered_map<K, T, std::hash<K>,
1247 std::equal_to<K>, std::allocator<std::pair<const K, T> > >;
1248
1249 template<typename S, typename T>
1251 {
1252 template<typename SS, typename TT>
1253 static auto test(int)
1254 -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
1255
1256 template<typename, typename>
1257 static auto test(...) -> std::false_type;
1258
1259 public:
1260 using type = decltype(test<S,T>(0));
1261 };
1262
1263 template <typename T>
1264 using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type;
1265
1266 template <typename T>
1267 typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
1268 ToString(const T &value)
1269 {
1270 std::ostringstream s;
1271 s << value;
1272 return s.str();
1273 }
1274
1275 template <typename T>
1276 typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
1277 ToString(const T &)
1278 {
1279 return {};
1280 }
1281
1282 template <typename T>
1283 std::vector<std::string> MapKeysToStrings(const T &map)
1284 {
1285 std::vector<std::string> res;
1286 using K = typename std::decay<decltype(std::begin(map)->first)>::type;
1287 if (IsConvertableToString<K>::value)
1288 {
1289 for (const auto &p : map)
1290 {
1291 res.push_back(detail::ToString(p.first));
1292 }
1293
1294 std::sort(res.begin(), res.end());
1295 }
1296 return res;
1297 }
1298 }
1299
1302 class FlagBase : public NamedBase
1303 {
1304 protected:
1305 const Matcher matcher;
1306
1307 virtual std::string GetNameString(const HelpParams &params) const override
1308 {
1309 const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
1310 std::string flags;
1311 const auto flagStrings = matcher.GetFlagStrings();
1312 const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
1313 for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
1314 {
1315 auto &flag = *it;
1316 if (it != flagStrings.begin())
1317 {
1318 flags += ", ";
1319 }
1320
1321 flags += flag.isShort ? params.shortPrefix : params.longPrefix;
1322 flags += flag.str();
1323
1324 if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
1325 {
1326 flags += flag.isShort ? params.shortSeparator : params.longSeparator;
1327 flags += params.valueOpen + postfix + params.valueClose;
1328 }
1329 }
1330
1331 return flags;
1332 }
1333
1334 public:
1335 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_)) {}
1336
1337 FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {}
1338
1339 virtual ~FlagBase() {}
1340
1341 virtual FlagBase *Match(const EitherFlag &flag) override
1342 {
1343 if (matcher.Match(flag))
1344 {
1345 if ((GetOptions() & Options::Single) != Options::None && matched)
1346 {
1347 std::ostringstream problem;
1348 problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once";
1349#ifdef ARGS_NOEXCEPT
1350 error = Error::Extra;
1351 errorMsg = problem.str();
1352#else
1353 throw ExtraError(problem.str());
1354#endif
1355 }
1356 matched = true;
1357 return this;
1358 }
1359 return nullptr;
1360 }
1361
1362 virtual std::vector<FlagBase*> GetAllFlags() override
1363 {
1364 return { this };
1365 }
1366
1367 const Matcher &GetMatcher() const
1368 {
1369 return matcher;
1370 }
1371
1372 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1373 {
1374 if (!Matched() && IsRequired())
1375 {
1376 std::ostringstream problem;
1377 problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required";
1378#ifdef ARGS_NOEXCEPT
1379 error = Error::Required;
1380 errorMsg = problem.str();
1381#else
1382 throw RequiredError(problem.str());
1383#endif
1384 }
1385 }
1386
1387 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1388 {
1389 if (!params.proglineShowFlags)
1390 {
1391 return {};
1392 }
1393
1394 const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name();
1395 const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny();
1396 std::string res = flag.str(params.shortPrefix, params.longPrefix);
1397 if (!postfix.empty())
1398 {
1399 res += params.proglineValueOpen + postfix + params.proglineValueClose;
1400 }
1401
1402 return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
1403 : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
1404 }
1405
1406 virtual bool HasFlag() const override
1407 {
1408 return true;
1409 }
1410
1411#ifdef ARGS_NOEXCEPT
1413 virtual Error GetError() const override
1414 {
1415 const auto nargs = NumberOfArguments();
1416 if (nargs.min > nargs.max)
1417 {
1418 return Error::Usage;
1419 }
1420
1421 const auto matcherError = matcher.GetError();
1422 if (matcherError != Error::None)
1423 {
1424 return matcherError;
1425 }
1426
1427 return error;
1428 }
1429#endif
1430
1435 virtual Nargs NumberOfArguments() const noexcept = 0;
1436
1441 virtual void ParseValue(const std::vector<std::string> &value) = 0;
1442 };
1443
1447 {
1448 public:
1449 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {}
1450 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {}
1451 virtual ~ValueFlagBase() {}
1452
1453 virtual Nargs NumberOfArguments() const noexcept override
1454 {
1455 return 1;
1456 }
1457 };
1458
1460 {
1461 public:
1462 std::vector<std::string> reply;
1463 size_t cword = 0;
1464 std::string syntax;
1465
1466 template <typename GroupClass>
1467 CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden)
1468 {
1469 group_.AddCompletion(*this);
1470 }
1471
1472 virtual ~CompletionFlag() {}
1473
1474 virtual Nargs NumberOfArguments() const noexcept override
1475 {
1476 return 2;
1477 }
1478
1479 virtual void ParseValue(const std::vector<std::string> &value_) override
1480 {
1481 syntax = value_.at(0);
1482 const std::string &raw = value_.at(1);
1483 bool failed = false;
1484
1485 const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c)
1486 {
1487 return std::isspace(static_cast<unsigned char>(c)) != 0;
1488 });
1489
1490 // Reject explicit signs: cword must be a plain non-negative
1491 // decimal index. istringstream would otherwise silently
1492 // accept "+1".
1493 if (firstNonSpace != raw.end() && (*firstNonSpace == '-' || *firstNonSpace == '+'))
1494 {
1495 failed = true;
1496 }
1497
1498 size_t parsed = 0;
1499 if (!failed)
1500 {
1501 std::istringstream ss(raw);
1502 // Use the C locale so that the cword index parses
1503 // consistently regardless of any std::locale::global call
1504 // elsewhere in the process. A locale with a non-empty
1505 // grouping facet would otherwise reject digit-only inputs
1506 // like "12" when grouping rules expect separators.
1507 ss.imbue(std::locale::classic());
1508 ss >> parsed;
1509 if (ss.fail())
1510 {
1511 failed = true;
1512 }
1513 else
1514 {
1515 char extra;
1516 if (ss >> extra)
1517 {
1518 failed = true;
1519 }
1520 else if (!ss.eof())
1521 {
1522 failed = true;
1523 }
1524 }
1525 }
1526
1527 if (failed)
1528 {
1529#ifdef ARGS_NOEXCEPT
1530 error = Error::Parse;
1531 errorMsg = "Argument 'completion' received invalid value type '" + raw + "'";
1532#else
1533 std::ostringstream problem;
1534 problem << "Argument 'completion' received invalid value type '" << raw << "'";
1535 throw ParseError(problem.str());
1536#endif
1537 return;
1538 }
1539
1540 cword = parsed;
1541 }
1542
1545 std::string Get() noexcept
1546 {
1547 return detail::Join(reply, "\n");
1548 }
1549
1550 virtual void Reset() noexcept override
1551 {
1552 ValueFlagBase::Reset();
1553 cword = 0;
1554 syntax.clear();
1555 reply.clear();
1556 }
1557 };
1558
1559
1563 {
1564 protected:
1565 bool ready;
1566
1567 public:
1568 PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {}
1569 virtual ~PositionalBase() {}
1570
1571 bool Ready()
1572 {
1573 return ready;
1574 }
1575
1576 virtual void ParseValue(const std::string &value_) = 0;
1577
1578 virtual void Reset() noexcept override
1579 {
1580 matched = false;
1581 ready = true;
1582#ifdef ARGS_NOEXCEPT
1583 error = Error::None;
1584 errorMsg.clear();
1585#endif
1586 }
1587
1588 virtual PositionalBase *GetNextPositional() override
1589 {
1590 return Ready() ? this : nullptr;
1591 }
1592
1593 virtual bool HasPositional() const override
1594 {
1595 return true;
1596 }
1597
1598 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1599 {
1600 return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose
1601 : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose };
1602 }
1603
1604 virtual void Validate(const std::string &, const std::string &) const override
1605 {
1606 if (IsRequired() && !Matched())
1607 {
1608 std::ostringstream problem;
1609 problem << "Option '" << Name() << "' is required";
1610#ifdef ARGS_NOEXCEPT
1611 error = Error::Required;
1612 errorMsg = problem.str();
1613#else
1614 throw RequiredError(problem.str());
1615#endif
1616 }
1617 }
1618 };
1619
1622 class Group : public Base
1623 {
1624 private:
1625 std::vector<Base*> children;
1626 std::function<bool(const Group &)> validator;
1627
1628 public:
1632 {
1633 static bool Xor(const Group &group)
1634 {
1635 return group.MatchedChildren() == 1;
1636 }
1637
1638 static bool AtLeastOne(const Group &group)
1639 {
1640 return group.MatchedChildren() >= 1;
1641 }
1642
1643 static bool AtMostOne(const Group &group)
1644 {
1645 return group.MatchedChildren() <= 1;
1646 }
1647
1648 static bool All(const Group &group)
1649 {
1650 return group.Children().size() == group.MatchedChildren();
1651 }
1652
1653 static bool AllOrNone(const Group &group)
1654 {
1655 return (All(group) || None(group));
1656 }
1657
1658 static bool AllChildGroups(const Group &group)
1659 {
1660 return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool {
1661 return child->IsGroup() && !child->Matched();
1662 });
1663 }
1664
1665 static bool DontCare(const Group &)
1666 {
1667 return true;
1668 }
1669
1670 static bool CareTooMuch(const Group &)
1671 {
1672 return false;
1673 }
1674
1675 static bool None(const Group &group)
1676 {
1677 return group.MatchedChildren() == 0;
1678 }
1679 };
1681 Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) {}
1683 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_)
1684 {
1685 group_.Add(*this);
1686 }
1687 virtual ~Group() {}
1688
1691 void Add(Base &child)
1692 {
1693 children.emplace_back(&child);
1694 }
1695
1698 const std::vector<Base *> &Children() const
1699 {
1700 return children;
1701 }
1702
1708 virtual FlagBase *Match(const EitherFlag &flag) override
1709 {
1710 for (Base *child: Children())
1711 {
1712 if (FlagBase *match = child->Match(flag))
1713 {
1714 return match;
1715 }
1716 }
1717 return nullptr;
1718 }
1719
1720 virtual std::vector<FlagBase*> GetAllFlags() override
1721 {
1722 std::vector<FlagBase*> res;
1723 for (Base *child: Children())
1724 {
1725 auto childRes = child->GetAllFlags();
1726 res.insert(res.end(), childRes.begin(), childRes.end());
1727 }
1728 return res;
1729 }
1730
1731 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1732 {
1733 for (Base *child: Children())
1734 {
1735 child->Validate(shortPrefix, longPrefix);
1736 }
1737 }
1738
1744 {
1745 for (Base *child: Children())
1746 {
1747 if (auto next = child->GetNextPositional())
1748 {
1749 return next;
1750 }
1751 }
1752 return nullptr;
1753 }
1754
1759 virtual bool HasFlag() const override
1760 {
1761 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
1762 }
1763
1768 virtual bool HasPositional() const override
1769 {
1770 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
1771 }
1772
1777 virtual bool HasCommand() const override
1778 {
1779 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
1780 }
1781
1784 std::vector<Base *>::size_type MatchedChildren() const
1785 {
1786 // Cast to avoid warnings from -Wsign-conversion
1787 return static_cast<std::vector<Base *>::size_type>(
1788 std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
1789 }
1790
1793 std::vector<Base *> GetMatchedChildren() const
1794 {
1795 // Could be replaced by C++ 20 filter, or a custom iterator.
1796 std::vector<Base*> matched_children;
1797 std::copy_if(children.begin(), children.end(), std::back_inserter(matched_children), [](Base* b){
1798 return b->Matched();
1799 });
1800 return matched_children;
1801 }
1802
1805 virtual bool Matched() const noexcept override
1806 {
1807 return validator(*this);
1808 }
1809
1812 bool Get() const
1813 {
1814 return Matched();
1815 }
1816
1819 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
1820 {
1821 std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
1822
1823 // Push that group description on the back if not empty
1824 unsigned addindent = 0;
1825 if (!help.empty())
1826 {
1827 descriptions.emplace_back(help, "", indent);
1828 addindent = 1;
1829 }
1830
1831 for (Base *child: Children())
1832 {
1833 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
1834 {
1835 continue;
1836 }
1837
1838 auto groupDescriptions = child->GetDescription(params, indent + addindent);
1839 descriptions.insert(
1840 std::end(descriptions),
1841 std::make_move_iterator(std::begin(groupDescriptions)),
1842 std::make_move_iterator(std::end(groupDescriptions)));
1843 }
1844 return descriptions;
1845 }
1846
1849 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1850 {
1851 std::vector <std::string> names;
1852 for (Base *child: Children())
1853 {
1854 if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
1855 {
1856 continue;
1857 }
1858
1859 auto groupNames = child->GetProgramLine(params);
1860 names.insert(
1861 std::end(names),
1862 std::make_move_iterator(std::begin(groupNames)),
1863 std::make_move_iterator(std::end(groupNames)));
1864 }
1865 return names;
1866 }
1867
1868 virtual std::vector<Command*> GetCommands() override
1869 {
1870 std::vector<Command*> res;
1871 for (const auto &child : Children())
1872 {
1873 auto subparsers = child->GetCommands();
1874 res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
1875 }
1876 return res;
1877 }
1878
1879 virtual bool IsGroup() const override
1880 {
1881 return true;
1882 }
1883
1884 virtual void Reset() noexcept override
1885 {
1886 Base::Reset();
1887
1888 for (auto &child: Children())
1889 {
1890 child->Reset();
1891 }
1892#ifdef ARGS_NOEXCEPT
1893 error = Error::None;
1894 errorMsg.clear();
1895#endif
1896 }
1897
1898#ifdef ARGS_NOEXCEPT
1900 virtual Error GetError() const override
1901 {
1902 if (error != Error::None)
1903 {
1904 return error;
1905 }
1906
1907 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
1908 if (it == Children().end())
1909 {
1910 return Error::None;
1911 } else
1912 {
1913 return (*it)->GetError();
1914 }
1915 }
1916
1918 virtual std::string GetErrorMsg() const override
1919 {
1920 if (error != Error::None)
1921 {
1922 return errorMsg;
1923 }
1924
1925 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
1926 if (it == Children().end())
1927 {
1928 return "";
1929 } else
1930 {
1931 return (*it)->GetErrorMsg();
1932 }
1933 }
1934#endif
1935
1936 };
1937
1940 class GlobalOptions : public Group
1941 {
1942 public:
1943 GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
1944 {
1945 Add(options_);
1946 }
1947 };
1948
1966 class Subparser : public Group
1967 {
1968 private:
1969 std::vector<std::string> args;
1970 std::vector<std::string> kicked;
1971 ArgumentParser *parser = nullptr;
1972 const HelpParams &helpParams;
1973 const Command &command;
1974 bool isParsed = false;
1975
1976 public:
1977 Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
1978 : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
1979 {
1980 }
1981
1982 Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
1983 {
1984 }
1985
1986 Subparser(const Subparser&) = delete;
1987 Subparser(Subparser&&) = delete;
1988 Subparser &operator = (const Subparser&) = delete;
1989 Subparser &operator = (Subparser&&) = delete;
1990
1991 const Command &GetCommand()
1992 {
1993 return command;
1994 }
1995
1998 bool IsParsed() const
1999 {
2000 return isParsed;
2001 }
2002
2005 void Parse();
2006
2011 const std::vector<std::string> &KickedOut() const noexcept
2012 {
2013 return kicked;
2014 }
2015 };
2016
2021 class Command : public Group
2022 {
2023 private:
2024 friend class Subparser;
2025
2026 std::string name;
2027 std::string help;
2028 std::string description;
2029 std::string epilog;
2030 std::string proglinePostfix;
2031
2032 std::function<void(Subparser&)> parserCoroutine;
2033 bool commandIsRequired = true;
2034 Command *selectedCommand = nullptr;
2035
2036 mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
2037 mutable std::vector<std::string> subparserProgramLine;
2038 mutable bool subparserHasFlag = false;
2039 mutable bool subparserHasPositional = false;
2040 mutable bool subparserHasCommand = false;
2041#ifdef ARGS_NOEXCEPT
2042 mutable Error subparserError = Error::None;
2043#endif
2044 mutable Subparser *subparser = nullptr;
2045
2046 protected:
2047
2048 class RaiiSubparser
2049 {
2050 public:
2051 RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
2052 RaiiSubparser(const Command &command_, const HelpParams &params_);
2053
2054 ~RaiiSubparser()
2055 {
2056 command.subparser = oldSubparser;
2057 }
2058
2059 Subparser &Parser()
2060 {
2061 return parser;
2062 }
2063
2064 private:
2065 const Command &command;
2066 Subparser parser;
2067 Subparser *oldSubparser;
2068 };
2069
2070 Command() = default;
2071
2072 std::function<void(Subparser&)> &GetCoroutine()
2073 {
2074 return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
2075 }
2076
2077 Command &SelectedCommand()
2078 {
2079 Command *res = this;
2080 while (res->selectedCommand != nullptr)
2081 {
2082 res = res->selectedCommand;
2083 }
2084
2085 return *res;
2086 }
2087
2088 const Command &SelectedCommand() const
2089 {
2090 const Command *res = this;
2091 while (res->selectedCommand != nullptr)
2092 {
2093 res = res->selectedCommand;
2094 }
2095
2096 return *res;
2097 }
2098
2099 void UpdateSubparserHelp(const HelpParams &params) const
2100 {
2101 if (parserCoroutine)
2102 {
2103 RaiiSubparser coro(*this, params);
2104#ifndef ARGS_NOEXCEPT
2105 try
2106 {
2107 parserCoroutine(coro.Parser());
2108 }
2109 catch (args::SubparserError&)
2110 {
2111 }
2112#else
2113 parserCoroutine(coro.Parser());
2114#endif
2115 }
2116 }
2117
2118 public:
2119 Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
2120 : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
2121 {
2122 base_.Add(*this);
2123 }
2124
2127 const std::string &ProglinePostfix() const
2128 { return proglinePostfix; }
2129
2132 void ProglinePostfix(const std::string &proglinePostfix_)
2133 { this->proglinePostfix = proglinePostfix_; }
2134
2137 const std::string &Description() const
2138 { return description; }
2142 void Description(const std::string &description_)
2143 { this->description = description_; }
2144
2147 const std::string &Epilog() const
2148 { return epilog; }
2149
2152 void Epilog(const std::string &epilog_)
2153 { this->epilog = epilog_; }
2154
2157 const std::string &Name() const
2158 { return name; }
2159
2162 const std::string &Help() const
2163 { return help; }
2164
2169 void RequireCommand(bool value)
2170 { commandIsRequired = value; }
2171
2172 virtual bool IsGroup() const override
2173 { return false; }
2174
2175 virtual bool Matched() const noexcept override
2176 { return Base::Matched(); }
2177
2178 operator bool() const noexcept
2179 { return Matched(); }
2180
2181 void Match() noexcept
2182 { matched = true; }
2183
2184 void SelectCommand(Command *c) noexcept
2185 {
2186 selectedCommand = c;
2187
2188 if (c != nullptr)
2189 {
2190 c->Match();
2191 }
2192 }
2193
2194 virtual FlagBase *Match(const EitherFlag &flag) override
2195 {
2196 if (selectedCommand != nullptr)
2197 {
2198 if (auto *res = selectedCommand->Match(flag))
2199 {
2200 return res;
2201 }
2202
2203 for (auto *child: Children())
2204 {
2205 if ((child->GetOptions() & Options::Global) != Options::None)
2206 {
2207 if (auto *res = child->Match(flag))
2208 {
2209 return res;
2210 }
2211 }
2212 }
2213
2214 return nullptr;
2215 }
2216
2217 if (subparser != nullptr)
2218 {
2219 return subparser->Match(flag);
2220 }
2221
2222 return Matched() ? Group::Match(flag) : nullptr;
2223 }
2224
2225 virtual std::vector<FlagBase*> GetAllFlags() override
2226 {
2227 std::vector<FlagBase*> res;
2228
2229 if (!Matched())
2230 {
2231 return res;
2232 }
2233
2234 for (auto *child: Children())
2235 {
2236 if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
2237 {
2238 auto childFlags = child->GetAllFlags();
2239 res.insert(res.end(), childFlags.begin(), childFlags.end());
2240 }
2241 }
2242
2243 if (selectedCommand != nullptr)
2244 {
2245 auto childFlags = selectedCommand->GetAllFlags();
2246 res.insert(res.end(), childFlags.begin(), childFlags.end());
2247 }
2248
2249 if (subparser != nullptr)
2250 {
2251 auto childFlags = subparser->GetAllFlags();
2252 res.insert(res.end(), childFlags.begin(), childFlags.end());
2253 }
2254
2255 return res;
2256 }
2257
2259 {
2260 if (selectedCommand != nullptr)
2261 {
2262 if (auto *res = selectedCommand->GetNextPositional())
2263 {
2264 return res;
2265 }
2266
2267 for (auto *child: Children())
2268 {
2269 if ((child->GetOptions() & Options::Global) != Options::None)
2270 {
2271 if (auto *res = child->GetNextPositional())
2272 {
2273 return res;
2274 }
2275 }
2276 }
2277
2278 return nullptr;
2279 }
2280
2281 if (subparser != nullptr)
2282 {
2283 return subparser->GetNextPositional();
2284 }
2285
2286 return Matched() ? Group::GetNextPositional() : nullptr;
2287 }
2288
2289 virtual bool HasFlag() const override
2290 {
2291 return subparserHasFlag || Group::HasFlag();
2292 }
2293
2294 virtual bool HasPositional() const override
2295 {
2296 return subparserHasPositional || Group::HasPositional();
2297 }
2298
2299 virtual bool HasCommand() const override
2300 {
2301 return true;
2302 }
2303
2304 std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
2305 {
2306 UpdateSubparserHelp(params);
2307
2308 std::vector<std::string> res;
2309
2310 if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
2311 {
2312 res.push_back(params.proglineOptions);
2313 }
2314
2315 auto group_res = Group::GetProgramLine(params);
2316 std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res));
2317
2318 res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
2319
2320 if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
2321 {
2322 res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
2323 }
2324
2325 if (!Name().empty())
2326 {
2327 res.insert(res.begin(), Name());
2328 }
2329
2330 if (!ProglinePostfix().empty())
2331 {
2332 std::string line;
2333 for (auto c : ProglinePostfix())
2334 {
2335 if (std::isspace(static_cast<unsigned char>(c)))
2336 {
2337 if (!line.empty())
2338 {
2339 res.push_back(line);
2340 line.clear();
2341 }
2342
2343 if (c == '\n')
2344 {
2345 res.push_back("\n");
2346 }
2347 }
2348 else
2349 {
2350 line += c;
2351 }
2352 }
2353
2354 if (!line.empty())
2355 {
2356 res.push_back(line);
2357 }
2358 }
2359
2360 return res;
2361 }
2362
2363 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
2364 {
2365 if (!Matched())
2366 {
2367 return {};
2368 }
2369
2370 return GetCommandProgramLine(params);
2371 }
2372
2373 virtual std::vector<Command*> GetCommands() override
2374 {
2375 if (selectedCommand != nullptr)
2376 {
2377 return selectedCommand->GetCommands();
2378 }
2379
2380 if (Matched())
2381 {
2382 return Group::GetCommands();
2383 }
2384
2385 return { this };
2386 }
2387
2388 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
2389 {
2390 std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
2391 unsigned addindent = 0;
2392
2393 UpdateSubparserHelp(params);
2394
2395 if (!Matched())
2396 {
2397 if (params.showCommandFullHelp)
2398 {
2399 std::ostringstream s;
2400 bool empty = true;
2401 for (const auto &progline: GetCommandProgramLine(params))
2402 {
2403 if (!empty)
2404 {
2405 s << ' ';
2406 }
2407 else
2408 {
2409 empty = false;
2410 }
2411
2412 s << progline;
2413 }
2414
2415 descriptions.emplace_back(s.str(), "", indent);
2416 }
2417 else
2418 {
2419 descriptions.emplace_back(Name(), help, indent);
2420 }
2421
2422 if (!params.showCommandChildren && !params.showCommandFullHelp)
2423 {
2424 return descriptions;
2425 }
2426
2427 addindent = 1;
2428 }
2429
2430 if (params.showCommandFullHelp && !Matched())
2431 {
2432 descriptions.emplace_back("", "", indent + addindent);
2433 descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
2434 descriptions.emplace_back("", "", indent + addindent);
2435 }
2436
2437 for (Base *child: Children())
2438 {
2439 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
2440 {
2441 continue;
2442 }
2443
2444 auto groupDescriptions = child->GetDescription(params, indent + addindent);
2445 descriptions.insert(
2446 std::end(descriptions),
2447 std::make_move_iterator(std::begin(groupDescriptions)),
2448 std::make_move_iterator(std::end(groupDescriptions)));
2449 }
2450
2451 for (auto childDescription: subparserDescription)
2452 {
2453 std::get<2>(childDescription) += indent + addindent;
2454 descriptions.push_back(std::move(childDescription));
2455 }
2456
2457 if (params.showCommandFullHelp && !Matched())
2458 {
2459 descriptions.emplace_back("", "", indent + addindent);
2460 if (!Epilog().empty())
2461 {
2462 descriptions.emplace_back(Epilog(), "", indent + addindent);
2463 descriptions.emplace_back("", "", indent + addindent);
2464 }
2465 }
2466
2467 return descriptions;
2468 }
2469
2470 virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
2471 {
2472 if (!Matched())
2473 {
2474 return;
2475 }
2476
2477 auto onValidationError = [&]
2478 {
2479 std::ostringstream problem;
2480 problem << "Group validation failed somewhere!";
2481#ifdef ARGS_NOEXCEPT
2482 error = Error::Validation;
2483 errorMsg = problem.str();
2484#else
2485 throw ValidationError(problem.str());
2486#endif
2487 };
2488
2489 for (Base *child: Children())
2490 {
2491 if (child->IsGroup() && !child->Matched())
2492 {
2493 onValidationError();
2494 }
2495
2496 child->Validate(shortprefix, longprefix);
2497 }
2498
2499 if (subparser != nullptr)
2500 {
2501 subparser->Validate(shortprefix, longprefix);
2502 if (!subparser->Matched())
2503 {
2504 onValidationError();
2505 }
2506 }
2507
2508 if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
2509 {
2510 std::ostringstream problem;
2511 problem << "Command is required";
2512#ifdef ARGS_NOEXCEPT
2513 error = Error::Validation;
2514 errorMsg = problem.str();
2515#else
2516 throw ValidationError(problem.str());
2517#endif
2518 }
2519 }
2520
2521 virtual void Reset() noexcept override
2522 {
2523 Group::Reset();
2524 selectedCommand = nullptr;
2525 subparserProgramLine.clear();
2526 subparserDescription.clear();
2527 subparserHasFlag = false;
2528 subparserHasPositional = false;
2529 subparserHasCommand = false;
2530#ifdef ARGS_NOEXCEPT
2531 subparserError = Error::None;
2532#endif
2533 }
2534
2535#ifdef ARGS_NOEXCEPT
2537 virtual Error GetError() const override
2538 {
2539 if (!Matched())
2540 {
2541 return Error::None;
2542 }
2543
2544 if (error != Error::None)
2545 {
2546 return error;
2547 }
2548
2549 if (subparserError != Error::None)
2550 {
2551 return subparserError;
2552 }
2553
2554 return Group::GetError();
2555 }
2556#endif
2557 };
2558
2562 {
2563 friend class Subparser;
2564
2565 private:
2566 std::string longprefix;
2567 std::string shortprefix;
2568
2569 std::string longseparator;
2570
2571 std::string terminator;
2572
2573 bool allowJoinedShortValue = true;
2574 bool allowJoinedLongValue = true;
2575 bool allowSeparateShortValue = true;
2576 bool allowSeparateLongValue = true;
2577
2578 bool readCompletion = false;
2579 CompletionFlag *completion = nullptr;
2580
2581 protected:
2582 enum class OptionType
2583 {
2584 LongFlag,
2585 ShortFlag,
2587 };
2588
2589 OptionType ParseOption(const std::string &s, bool allowEmpty = false)
2590 {
2591 const bool matchesLong = s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length());
2592 const bool matchesShort = s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length());
2593
2594 // A chunk can start with both prefixes when one is a prefix of
2595 // the other, or when the long prefix is empty (every string
2596 // starts with it). Resolve to the longer, more specific prefix:
2597 // this keeps the default "--"/"-" preference for long flags
2598 // while letting a short flag be recognised under an empty long
2599 // prefix instead of being swallowed as a nameless long flag.
2600 if (matchesLong && matchesShort)
2601 {
2602 return longprefix.length() >= shortprefix.length() ? OptionType::LongFlag : OptionType::ShortFlag;
2603 }
2604
2605 if (matchesLong)
2606 {
2607 return OptionType::LongFlag;
2608 }
2609
2610 if (matchesShort)
2611 {
2612 return OptionType::ShortFlag;
2613 }
2614
2615 return OptionType::Positional;
2616 }
2617
2618 template <typename It>
2619 bool Complete(FlagBase &flag, It it, It end)
2620 {
2621 auto nextIt = it;
2622 if (!readCompletion || (++nextIt != end))
2623 {
2624 return false;
2625 }
2626
2627 const auto &chunk = *it;
2628 for (auto &choice : flag.HelpChoices(helpParams))
2629 {
2630 AddCompletionReply(chunk, choice);
2631 }
2632
2633#ifndef ARGS_NOEXCEPT
2634 throw Completion(completion->Get());
2635#else
2636 return true;
2637#endif
2638 }
2639
2649 template <typename It>
2650 std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
2651 const bool allowSeparate, const bool allowJoined,
2652 const bool hasJoined, const std::string &joinedArg,
2653 const bool canDiscardJoined, std::vector<std::string> &values)
2654 {
2655 values.clear();
2656
2657 Nargs nargs = flag.NumberOfArguments();
2658
2659 if (hasJoined && !allowJoined && nargs.min != 0)
2660 {
2661 return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2662 }
2663
2664 if (hasJoined)
2665 {
2666 if (!canDiscardJoined || nargs.max != 0)
2667 {
2668 values.push_back(joinedArg);
2669 }
2670 } else if (!allowSeparate)
2671 {
2672 if (nargs.min != 0)
2673 {
2674 return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2675 }
2676 } else
2677 {
2678 auto valueIt = it;
2679 ++valueIt;
2680
2681 while (valueIt != end &&
2682 *valueIt != terminator &&
2683 values.size() < nargs.max &&
2684 (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2685 {
2686 if (Complete(flag, valueIt, end))
2687 {
2688 // Park `it` on the completion position rather than
2689 // `end`. In ARGS_NOEXCEPT mode Complete returns
2690 // true (no throw), so the caller's for-loop will
2691 // run its ++it after we return; advancing an
2692 // already-end iterator is undefined behavior and
2693 // causes a subsequent out-of-bounds read of the
2694 // arg vector. Since Complete only fires when
2695 // ++nextIt == end, valueIt is the last element,
2696 // and ++(it=valueIt) safely lands on end.
2697 it = valueIt;
2698 return "";
2699 }
2700
2701 values.push_back(*valueIt);
2702 ++it;
2703 ++valueIt;
2704 }
2705 }
2706
2707 if (values.size() > nargs.max)
2708 {
2709 return "Passed an argument into a non-argument flag: " + arg;
2710 } else if (values.size() < nargs.min)
2711 {
2712 if (nargs.min == 1 && nargs.max == 1)
2713 {
2714 return "Flag '" + arg + "' requires an argument but received none";
2715 } else if (nargs.min == 1)
2716 {
2717 return "Flag '" + arg + "' requires at least one argument but received none";
2718 } else if (nargs.min != nargs.max)
2719 {
2720 return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2721 " arguments but received " + std::to_string(values.size());
2722 } else
2723 {
2724 return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2725 " arguments but received " + std::to_string(values.size());
2726 }
2727 }
2728
2729 return {};
2730 }
2731
2732 template <typename It>
2733 bool ParseLong(It &it, It end)
2734 {
2735 const auto &chunk = *it;
2736 const auto argchunk = chunk.substr(longprefix.size());
2737 // Try to separate it, in case of a separator:
2738 const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2739 // If the separator is in the argument, separate it.
2740 const auto arg = (separator != argchunk.npos ?
2741 std::string(argchunk, 0, separator)
2742 : argchunk);
2743 const auto joined = (separator != argchunk.npos ?
2744 argchunk.substr(separator + longseparator.size())
2745 : std::string());
2746
2747 if (auto flag = Match(arg))
2748 {
2749#ifdef ARGS_NOEXCEPT
2750 // Match() may set the flag's error (e.g. Error::Extra when
2751 // Options::Single is violated). In non-noexcept mode that
2752 // path throws and parsing stops before the value is read;
2753 // in noexcept mode we must mirror that and skip the value
2754 // parsing so the previously-stored value is preserved.
2755 if (flag->GetError() != Error::None)
2756 {
2757 return false;
2758 }
2759#endif
2760 std::vector<std::string> values;
2761 const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2762 separator != argchunk.npos, joined, false, values);
2763 if (!errorMessage.empty())
2764 {
2765#ifndef ARGS_NOEXCEPT
2766 throw ParseError(errorMessage);
2767#else
2768 error = Error::Parse;
2769 errorMsg = errorMessage;
2770 return false;
2771#endif
2772 }
2773
2774 if (!readCompletion)
2775 {
2776 flag->ParseValue(values);
2777#ifdef ARGS_NOEXCEPT
2778 // Non-noexcept ParseValue paths throw on Help, reader
2779 // failure, or Map miss, which halts parsing. Mirror
2780 // that here so a later parser-level error (e.g. an
2781 // unknown flag) cannot shadow the flag's error in
2782 // ArgumentParser::GetError().
2783 if (flag->GetError() != Error::None)
2784 {
2785 return false;
2786 }
2787#endif
2788 }
2789
2790 if (flag->KickOut())
2791 {
2792 ++it;
2793 return false;
2794 }
2795 } else
2796 {
2797 const std::string errorMessage("Flag could not be matched: " + arg);
2798#ifndef ARGS_NOEXCEPT
2799 throw ParseError(errorMessage);
2800#else
2801 error = Error::Parse;
2802 errorMsg = errorMessage;
2803 return false;
2804#endif
2805 }
2806
2807 return true;
2808 }
2809
2810 template <typename It>
2811 bool ParseShort(It &it, It end)
2812 {
2813 const auto &chunk = *it;
2814 const auto argchunk = chunk.substr(shortprefix.size());
2815 for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2816 {
2817 const auto arg = *argit;
2818
2819 if (auto flag = Match(arg))
2820 {
2821#ifdef ARGS_NOEXCEPT
2822 // See ParseLong: if Match recorded an error
2823 // (e.g. Options::Single violation), bail before the
2824 // value is parsed so the prior value is preserved.
2825 if (flag->GetError() != Error::None)
2826 {
2827 return false;
2828 }
2829#endif
2830 const std::string value(argit + 1, std::end(argchunk));
2831 std::vector<std::string> values;
2832 const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2833 allowSeparateShortValue, allowJoinedShortValue,
2834 !value.empty(), value, !value.empty(), values);
2835
2836 if (!errorMessage.empty())
2837 {
2838#ifndef ARGS_NOEXCEPT
2839 throw ParseError(errorMessage);
2840#else
2841 error = Error::Parse;
2842 errorMsg = errorMessage;
2843 return false;
2844#endif
2845 }
2846
2847 if (!readCompletion)
2848 {
2849 flag->ParseValue(values);
2850#ifdef ARGS_NOEXCEPT
2851 // See ParseLong: ensure a flag-level error from
2852 // ParseValue (Help, Parse, Map) halts parsing so
2853 // it cannot be shadowed by a later parser error.
2854 if (flag->GetError() != Error::None)
2855 {
2856 return false;
2857 }
2858#endif
2859 }
2860
2861 if (flag->KickOut())
2862 {
2863 ++it;
2864 return false;
2865 }
2866
2867 if (!values.empty())
2868 {
2869 break;
2870 }
2871 } else
2872 {
2873 const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
2874#ifndef ARGS_NOEXCEPT
2875 throw ParseError(errorMessage);
2876#else
2877 error = Error::Parse;
2878 errorMsg = errorMessage;
2879 return false;
2880#endif
2881 }
2882 }
2883
2884 return true;
2885 }
2886
2887 bool AddCompletionReply(const std::string &cur, const std::string &choice)
2888 {
2889 if (cur.empty() || choice.find(cur) == 0)
2890 {
2891 if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
2892 {
2893 completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size()));
2894 } else
2895 {
2896 completion->reply.push_back(choice);
2897 }
2898 return true;
2899 }
2900
2901 return false;
2902 }
2903
2904 template <typename It>
2905 bool Complete(It it, It end, bool terminated)
2906 {
2907 auto nextIt = it;
2908 if (!readCompletion || (++nextIt != end))
2909 {
2910 return false;
2911 }
2912
2913 const auto &chunk = *it;
2914 auto pos = GetNextPositional();
2915 std::vector<Command *> commands = GetCommands();
2916 const auto optionType = ParseOption(chunk, true);
2917
2918 // Once the terminator has been seen the parser treats every
2919 // following chunk as positional, so only positional choices are
2920 // valid completions here. Suggesting flags or commands past the
2921 // terminator offers candidates the parser would then reject.
2922 if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
2923 {
2924 for (auto &cmd : commands)
2925 {
2926 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2927 {
2928 AddCompletionReply(chunk, cmd->Name());
2929 }
2930 }
2931 } else
2932 {
2933 bool hasPositionalCompletion = true;
2934
2935 if (!terminated && !commands.empty())
2936 {
2937 for (auto &cmd : commands)
2938 {
2939 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2940 {
2941 AddCompletionReply(chunk, cmd->Name());
2942 }
2943 }
2944 } else if (pos)
2945 {
2946 if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2947 {
2948 auto choices = pos->HelpChoices(helpParams);
2949 hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
2950 for (auto &choice : choices)
2951 {
2952 AddCompletionReply(chunk, choice);
2953 }
2954 }
2955 }
2956
2957 if (!terminated && hasPositionalCompletion)
2958 {
2959 auto flags = GetAllFlags();
2960 for (auto flag : flags)
2961 {
2962 if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
2963 {
2964 continue;
2965 }
2966
2967 auto &matcher = flag->GetMatcher();
2968 if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
2969 {
2970 for (auto &flagName : matcher.GetFlagStrings())
2971 {
2972 if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
2973 {
2974 break;
2975 }
2976 }
2977 }
2978 }
2979
2980 if (optionType == OptionType::LongFlag && allowJoinedLongValue)
2981 {
2982 const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
2983 // Only attempt joined-value completion when the
2984 // separator lies at or past the long prefix, so
2985 // there is a (possibly empty) flag name between
2986 // them. With a custom longseparator that overlaps
2987 // the prefix (e.g. LongSeparator("-") under the
2988 // default "--" prefix), an attacker-controlled
2989 // completion word like "--x" puts the separator
2990 // inside the prefix, making `arg` shorter than
2991 // longprefix. arg.substr(longprefix.size()) would
2992 // then throw std::out_of_range, which escapes the
2993 // parser as a non-args exception (bypassing the
2994 // documented catch(args::Error) idiom) and is
2995 // thrown even under ARGS_NOEXCEPT.
2996 if (separator != chunk.npos && separator >= longprefix.size())
2997 {
2998 std::string arg(chunk, 0, separator);
2999 if (auto flag = this->Match(arg.substr(longprefix.size())))
3000 {
3001 for (auto &choice : flag->HelpChoices(helpParams))
3002 {
3003 AddCompletionReply(chunk, arg + longseparator + choice);
3004 }
3005 }
3006 }
3007 } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
3008 {
3009 if (chunk.size() > shortprefix.size() + 1)
3010 {
3011 auto arg = chunk.at(shortprefix.size());
3012 //TODO: support -abcVALUE where a and b take no value
3013 if (auto flag = this->Match(arg))
3014 {
3015 for (auto &choice : flag->HelpChoices(helpParams))
3016 {
3017 AddCompletionReply(chunk, shortprefix + arg + choice);
3018 }
3019 }
3020 }
3021 }
3022 }
3023 }
3024
3025#ifndef ARGS_NOEXCEPT
3026 throw Completion(completion->Get());
3027#else
3028 return true;
3029#endif
3030 }
3031
3032 template <typename It>
3033 It Parse(It begin, It end)
3034 {
3035 bool terminated = false;
3036 std::vector<Command *> commands = GetCommands();
3037
3038 // Check all arg chunks
3039 for (auto it = begin; it != end; ++it)
3040 {
3041 if (Complete(it, end, terminated))
3042 {
3043 return end;
3044 }
3045
3046 const auto &chunk = *it;
3047
3048 if (!terminated && chunk == terminator)
3049 {
3050 terminated = true;
3051 } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
3052 {
3053 if (!ParseLong(it, end))
3054 {
3055 return it;
3056 }
3057 } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
3058 {
3059 if (!ParseShort(it, end))
3060 {
3061 return it;
3062 }
3063 } else if (!terminated && !commands.empty())
3064 {
3065 auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
3066 if (itCommand == commands.end())
3067 {
3068 const std::string errorMessage("Unknown command: " + chunk);
3069#ifndef ARGS_NOEXCEPT
3070 throw ParseError(errorMessage);
3071#else
3072 error = Error::Parse;
3073 errorMsg = errorMessage;
3074 return it;
3075#endif
3076 }
3077
3078 SelectCommand(*itCommand);
3079
3080 if (const auto &coroutine = GetCoroutine())
3081 {
3082 ++it;
3083 RaiiSubparser coro(*this, std::vector<std::string>(it, end));
3084 coroutine(coro.Parser());
3085#ifdef ARGS_NOEXCEPT
3086 error = GetError();
3087 if (error != Error::None)
3088 {
3089 return end;
3090 }
3091
3092 if (!coro.Parser().IsParsed())
3093 {
3094 error = Error::Usage;
3095 return end;
3096 }
3097#else
3098 if (!coro.Parser().IsParsed())
3099 {
3100 throw UsageError("Subparser::Parse was not called");
3101 }
3102#endif
3103
3104 break;
3105 }
3106
3107 commands = GetCommands();
3108 } else
3109 {
3110 auto pos = GetNextPositional();
3111 if (pos)
3112 {
3113 pos->ParseValue(chunk);
3114#ifdef ARGS_NOEXCEPT
3115 if (pos->GetError() != Error::None)
3116 {
3117 return it;
3118 }
3119#endif
3120
3121 if (pos->KickOut())
3122 {
3123 return ++it;
3124 }
3125 } else
3126 {
3127 const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
3128#ifndef ARGS_NOEXCEPT
3129 throw ParseError(errorMessage);
3130#else
3131 error = Error::Parse;
3132 errorMsg = errorMessage;
3133 return it;
3134#endif
3135 }
3136 }
3137
3138 if (!readCompletion && completion != nullptr && completion->Matched())
3139 {
3140#ifdef ARGS_NOEXCEPT
3141 if (completion->GetError() != Error::None)
3142 {
3143 error = completion->GetError();
3144 if (errorMsg.empty())
3145 {
3146 errorMsg = completion->GetErrorMsg();
3147 }
3148 return it;
3149 }
3150
3151 error = Error::Completion;
3152#endif
3153 readCompletion = true;
3154 ++it;
3155 const auto argsLeft = static_cast<size_t>(std::distance(it, end));
3156 if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
3157 {
3158#ifndef ARGS_NOEXCEPT
3159 throw Completion("");
3160#else
3161 return end;
3162#endif
3163 }
3164
3165 ++it;
3166 std::vector<std::string> curArgs;
3167 curArgs.reserve(completion->cword);
3168 auto curIt = it;
3169 for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
3170 {
3171 curArgs.push_back(*curIt);
3172 }
3173
3174 if (completion->syntax == "bash")
3175 {
3176 // bash tokenizes --flag=value as --flag=value
3177 // Security fix: Use size_t arithmetic throughout to avoid conversion issues
3178 for (size_t idx = 0; idx < curArgs.size(); )
3179 {
3180 if (idx > 0 && curArgs[idx] == "=")
3181 {
3182 size_t prev_idx = idx - 1; // Safe since we checked idx > 0
3183 curArgs[prev_idx] += "=";
3184 size_t next_idx = 0;
3185 if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
3186 {
3187 curArgs[prev_idx] += curArgs[next_idx];
3188 // Erase the '=' token and the following value token.
3189 size_t erase_end = 0;
3190 if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
3191 {
3192 typedef std::vector<std::string>::difference_type diff_t;
3193 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx),
3194 curArgs.begin() + static_cast<diff_t>(erase_end));
3195 }
3196 } else
3197 {
3198 // Safe erase of single '=' token at the end
3199 typedef std::vector<std::string>::difference_type diff_t;
3200 curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx));
3201 }
3202 // Do not increment idx - next element slides into current position
3203 } else
3204 {
3205 ++idx;
3206 }
3207 }
3208
3209 }
3210#ifndef ARGS_NOEXCEPT
3211 try
3212 {
3213 Parse(curArgs.begin(), curArgs.end());
3214 throw Completion("");
3215 }
3216 catch (Completion &)
3217 {
3218 throw;
3219 }
3220 catch (args::Error&)
3221 {
3222 throw Completion("");
3223 }
3224#else
3225 // Discard the nested Parse's return value: it points
3226 // into the local curArgs vector, which is destroyed
3227 // when this function returns, leaving the caller with
3228 // a dangling iterator that would be compared against
3229 // the outer `end` in ParseCLI. Return the outer
3230 // `end` instead so the iterator stays in the caller's
3231 // container.
3232 Parse(curArgs.begin(), curArgs.end());
3233 error = Error::Completion;
3234 errorMsg.clear();
3235 return end;
3236#endif
3237 }
3238 }
3239
3240 Validate(shortprefix, longprefix);
3241 return end;
3242 }
3243
3244 public:
3245 HelpParams helpParams;
3246
3247 ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
3248 {
3249 Description(description_);
3250 Epilog(epilog_);
3251 LongPrefix("--");
3252 ShortPrefix("-");
3253 LongSeparator("=");
3254 Terminator("--");
3255 SetArgumentSeparations(true, true, true, true);
3256 matched = true;
3257 }
3258
3259 void AddCompletion(CompletionFlag &completionFlag)
3260 {
3261 completion = &completionFlag;
3262 Add(completionFlag);
3263 }
3264
3267 const std::string &Prog() const
3268 { return helpParams.programName; }
3271 void Prog(const std::string &prog_)
3272 { this->helpParams.programName = prog_; }
3273
3276 const std::string &LongPrefix() const
3277 { return longprefix; }
3280 void LongPrefix(const std::string &longprefix_)
3281 {
3282 this->longprefix = longprefix_;
3283 this->helpParams.longPrefix = longprefix_;
3284 }
3285
3288 const std::string &ShortPrefix() const
3289 { return shortprefix; }
3292 void ShortPrefix(const std::string &shortprefix_)
3293 {
3294 this->shortprefix = shortprefix_;
3295 this->helpParams.shortPrefix = shortprefix_;
3296 }
3297
3300 const std::string &LongSeparator() const
3301 { return longseparator; }
3304 void LongSeparator(const std::string &longseparator_)
3305 {
3306 if (longseparator_.empty())
3307 {
3308 const std::string errorMessage("longseparator can not be set to empty");
3309#ifdef ARGS_NOEXCEPT
3310 error = Error::Usage;
3311 errorMsg = errorMessage;
3312#else
3313 throw UsageError(errorMessage);
3314#endif
3315 } else
3316 {
3317 this->longseparator = longseparator_;
3318 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3319 }
3320 }
3321
3324 const std::string &Terminator() const
3325 { return terminator; }
3328 void Terminator(const std::string &terminator_)
3329 { this->terminator = terminator_; }
3330
3336 bool &allowJoinedShortValue_,
3337 bool &allowJoinedLongValue_,
3338 bool &allowSeparateShortValue_,
3339 bool &allowSeparateLongValue_) const
3340 {
3341 allowJoinedShortValue_ = this->allowJoinedShortValue;
3342 allowJoinedLongValue_ = this->allowJoinedLongValue;
3343 allowSeparateShortValue_ = this->allowSeparateShortValue;
3344 allowSeparateLongValue_ = this->allowSeparateLongValue;
3345 }
3346
3355 const bool allowJoinedShortValue_,
3356 const bool allowJoinedLongValue_,
3357 const bool allowSeparateShortValue_,
3358 const bool allowSeparateLongValue_)
3359 {
3360 this->allowJoinedShortValue = allowJoinedShortValue_;
3361 this->allowJoinedLongValue = allowJoinedLongValue_;
3362 this->allowSeparateShortValue = allowSeparateShortValue_;
3363 this->allowSeparateLongValue = allowSeparateLongValue_;
3364
3365 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3366 this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
3367 }
3368
3371 void Help(std::ostream &help_) const
3372 {
3373 auto &command = SelectedCommand();
3374 const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
3375 const auto desc_indent = helpParams.descriptionindent;
3376 const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0;
3377 const auto description_text = Wrap(commandDescription, effective_desc_width);
3378 const auto epilog_text = Wrap(command.Epilog(), effective_desc_width);
3379
3380 const bool hasoptions = command.HasFlag();
3381 const bool hasarguments = command.HasPositional();
3382
3383 std::vector<std::string> prognameline;
3384 prognameline.push_back(helpParams.usageString);
3385 prognameline.push_back(Prog());
3386 auto commandProgLine = command.GetProgramLine(helpParams);
3387 prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
3388
3389 const auto prog_sum = helpParams.progindent + helpParams.progtailindent;
3390 const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0;
3391 const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0;
3392 const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
3393 effective_prog_width,
3394 effective_prog_first);
3395 auto progit = std::begin(proglines);
3396 if (progit != std::end(proglines))
3397 {
3398 help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
3399 ++progit;
3400 }
3401 for (; progit != std::end(proglines); ++progit)
3402 {
3403 help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
3404 }
3405
3406 help_ << '\n';
3407
3408 if (!description_text.empty())
3409 {
3410 for (const auto &line: description_text)
3411 {
3412 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3413 }
3414 help_ << "\n";
3415 }
3416
3417 bool lastDescriptionIsNewline = false;
3418
3419 if (!helpParams.optionsString.empty())
3420 {
3421 help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
3422 }
3423
3424 for (const auto &desc: command.GetDescription(helpParams, 0))
3425 {
3426 lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
3427 const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
3428 const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter;
3429 const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0;
3430 const auto flags = Wrap(std::get<0>(desc), effective_flag_width);
3431 const auto info_sum = helpParams.helpindent + groupindent;
3432 const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0;
3433 const auto info = Wrap(std::get<1>(desc), effective_info_width);
3434
3435 std::string::size_type flagssize = 0;
3436 for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
3437 {
3438 if (flagsit != std::begin(flags))
3439 {
3440 help_ << '\n';
3441 }
3442 help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
3443 flagssize = Glyphs(*flagsit);
3444 }
3445
3446 auto infoit = std::begin(info);
3447 // groupindent is on both sides of this inequality, and therefore can be removed
3448 if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
3449 {
3450 help_ << '\n';
3451 } else
3452 {
3453 // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
3454 const auto indent_sum = helpParams.flagindent + flagssize;
3455 const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0;
3456 help_ << std::string(effective_space, ' ') << *infoit << '\n';
3457 ++infoit;
3458 }
3459 for (; infoit != std::end(info); ++infoit)
3460 {
3461 help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
3462 }
3463 }
3464 if (hasoptions && hasarguments && helpParams.showTerminator)
3465 {
3466 lastDescriptionIsNewline = false;
3467 const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0;
3468 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))
3469 {
3470 help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
3471 }
3472 }
3473
3474 if (!lastDescriptionIsNewline)
3475 {
3476 help_ << "\n";
3477 }
3478
3479 for (const auto &line: epilog_text)
3480 {
3481 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3482 }
3483 }
3484
3489 std::string Help() const
3490 {
3491 std::ostringstream help_;
3492 Help(help_);
3493 return help_.str();
3494 }
3495
3496 virtual void Reset() noexcept override
3497 {
3498 Command::Reset();
3499 matched = true;
3500 readCompletion = false;
3501 }
3502
3509 template <typename It>
3510 It ParseArgs(It begin, It end)
3511 {
3512 // Reset all Matched statuses and errors
3513 Reset();
3514#ifdef ARGS_NOEXCEPT
3515 error = GetError();
3516 if (error != Error::None)
3517 {
3518 return end;
3519 }
3520#endif
3521 return Parse(begin, end);
3522 }
3523
3529 template <typename T>
3530 auto ParseArgs(const T &args) -> decltype(std::begin(args))
3531 {
3532 return ParseArgs(std::begin(args), std::end(args));
3533 }
3534
3541 bool ParseCLI(const int argc, const char * const * argv)
3542 {
3543 if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty())
3544 {
3545 Prog(argv[0]);
3546 }
3547
3548 std::vector<std::string> args;
3549 if (argc > 1 && argv != nullptr)
3550 {
3551 args.assign(argv + 1, argv + argc);
3552 }
3553
3554 return ParseArgs(args) == std::end(args);
3555 }
3556
3557 template <typename T>
3558 bool ParseCLI(const T &args)
3559 {
3560 return ParseArgs(args) == std::end(args);
3561 }
3562 };
3563
3564 inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
3565 : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
3566 {
3567 command.subparser = &parser;
3568 }
3569
3570 inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
3571 {
3572 command.subparser = &parser;
3573 }
3574
3575 inline void Subparser::Parse()
3576 {
3577 isParsed = true;
3578 Reset();
3579 command.subparserDescription = GetDescription(helpParams, 0);
3580 command.subparserHasFlag = HasFlag();
3581 command.subparserHasPositional = HasPositional();
3582 command.subparserHasCommand = HasCommand();
3583 command.subparserProgramLine = GetProgramLine(helpParams);
3584 if (parser == nullptr)
3585 {
3586#ifndef ARGS_NOEXCEPT
3587 throw args::SubparserError();
3588#else
3589 error = Error::Subparser;
3590 return;
3591#endif
3592 }
3593
3594 auto it = parser->Parse(args.begin(), args.end());
3595 command.Validate(parser->ShortPrefix(), parser->LongPrefix());
3596 kicked.assign(it, args.end());
3597
3598#ifdef ARGS_NOEXCEPT
3599 command.subparserError = GetError();
3600#endif
3601 }
3602
3603 inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
3604 {
3605 parser.Help(os);
3606 return os;
3607 }
3608
3611 class Flag : public FlagBase
3612 {
3613 public:
3614 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
3615 {
3616 group_.Add(*this);
3617 }
3618
3619 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)
3620 {
3621 }
3622
3623 virtual ~Flag() {}
3624
3627 bool Get() const
3628 {
3629 return Matched();
3630 }
3631
3632 virtual Nargs NumberOfArguments() const noexcept override
3633 {
3634 return 0;
3635 }
3636
3637 virtual void ParseValue(const std::vector<std::string>&) override
3638 {
3639 }
3640 };
3641
3646 class HelpFlag : public Flag
3647 {
3648 public:
3649 HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
3650
3651 virtual ~HelpFlag() {}
3652
3653 virtual void ParseValue(const std::vector<std::string> &)
3654 {
3655#ifdef ARGS_NOEXCEPT
3656 error = Error::Help;
3657 errorMsg = Name();
3658#else
3659 throw Help(Name());
3660#endif
3661 }
3662
3665 bool Get() const noexcept
3666 {
3667 return Matched();
3668 }
3669 };
3670
3673 class CounterFlag : public Flag
3674 {
3675 private:
3676 const int startcount;
3677 int count;
3678
3679 public:
3680 CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
3681 Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
3682
3683 virtual ~CounterFlag() {}
3684
3685 virtual FlagBase *Match(const EitherFlag &arg) override
3686 {
3687 auto me = FlagBase::Match(arg);
3688 if (me)
3689 {
3690#ifdef ARGS_NOEXCEPT
3691 // Suppress increment when FlagBase::Match recorded an
3692 // error on this same call (e.g. Options::Single violated).
3693 // In non-noexcept mode that path would have thrown before
3694 // reaching here and the count would not have advanced.
3695 if (GetError() != Error::None)
3696 {
3697 return me;
3698 }
3699#endif
3700 ++count;
3701 }
3702 return me;
3703 }
3704
3707 int &Get() noexcept
3708 {
3709 return count;
3710 }
3711
3712 int &operator *() noexcept {
3713 return count;
3714 }
3715
3716 const int &operator *() const noexcept {
3717 return count;
3718 }
3719
3720 virtual void Reset() noexcept override
3721 {
3722 FlagBase::Reset();
3723 count = startcount;
3724 }
3725 };
3726
3729 class ActionFlag : public FlagBase
3730 {
3731 private:
3732 std::function<void(const std::vector<std::string> &)> action;
3733 Nargs nargs;
3734
3735 public:
3736 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_ = {}):
3737 FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
3738 {
3739 group_.Add(*this);
3740 }
3741
3742 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
3743 FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
3744 {
3745 group_.Add(*this);
3746 action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
3747 }
3748
3749 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
3750 FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
3751 {
3752 group_.Add(*this);
3753 action = [action_](const std::vector<std::string> &) { return action_(); };
3754 }
3755
3756 virtual Nargs NumberOfArguments() const noexcept override
3757 { return nargs; }
3758
3759 virtual void ParseValue(const std::vector<std::string> &value) override
3760 { action(value); }
3761 };
3762
3770 {
3771 private:
3772 template <typename T>
3773 static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type
3774 HasUnsignedNegativeSign(const std::string &value)
3775 {
3776 const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c)
3777 {
3778 return std::isspace(static_cast<unsigned char>(c)) != 0;
3779 });
3780
3781 return firstNonSpace != value.end() && *firstNonSpace == '-';
3782 }
3783
3784 template <typename T>
3785 static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type
3786 HasUnsignedNegativeSign(const std::string &)
3787 {
3788 return false;
3789 }
3790
3791 public:
3792 template <typename T>
3793 typename std::enable_if<
3794 std::is_integral<T>::value &&
3795 !std::is_same<T, bool>::value &&
3796 !std::is_same<T, char>::value &&
3797 !std::is_same<T, signed char>::value &&
3798 !std::is_same<T, unsigned char>::value,
3799 bool>::type
3800 ParseNumericValue(const std::string &value, T &destination)
3801 {
3802 if (HasUnsignedNegativeSign<T>(value))
3803 {
3804 return false;
3805 }
3806
3807 const char *begin = value.c_str();
3808 // The true end of the value, derived from its length rather than
3809 // from the first NUL. strtoull/strtoll treat the buffer as a C
3810 // string and stop at an embedded '\0', so checking `*end == '\0'`
3811 // for "no trailing data" is defeated by a value like "12\0junk":
3812 // end lands on the embedded NUL and the junk after it is silently
3813 // accepted. Comparing against `stop` validates the whole string
3814 // and matches the istringstream-based reader used for other types.
3815 const char *const stop = begin + value.size();
3816
3817 // C++11-compatible: use strtoull/strtoll. Hardening retained from
3818 // the original from_chars draft (errno save/restore, ERANGE check,
3819 // narrowing range check, trailing-whitespace tolerance). No
3820 // unconditional dependency on <charconv> / C++17.
3821 const int saved_errno = errno;
3822 errno = 0;
3823
3824 char *end = nullptr;
3825
3826 if (std::is_unsigned<T>::value)
3827 {
3828 const unsigned long long parsed = std::strtoull(begin, &end, 0);
3829 if (end == begin)
3830 {
3831 errno = saved_errno;
3832 return false;
3833 }
3834 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3835 {
3836 ++end;
3837 }
3838 if (end != stop || errno == ERANGE ||
3839 parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
3840 {
3841 errno = saved_errno;
3842 return false;
3843 }
3844
3845 destination = static_cast<T>(parsed);
3846 }
3847 else
3848 {
3849 const long long parsed = std::strtoll(begin, &end, 0);
3850 if (end == begin)
3851 {
3852 errno = saved_errno;
3853 return false;
3854 }
3855 while (end != stop && std::isspace(static_cast<unsigned char>(*end)))
3856 {
3857 ++end;
3858 }
3859 if (end != stop || errno == ERANGE ||
3860 parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
3861 parsed > static_cast<long long>(std::numeric_limits<T>::max()))
3862 {
3863 errno = saved_errno;
3864 return false;
3865 }
3866
3867 destination = static_cast<T>(parsed);
3868 }
3869
3870 errno = saved_errno;
3871 return true;
3872 }
3873
3874 template <typename T>
3875 typename std::enable_if<
3876 !std::is_integral<T>::value ||
3877 std::is_same<T, bool>::value ||
3878 std::is_same<T, char>::value ||
3879 std::is_same<T, signed char>::value ||
3880 std::is_same<T, unsigned char>::value,
3881 bool>::type
3882 ParseNumericValue(const std::string &value, T &destination)
3883 {
3884 std::istringstream ss(value);
3885 // Pin parsing to the C locale so that the decimal separator and
3886 // thousands grouping behavior do not silently depend on whatever
3887 // std::locale::global was last set to elsewhere in the process.
3888 // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in
3889 // any locale whose numpunct facet treats ',' as the decimal point.
3890 ss.imbue(std::locale::classic());
3891 ss >> destination;
3892 if (ss.fail())
3893 {
3894 return false;
3895 }
3896
3897 // Check for trailing garbage by attempting to extract any remaining characters.
3898 // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit
3899 // on EOF, causing false rejection of valid input.
3900 char extra = '\0';
3901 ss >> std::ws >> extra;
3902 // If extraction succeeded, there's trailing garbage (return false).
3903 // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true).
3904 // If extraction failed for other reasons, it's invalid (return false).
3905 if (ss.fail())
3906 {
3907 // Clear the failbit to check if EOF is the only issue
3908 ss.clear(ss.rdstate() & ~std::ios::failbit);
3909 return ss.eof();
3910 }
3911 // Extraction succeeded, meaning there's trailing garbage
3912 return false;
3913 }
3914
3915 template <typename T>
3916 typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
3917 operator ()(const std::string &name, const std::string &value, T &destination)
3918 {
3919 const bool success = ParseNumericValue(value, destination);
3920 if (!success)
3921 {
3922#ifdef ARGS_NOEXCEPT
3923 (void)name;
3924 return false;
3925#else
3926 std::ostringstream problem;
3927 problem << "Argument '" << name << "' received invalid value type '" << value << "'";
3928 throw ParseError(problem.str());
3929#endif
3930 }
3931 return true;
3932 }
3933
3934 template <typename T>
3935 typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
3936 operator()(const std::string &, const std::string &value, T &destination)
3937 {
3938 destination = value;
3939 return true;
3940 }
3941 };
3942
3948 template <
3949 typename T,
3950 typename Reader = ValueReader>
3952 {
3953 protected:
3954 T value;
3955 T defaultValue;
3956
3957 virtual std::string GetDefaultString(const HelpParams&) const override
3958 {
3959 return detail::ToString(defaultValue);
3960 }
3961
3962 private:
3963 Reader reader;
3964
3965 public:
3966
3967 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_)
3968 {
3969 group_.Add(*this);
3970 }
3971
3972 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)
3973 {
3974 }
3975
3976 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
3977 {
3978 }
3979
3980 virtual ~ValueFlag() {}
3981
3982 virtual void ParseValue(const std::vector<std::string> &values_) override
3983 {
3984 const std::string &value_ = values_.at(0);
3985
3986#ifdef ARGS_NOEXCEPT
3987 if (!reader(name, value_, this->value))
3988 {
3989 error = Error::Parse;
3990 }
3991#else
3992 reader(name, value_, this->value);
3993#endif
3994 }
3995
3996 virtual void Reset() noexcept override
3997 {
3998 ValueFlagBase::Reset();
3999 value = defaultValue;
4000 }
4001
4004 T &Get() noexcept
4005 {
4006 return value;
4007 }
4008
4011 T &operator *() noexcept
4012 {
4013 return value;
4014 }
4015
4018 const T &operator *() const noexcept
4019 {
4020 return value;
4021 }
4022
4025 T *operator ->() noexcept
4026 {
4027 return &value;
4028 }
4029
4032 const T *operator ->() const noexcept
4033 {
4034 return &value;
4035 }
4036
4039 const T &GetDefault() noexcept
4040 {
4041 return defaultValue;
4042 }
4043 };
4044
4050 template <
4051 typename T,
4052 typename Reader = ValueReader>
4053 class ImplicitValueFlag : public ValueFlag<T, Reader>
4054 {
4055 protected:
4056 T implicitValue;
4057
4058 public:
4059
4060 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
4061 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
4062 {
4063 }
4064
4065 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
4066 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
4067 {
4068 }
4069
4070 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
4071 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
4072 {
4073 }
4074
4075 virtual ~ImplicitValueFlag() {}
4076
4077 virtual Nargs NumberOfArguments() const noexcept override
4078 {
4079 return {0, 1};
4080 }
4081
4082 virtual void ParseValue(const std::vector<std::string> &value_) override
4083 {
4084 if (value_.empty())
4085 {
4086 this->value = implicitValue;
4087 } else
4088 {
4090 }
4091 }
4092 };
4093
4100 template <
4101 typename T,
4102 template <typename...> class List = detail::vector,
4103 typename Reader = ValueReader>
4105 {
4106 protected:
4107
4108 List<T> values;
4109 const List<T> defaultValues;
4110 Nargs nargs;
4111 Reader reader;
4112
4113 public:
4114
4115 typedef List<T> Container;
4116 typedef T value_type;
4117 typedef typename Container::allocator_type allocator_type;
4118 typedef typename Container::pointer pointer;
4119 typedef typename Container::const_pointer const_pointer;
4120 typedef T& reference;
4121 typedef const T& const_reference;
4122 typedef typename Container::size_type size_type;
4123 typedef typename Container::difference_type difference_type;
4124 typedef typename Container::iterator iterator;
4125 typedef typename Container::const_iterator const_iterator;
4126 typedef std::reverse_iterator<iterator> reverse_iterator;
4127 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4128
4129 NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
4130 : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
4131 {
4132 group_.Add(*this);
4133 }
4134
4135 virtual ~NargsValueFlag() {}
4136
4137 virtual Nargs NumberOfArguments() const noexcept override
4138 {
4139 return nargs;
4140 }
4141
4142 virtual void ParseValue(const std::vector<std::string> &values_) override
4143 {
4144 values.clear();
4145
4146 for (const std::string &value : values_)
4147 {
4148 T v {};
4149#ifdef ARGS_NOEXCEPT
4150 if (!reader(name, value, v))
4151 {
4152 error = Error::Parse;
4153 return;
4154 }
4155#else
4156 reader(name, value, v);
4157#endif
4158 values.insert(std::end(values), v);
4159 }
4160 }
4161
4162 List<T> &Get() noexcept
4163 {
4164 return values;
4165 }
4166
4169 List<T> &operator *() noexcept
4170 {
4171 return values;
4172 }
4173
4176 const List<T> &operator *() const noexcept
4177 {
4178 return values;
4179 }
4180
4183 List<T> *operator ->() noexcept
4184 {
4185 return &values;
4186 }
4187
4190 const List<T> *operator ->() const noexcept
4191 {
4192 return &values;
4193 }
4194
4195 iterator begin() noexcept
4196 {
4197 return values.begin();
4198 }
4199
4200 const_iterator begin() const noexcept
4201 {
4202 return values.begin();
4203 }
4204
4205 const_iterator cbegin() const noexcept
4206 {
4207 return values.cbegin();
4208 }
4209
4210 iterator end() noexcept
4211 {
4212 return values.end();
4213 }
4214
4215 const_iterator end() const noexcept
4216 {
4217 return values.end();
4218 }
4219
4220 const_iterator cend() const noexcept
4221 {
4222 return values.cend();
4223 }
4224
4225 virtual void Reset() noexcept override
4226 {
4227 FlagBase::Reset();
4228 values = defaultValues;
4229 }
4230
4231 virtual FlagBase *Match(const EitherFlag &arg) override
4232 {
4233 const bool wasMatched = Matched();
4234 auto me = FlagBase::Match(arg);
4235 if (me && !wasMatched)
4236 {
4237 values.clear();
4238 }
4239 return me;
4240 }
4241 };
4242
4249 template <
4250 typename T,
4251 template <typename...> class List = detail::vector,
4252 typename Reader = ValueReader>
4254 {
4255 private:
4256 using Container = List<T>;
4257 Container values;
4258 const Container defaultValues;
4259 Reader reader;
4260
4261 public:
4262
4263 typedef T value_type;
4264 typedef typename Container::allocator_type allocator_type;
4265 typedef typename Container::pointer pointer;
4266 typedef typename Container::const_pointer const_pointer;
4267 typedef T& reference;
4268 typedef const T& const_reference;
4269 typedef typename Container::size_type size_type;
4270 typedef typename Container::difference_type difference_type;
4271 typedef typename Container::iterator iterator;
4272 typedef typename Container::const_iterator const_iterator;
4273 typedef std::reverse_iterator<iterator> reverse_iterator;
4274 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4275
4276 ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
4277 ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
4278 {
4279 group_.Add(*this);
4280 }
4281
4282 virtual ~ValueFlagList() {}
4283
4284 virtual void ParseValue(const std::vector<std::string> &values_) override
4285 {
4286 const std::string &value_ = values_.at(0);
4287
4288 T v{};
4289#ifdef ARGS_NOEXCEPT
4290 if (!reader(name, value_, v))
4291 {
4292 error = Error::Parse;
4293 return;
4294 }
4295#else
4296 reader(name, value_, v);
4297#endif
4298 values.insert(std::end(values), v);
4299 }
4300
4303 Container &Get() noexcept
4304 {
4305 return values;
4306 }
4307
4310 Container &operator *() noexcept
4311 {
4312 return values;
4313 }
4314
4317 const Container &operator *() const noexcept
4318 {
4319 return values;
4320 }
4321
4324 Container *operator ->() noexcept
4325 {
4326 return &values;
4327 }
4328
4331 const Container *operator ->() const noexcept
4332 {
4333 return &values;
4334 }
4335
4336 virtual std::string Name() const override
4337 {
4338 return name + std::string("...");
4339 }
4340
4341 virtual void Reset() noexcept override
4342 {
4343 ValueFlagBase::Reset();
4344 values = defaultValues;
4345 }
4346
4347 virtual FlagBase *Match(const EitherFlag &arg) override
4348 {
4349 const bool wasMatched = Matched();
4350 auto me = FlagBase::Match(arg);
4351 if (me && !wasMatched)
4352 {
4353 values.clear();
4354 }
4355 return me;
4356 }
4357
4358 iterator begin() noexcept
4359 {
4360 return values.begin();
4361 }
4362
4363 const_iterator begin() const noexcept
4364 {
4365 return values.begin();
4366 }
4367
4368 const_iterator cbegin() const noexcept
4369 {
4370 return values.cbegin();
4371 }
4372
4373 iterator end() noexcept
4374 {
4375 return values.end();
4376 }
4377
4378 const_iterator end() const noexcept
4379 {
4380 return values.end();
4381 }
4382
4383 const_iterator cend() const noexcept
4384 {
4385 return values.cend();
4386 }
4387 };
4388
4396 template <
4397 typename K,
4398 typename T,
4399 typename Reader = ValueReader,
4400 template <typename...> class Map = detail::unordered_map>
4401 class MapFlag : public ValueFlagBase
4402 {
4403 private:
4404 const Map<K, T> map;
4405 T value;
4406 const T defaultValue;
4407 Reader reader;
4408
4409 protected:
4410 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4411 {
4412 return detail::MapKeysToStrings(map);
4413 }
4414
4415 public:
4416
4417 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_)
4418 {
4419 group_.Add(*this);
4420 }
4421
4422 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)
4423 {
4424 }
4425
4426 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_)
4427 {
4428 }
4429
4430 virtual ~MapFlag() {}
4431
4432 virtual void ParseValue(const std::vector<std::string> &values_) override
4433 {
4434 const std::string &value_ = values_.at(0);
4435
4436 K key{};
4437#ifdef ARGS_NOEXCEPT
4438 if (!reader(name, value_, key))
4439 {
4440 error = Error::Parse;
4441 return;
4442 }
4443#else
4444 reader(name, value_, key);
4445#endif
4446 auto it = map.find(key);
4447 if (it == std::end(map))
4448 {
4449 std::ostringstream problem;
4450 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4451#ifdef ARGS_NOEXCEPT
4452 error = Error::Map;
4453 errorMsg = problem.str();
4454#else
4455 throw MapError(problem.str());
4456#endif
4457 } else
4458 {
4459 this->value = it->second;
4460 }
4461 }
4462
4465 T &Get() noexcept
4466 {
4467 return value;
4468 }
4469
4472 T &operator *() noexcept
4473 {
4474 return value;
4475 }
4476
4479 const T &operator *() const noexcept
4480 {
4481 return value;
4482 }
4483
4486 T *operator ->() noexcept
4487 {
4488 return &value;
4489 }
4490
4493 const T *operator ->() const noexcept
4494 {
4495 return &value;
4496 }
4497
4498 virtual void Reset() noexcept override
4499 {
4500 ValueFlagBase::Reset();
4501 value = defaultValue;
4502 }
4503 };
4504
4513 template <
4514 typename K,
4515 typename T,
4516 template <typename...> class List = detail::vector,
4517 typename Reader = ValueReader,
4518 template <typename...> class Map = detail::unordered_map>
4520 {
4521 private:
4522 using Container = List<T>;
4523 const Map<K, T> map;
4524 Container values;
4525 const Container defaultValues;
4526 Reader reader;
4527
4528 protected:
4529 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4530 {
4531 return detail::MapKeysToStrings(map);
4532 }
4533
4534 public:
4535 typedef T value_type;
4536 typedef typename Container::allocator_type allocator_type;
4537 typedef typename Container::pointer pointer;
4538 typedef typename Container::const_pointer const_pointer;
4539 typedef T& reference;
4540 typedef const T& const_reference;
4541 typedef typename Container::size_type size_type;
4542 typedef typename Container::difference_type difference_type;
4543 typedef typename Container::iterator iterator;
4544 typedef typename Container::const_iterator const_iterator;
4545 typedef std::reverse_iterator<iterator> reverse_iterator;
4546 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4547
4548 MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4549 ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4550 {
4551 group_.Add(*this);
4552 }
4553
4554 virtual ~MapFlagList() {}
4555
4556 virtual void ParseValue(const std::vector<std::string> &values_) override
4557 {
4558 const std::string &value_ = values_.at(0);
4559
4560 K key{};
4561#ifdef ARGS_NOEXCEPT
4562 if (!reader(name, value_, key))
4563 {
4564 error = Error::Parse;
4565 return;
4566 }
4567#else
4568 reader(name, value_, key);
4569#endif
4570 auto it = map.find(key);
4571 if (it == std::end(map))
4572 {
4573 std::ostringstream problem;
4574 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4575#ifdef ARGS_NOEXCEPT
4576 error = Error::Map;
4577 errorMsg = problem.str();
4578#else
4579 throw MapError(problem.str());
4580#endif
4581 } else
4582 {
4583 this->values.emplace_back(it->second);
4584 }
4585 }
4586
4589 Container &Get() noexcept
4590 {
4591 return values;
4592 }
4593
4596 Container &operator *() noexcept
4597 {
4598 return values;
4599 }
4600
4603 const Container &operator *() const noexcept
4604 {
4605 return values;
4606 }
4607
4610 Container *operator ->() noexcept
4611 {
4612 return &values;
4613 }
4614
4617 const Container *operator ->() const noexcept
4618 {
4619 return &values;
4620 }
4621
4622 virtual std::string Name() const override
4623 {
4624 return name + std::string("...");
4625 }
4626
4627 virtual void Reset() noexcept override
4628 {
4629 ValueFlagBase::Reset();
4630 values = defaultValues;
4631 }
4632
4633 virtual FlagBase *Match(const EitherFlag &arg) override
4634 {
4635 const bool wasMatched = Matched();
4636 auto me = FlagBase::Match(arg);
4637 if (me && !wasMatched)
4638 {
4639 values.clear();
4640 }
4641 return me;
4642 }
4643
4644 iterator begin() noexcept
4645 {
4646 return values.begin();
4647 }
4648
4649 const_iterator begin() const noexcept
4650 {
4651 return values.begin();
4652 }
4653
4654 const_iterator cbegin() const noexcept
4655 {
4656 return values.cbegin();
4657 }
4658
4659 iterator end() noexcept
4660 {
4661 return values.end();
4662 }
4663
4664 const_iterator end() const noexcept
4665 {
4666 return values.end();
4667 }
4668
4669 const_iterator cend() const noexcept
4670 {
4671 return values.cend();
4672 }
4673 };
4674
4680 template <
4681 typename T,
4682 typename Reader = ValueReader>
4684 {
4685 private:
4686 T value;
4687 const T defaultValue;
4688 Reader reader;
4689 public:
4690 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_)
4691 {
4692 group_.Add(*this);
4693 }
4694
4695 Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
4696 {
4697 }
4698
4699 virtual ~Positional() {}
4700
4701 virtual void ParseValue(const std::string &value_) override
4702 {
4703#ifdef ARGS_NOEXCEPT
4704 if (!reader(name, value_, this->value))
4705 {
4706 error = Error::Parse;
4707 return;
4708 }
4709#else
4710 reader(name, value_, this->value);
4711#endif
4712 ready = false;
4713 matched = true;
4714 }
4715
4718 T &Get() noexcept
4719 {
4720 return value;
4721 }
4722
4725 T &operator *() noexcept
4726 {
4727 return value;
4728 }
4729
4732 const T &operator *() const noexcept
4733 {
4734 return value;
4735 }
4736
4739 T *operator ->() noexcept
4740 {
4741 return &value;
4742 }
4743
4746 const T *operator ->() const noexcept
4747 {
4748 return &value;
4749 }
4750
4751 virtual void Reset() noexcept override
4752 {
4753 PositionalBase::Reset();
4754 value = defaultValue;
4755 }
4756 };
4757
4764 template <
4765 typename T,
4766 template <typename...> class List = detail::vector,
4767 typename Reader = ValueReader>
4769 {
4770 private:
4771 using Container = List<T>;
4772 Container values;
4773 const Container defaultValues;
4774 Reader reader;
4775
4776 public:
4777 typedef T value_type;
4778 typedef typename Container::allocator_type allocator_type;
4779 typedef typename Container::pointer pointer;
4780 typedef typename Container::const_pointer const_pointer;
4781 typedef T& reference;
4782 typedef const T& const_reference;
4783 typedef typename Container::size_type size_type;
4784 typedef typename Container::difference_type difference_type;
4785 typedef typename Container::iterator iterator;
4786 typedef typename Container::const_iterator const_iterator;
4787 typedef std::reverse_iterator<iterator> reverse_iterator;
4788 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4789
4790 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_)
4791 {
4792 group_.Add(*this);
4793 }
4794
4795 PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
4796 {
4797 }
4798
4799 virtual ~PositionalList() {}
4800
4801 virtual void ParseValue(const std::string &value_) override
4802 {
4803 T v{};
4804#ifdef ARGS_NOEXCEPT
4805 if (!reader(name, value_, v))
4806 {
4807 error = Error::Parse;
4808 return;
4809 }
4810#else
4811 reader(name, value_, v);
4812#endif
4813 values.insert(std::end(values), v);
4814 matched = true;
4815 }
4816
4817 virtual std::string Name() const override
4818 {
4819 return name + std::string("...");
4820 }
4821
4824 Container &Get() noexcept
4825 {
4826 return values;
4827 }
4828
4831 Container &operator *() noexcept
4832 {
4833 return values;
4834 }
4835
4838 const Container &operator *() const noexcept
4839 {
4840 return values;
4841 }
4842
4845 Container *operator ->() noexcept
4846 {
4847 return &values;
4848 }
4849
4852 const Container *operator ->() const noexcept
4853 {
4854 return &values;
4855 }
4856
4857 virtual void Reset() noexcept override
4858 {
4859 PositionalBase::Reset();
4860 values = defaultValues;
4861 }
4862
4863 virtual PositionalBase *GetNextPositional() override
4864 {
4865 const bool wasMatched = Matched();
4866 auto me = PositionalBase::GetNextPositional();
4867 if (me && !wasMatched)
4868 {
4869 values.clear();
4870 }
4871 return me;
4872 }
4873
4874 iterator begin() noexcept
4875 {
4876 return values.begin();
4877 }
4878
4879 const_iterator begin() const noexcept
4880 {
4881 return values.begin();
4882 }
4883
4884 const_iterator cbegin() const noexcept
4885 {
4886 return values.cbegin();
4887 }
4888
4889 iterator end() noexcept
4890 {
4891 return values.end();
4892 }
4893
4894 const_iterator end() const noexcept
4895 {
4896 return values.end();
4897 }
4898
4899 const_iterator cend() const noexcept
4900 {
4901 return values.cend();
4902 }
4903 };
4904
4912 template <
4913 typename K,
4914 typename T,
4915 typename Reader = ValueReader,
4916 template <typename...> class Map = detail::unordered_map>
4918 {
4919 private:
4920 const Map<K, T> map;
4921 T value;
4922 const T defaultValue;
4923 Reader reader;
4924
4925 protected:
4926 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4927 {
4928 return detail::MapKeysToStrings(map);
4929 }
4930
4931 public:
4932
4933 MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
4934 PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
4935 {
4936 group_.Add(*this);
4937 }
4938
4939 virtual ~MapPositional() {}
4940
4941 virtual void ParseValue(const std::string &value_) override
4942 {
4943 K key{};
4944#ifdef ARGS_NOEXCEPT
4945 if (!reader(name, value_, key))
4946 {
4947 error = Error::Parse;
4948 return;
4949 }
4950#else
4951 reader(name, value_, key);
4952#endif
4953 auto it = map.find(key);
4954 if (it == std::end(map))
4955 {
4956 std::ostringstream problem;
4957 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4958#ifdef ARGS_NOEXCEPT
4959 error = Error::Map;
4960 errorMsg = problem.str();
4961#else
4962 throw MapError(problem.str());
4963#endif
4964 } else
4965 {
4966 this->value = it->second;
4967 ready = false;
4968 matched = true;
4969 }
4970 }
4971
4974 T &Get() noexcept
4975 {
4976 return value;
4977 }
4978
4981 T &operator *() noexcept
4982 {
4983 return value;
4984 }
4985
4988 const T &operator *() const noexcept
4989 {
4990 return value;
4991 }
4992
4995 T *operator ->() noexcept
4996 {
4997 return &value;
4998 }
4999
5002 const T *operator ->() const noexcept
5003 {
5004 return &value;
5005 }
5006
5007 virtual void Reset() noexcept override
5008 {
5009 PositionalBase::Reset();
5010 value = defaultValue;
5011 }
5012 };
5013
5022 template <
5023 typename K,
5024 typename T,
5025 template <typename...> class List = detail::vector,
5026 typename Reader = ValueReader,
5027 template <typename...> class Map = detail::unordered_map>
5029 {
5030 private:
5031 using Container = List<T>;
5032
5033 const Map<K, T> map;
5034 Container values;
5035 const Container defaultValues;
5036 Reader reader;
5037
5038 protected:
5039 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
5040 {
5041 return detail::MapKeysToStrings(map);
5042 }
5043
5044 public:
5045 typedef T value_type;
5046 typedef typename Container::allocator_type allocator_type;
5047 typedef typename Container::pointer pointer;
5048 typedef typename Container::const_pointer const_pointer;
5049 typedef T& reference;
5050 typedef const T& const_reference;
5051 typedef typename Container::size_type size_type;
5052 typedef typename Container::difference_type difference_type;
5053 typedef typename Container::iterator iterator;
5054 typedef typename Container::const_iterator const_iterator;
5055 typedef std::reverse_iterator<iterator> reverse_iterator;
5056 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
5057
5058 MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
5059 PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
5060 {
5061 group_.Add(*this);
5062 }
5063
5064 virtual ~MapPositionalList() {}
5065
5066 virtual void ParseValue(const std::string &value_) override
5067 {
5068 K key{};
5069#ifdef ARGS_NOEXCEPT
5070 if (!reader(name, value_, key))
5071 {
5072 error = Error::Parse;
5073 return;
5074 }
5075#else
5076 reader(name, value_, key);
5077#endif
5078 auto it = map.find(key);
5079 if (it == std::end(map))
5080 {
5081 std::ostringstream problem;
5082 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
5083#ifdef ARGS_NOEXCEPT
5084 error = Error::Map;
5085 errorMsg = problem.str();
5086#else
5087 throw MapError(problem.str());
5088#endif
5089 } else
5090 {
5091 this->values.emplace_back(it->second);
5092 matched = true;
5093 }
5094 }
5095
5098 Container &Get() noexcept
5099 {
5100 return values;
5101 }
5102
5105 Container &operator *() noexcept
5106 {
5107 return values;
5108 }
5109
5112 const Container &operator *() const noexcept
5113 {
5114 return values;
5115 }
5116
5119 Container *operator ->() noexcept
5120 {
5121 return &values;
5122 }
5123
5126 const Container *operator ->() const noexcept
5127 {
5128 return &values;
5129 }
5130
5131 virtual std::string Name() const override
5132 {
5133 return name + std::string("...");
5134 }
5135
5136 virtual void Reset() noexcept override
5137 {
5138 PositionalBase::Reset();
5139 values = defaultValues;
5140 }
5141
5142 virtual PositionalBase *GetNextPositional() override
5143 {
5144 const bool wasMatched = Matched();
5145 auto me = PositionalBase::GetNextPositional();
5146 if (me && !wasMatched)
5147 {
5148 values.clear();
5149 }
5150 return me;
5151 }
5152
5153 iterator begin() noexcept
5154 {
5155 return values.begin();
5156 }
5157
5158 const_iterator begin() const noexcept
5159 {
5160 return values.begin();
5161 }
5162
5163 const_iterator cbegin() const noexcept
5164 {
5165 return values.cbegin();
5166 }
5167
5168 iterator end() noexcept
5169 {
5170 return values.end();
5171 }
5172
5173 const_iterator end() const noexcept
5174 {
5175 return values.end();
5176 }
5177
5178 const_iterator cend() const noexcept
5179 {
5180 return values.cend();
5181 }
5182 };
5183}
5184
5185#pragma pop_macro("min")
5186#pragma pop_macro("max")
5187#endif
A flag class that calls a function when it's matched.
Definition args.hxx:3730
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3756
virtual void ParseValue(const std::vector< std::string > &value) override
Parse values of this option.
Definition args.hxx:3759
The main user facing command line argument parser class.
Definition args.hxx:2562
const std::string & ShortPrefix() const
The prefix for short flags.
Definition args.hxx:3288
void SetArgumentSeparations(const bool allowJoinedShortValue_, const bool allowJoinedLongValue_, const bool allowSeparateShortValue_, const bool allowSeparateLongValue_)
Change allowed option separation.
Definition args.hxx:3354
void Prog(const std::string &prog_)
The program name for help generation.
Definition args.hxx:3271
It ParseArgs(It begin, It end)
Parse all arguments.
Definition args.hxx:3510
const std::string & Prog() const
The program name for help generation.
Definition args.hxx:3267
void LongPrefix(const std::string &longprefix_)
The prefix for long flags.
Definition args.hxx:3280
void LongSeparator(const std::string &longseparator_)
The separator for long flags.
Definition args.hxx:3304
void Help(std::ostream &help_) const
Pass the help menu into an ostream.
Definition args.hxx:3371
const std::string & LongPrefix() const
The prefix for long flags.
Definition args.hxx:3276
const std::string & LongSeparator() const
The separator for long flags.
Definition args.hxx:3300
bool ParseCLI(const int argc, const char *const *argv)
Convenience function to parse the CLI from argc and argv.
Definition args.hxx:3541
const std::string & Terminator() const
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3324
auto ParseArgs(const T &args) -> decltype(std::begin(args))
Parse all arguments.
Definition args.hxx:3530
void Terminator(const std::string &terminator_)
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3328
void GetArgumentSeparations(bool &allowJoinedShortValue_, bool &allowJoinedLongValue_, bool &allowSeparateShortValue_, bool &allowSeparateLongValue_) const
Get the current argument separation parameters.
Definition args.hxx:3335
std::string Help() const
Generate a help menu as a string.
Definition args.hxx:3489
void ShortPrefix(const std::string &shortprefix_)
The prefix for short flags.
Definition args.hxx:3292
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:2650
Base class for all match types.
Definition args.hxx:1016
void KickOut(bool kickout_) noexcept
Sets a kick-out value for building subparsers.
Definition args.hxx:1111
bool KickOut() const noexcept
Gets the kick-out value for building subparsers.
Definition args.hxx:1124
Main class for building subparsers.
Definition args.hxx:2022
const std::string & Name() const
The name of command.
Definition args.hxx:2157
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:2258
const std::string & ProglinePostfix() const
The description that appears on the prog line after options.
Definition args.hxx:2127
void Description(const std::string &description_)
The description that appears above options.
Definition args.hxx:2142
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:2294
const std::string & Description() const
The description that appears above options.
Definition args.hxx:2137
const std::string & Help() const
The description of command.
Definition args.hxx:2162
void Epilog(const std::string &epilog_)
The description that appears below options.
Definition args.hxx:2152
void ProglinePostfix(const std::string &proglinePostfix_)
The description that appears on the prog line after options.
Definition args.hxx:2132
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:2194
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:2299
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:2363
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:2289
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:2388
const std::string & Epilog() const
The description that appears below options.
Definition args.hxx:2147
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:2175
void RequireCommand(bool value)
If value is true, parser will fail if no command was parsed.
Definition args.hxx:2169
Definition args.hxx:1460
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1474
std::string Get() noexcept
Get the completion reply.
Definition args.hxx:1545
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:1479
An exception that contains autocompletion reply.
Definition args.hxx:565
A flag class that simply counts the number of times it's matched.
Definition args.hxx:3674
int & Get() noexcept
Get the count.
Definition args.hxx:3707
Base error class.
Definition args.hxx:484
Error that occurs when a singular flag is specified multiple times.
Definition args.hxx:538
Base class for all flag options.
Definition args.hxx:1303
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:3612
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3632
virtual void ParseValue(const std::vector< std::string > &) override
Parse values of this option.
Definition args.hxx:3637
bool Get() const
Get whether this was matched.
Definition args.hxx:3627
Class for using global options in ArgumentParser.
Definition args.hxx:1941
Class for all kinds of validating groups, including ArgumentParser.
Definition args.hxx:1623
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:1777
std::vector< Base * >::size_type MatchedChildren() const
Count the number of matched children this group has.
Definition args.hxx:1784
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:1768
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:1849
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:1683
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:1759
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:1681
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:1805
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:1708
bool Get() const
Get validation.
Definition args.hxx:1812
const std::vector< Base * > & Children() const
Get all this group's children.
Definition args.hxx:1698
std::vector< Base * > GetMatchedChildren() const
Get the list of children which were matched.
Definition args.hxx:1793
void Add(Base &child)
Append a child to this Group.
Definition args.hxx:1691
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:1819
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:1743
Help flag class.
Definition args.hxx:3647
bool Get() const noexcept
Get whether this was matched.
Definition args.hxx:3665
virtual void ParseValue(const std::vector< std::string > &)
Parse values of this option.
Definition args.hxx:3653
An exception that indicates that the user has requested help.
Definition args.hxx:547
An optional argument-accepting flag class.
Definition args.hxx:4054
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:4082
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4077
Errors in map lookups.
Definition args.hxx:529
A mapping value flag list class.
Definition args.hxx:4520
Container * operator->() noexcept
Get the values.
Definition args.hxx:4610
Container & Get() noexcept
Get the value.
Definition args.hxx:4589
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4556
Container & operator*() noexcept
Get the value.
Definition args.hxx:4596
A mapping value flag class.
Definition args.hxx:4402
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4432
T & Get() noexcept
Get the value.
Definition args.hxx:4465
T * operator->() noexcept
Get the value.
Definition args.hxx:4486
T & operator*() noexcept
Get the value.
Definition args.hxx:4472
A positional argument mapping list class.
Definition args.hxx:5029
Container & operator*() noexcept
Get the value.
Definition args.hxx:5105
Container * operator->() noexcept
Get the values.
Definition args.hxx:5119
Container & Get() noexcept
Get the value.
Definition args.hxx:5098
A positional argument mapping class.
Definition args.hxx:4918
T * operator->() noexcept
Get the value.
Definition args.hxx:4995
T & Get() noexcept
Get the value.
Definition args.hxx:4974
T & operator*() noexcept
Get the value.
Definition args.hxx:4981
A class of "matchers", specifying short and flags that can possibly be matched.
Definition args.hxx:633
EitherFlag GetShortOrAny() const
(INTERNAL) Get short flag if it exists or any long flag
Definition args.hxx:751
std::vector< EitherFlag > GetFlagStrings() const
(INTERNAL) Get all flag strings as a vector, with the prefixes embedded
Definition args.hxx:716
Matcher(Short &&shortIn, Long &&longIn)
Specify short and long flags separately as iterables.
Definition args.hxx:669
Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd)
Specify short and long flags separately as iterators.
Definition args.hxx:644
bool Match(const std::string &flag) const
(INTERNAL) Check if there is a match of a long flag
Definition args.hxx:702
EitherFlag GetLongOrAny() const
(INTERNAL) Get long flag if it exists or any short flag
Definition args.hxx:733
bool Match(const char flag) const
(INTERNAL) Check if there is a match of a short flag
Definition args.hxx:695
bool Match(const EitherFlag &flag) const
(INTERNAL) Check if there is a match of a flag
Definition args.hxx:709
Matcher(std::initializer_list< EitherFlag > in)
Specify a mixed single initializer-list of both short and long flags.
Definition args.hxx:685
Base class for all match types that have a name.
Definition args.hxx:1156
void HelpDefault(const std::string &str)
Sets default value string that will be added to argument description.
Definition args.hxx:1192
void HelpChoices(const std::vector< std::string > &array)
Sets choices strings that will be added to argument description.
Definition args.hxx:1208
std::string HelpDefault(const HelpParams &params) const
Gets default value string that will be added to argument description.
Definition args.hxx:1200
std::vector< std::string > HelpChoices(const HelpParams &params) const
Gets choices strings that will be added to argument description.
Definition args.hxx:1216
A variadic arguments accepting flag class.
Definition args.hxx:4105
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:4137
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4142
List< T > * operator->() noexcept
Get the values.
Definition args.hxx:4183
List< T > & operator*() noexcept
Get the value.
Definition args.hxx:4169
Errors that occur during regular parsing.
Definition args.hxx:502
Base class for positional options.
Definition args.hxx:1563
A positional argument class that pushes the found values into a list.
Definition args.hxx:4769
Container & operator*() noexcept
Get the value.
Definition args.hxx:4831
Container & Get() noexcept
Get the values.
Definition args.hxx:4824
Container * operator->() noexcept
Get the values.
Definition args.hxx:4845
A positional argument class.
Definition args.hxx:4684
T & operator*() noexcept
Get the value.
Definition args.hxx:4725
T & Get() noexcept
Get the value.
Definition args.hxx:4718
T * operator->() noexcept
Get the value.
Definition args.hxx:4739
Errors that when a required flag is omitted.
Definition args.hxx:520
Utility class for building subparsers with coroutines/callbacks.
Definition args.hxx:1967
void Parse()
Continue parsing arguments for new command.
Definition args.hxx:3575
const std::vector< std::string > & KickedOut() const noexcept
Returns a vector of kicked out arguments.
Definition args.hxx:2011
bool IsParsed() const
(INTERNAL) Determines whether Parse was called or not.
Definition args.hxx:1998
Errors that occur during usage.
Definition args.hxx:493
Errors that are detected from group validation after parsing finishes.
Definition args.hxx:511
Base class for value-accepting flag options.
Definition args.hxx:1447
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1453
An argument-accepting flag class that pushes the found values into a list.
Definition args.hxx:4254
Container * operator->() noexcept
Get the values.
Definition args.hxx:4324
Container & Get() noexcept
Get the values.
Definition args.hxx:4303
Container & operator*() noexcept
Get the value.
Definition args.hxx:4310
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4284
An argument-accepting flag class.
Definition args.hxx:3952
T & operator*() noexcept
Get the value.
Definition args.hxx:4011
T * operator->() noexcept
Get the value.
Definition args.hxx:4025
T & Get() noexcept
Get the value.
Definition args.hxx:4004
const T & GetDefault() noexcept
Get the default value.
Definition args.hxx:4039
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:3982
Definition args.hxx:1251
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:771
@ 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:575
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:600
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:585
Default validators.
Definition args.hxx:1632
A simple structure of parameters for easy user-modifyable help menus.
Definition args.hxx:827
std::string programName
The program name for help generation.
Definition args.hxx:884
bool showCommandFullHelp
Show command's descriptions and epilog.
Definition args.hxx:892
bool proglineShowFlags
Show flags in program line.
Definition args.hxx:928
std::string usageString
Program line prefix.
Definition args.hxx:936
unsigned int width
The width of the help menu.
Definition args.hxx:830
std::string proglineNonrequiredClose
The postfix for progline non-required argument.
Definition args.hxx:924
unsigned int helpindent
The indent of the flag descriptions.
Definition args.hxx:845
bool proglinePreferShortFlags
Use short flags in program lines when possible.
Definition args.hxx:932
std::string proglineCommand
The prefix for progline when command has any subcommands.
Definition args.hxx:900
std::string proglineOptions
The postfix for progline when showProglineOptions is true and command has any flags.
Definition args.hxx:896
std::string longSeparator
The separator for long flags.
Definition args.hxx:880
bool showValueName
Show value name.
Definition args.hxx:948
bool showTerminator
Show the terminator when both options and positional parameters are present.
Definition args.hxx:856
std::string proglineValueOpen
The prefix for progline value.
Definition args.hxx:904
std::string valueOpen
The prefix for option value.
Definition args.hxx:956
unsigned int progtailindent
The indent of the program trailing lines for long parameters.
Definition args.hxx:836
std::string proglineNonrequiredOpen
The prefix for progline non-required argument.
Definition args.hxx:920
unsigned int descriptionindent
The indent of the description and epilogs.
Definition args.hxx:839
bool showCommandChildren
Show command's flags.
Definition args.hxx:888
bool showProglineOptions
Show the {OPTIONS} on the prog line when this is true.
Definition args.hxx:860
std::string valueClose
The postfix for option value.
Definition args.hxx:960
std::string longPrefix
The prefix for long flags.
Definition args.hxx:872
std::string proglineRequiredClose
The postfix for progline required argument.
Definition args.hxx:916
unsigned int flagindent
The indent of the flags.
Definition args.hxx:842
std::string optionsString
String shown in help before flags descriptions.
Definition args.hxx:940
bool addNewlineBeforeDescription
Add newline before flag description.
Definition args.hxx:952
std::string shortPrefix
The prefix for short flags.
Definition args.hxx:868
bool addDefault
Add default values to argument description.
Definition args.hxx:972
unsigned int gutter
The minimum gutter between each flag and its help.
Definition args.hxx:852
bool showProglinePositionals
Show the positionals on the prog line when this is true.
Definition args.hxx:864
std::string shortSeparator
The separator for short flags.
Definition args.hxx:876
std::string proglineValueClose
The postfix for progline value.
Definition args.hxx:908
bool useValueNameOnce
Display value name after all the long and short flags.
Definition args.hxx:944
std::string defaultString
The prefix for default values.
Definition args.hxx:976
std::string proglineRequiredOpen
The prefix for progline required argument.
Definition args.hxx:912
unsigned int eachgroupindent
The additional indent each group adds.
Definition args.hxx:848
unsigned int progindent
The indent of the program line.
Definition args.hxx:833
bool addChoices
Add choices to argument description.
Definition args.hxx:964
std::string choiceString
The prefix for choices.
Definition args.hxx:968
A number of arguments which can be consumed by an option.
Definition args.hxx:984
A default Reader class for argument classes.
Definition args.hxx:3770