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)
109 {
110 static_assert(std::is_integral<T>::value, "SafeAdd requires integral types.");
111 if (std::is_unsigned<T>::value)
112 {
113 if (b > std::numeric_limits<T>::max() - a)
114 {
115 return false;
116 }
117 } else
118 {
119 if ((b > 0 && a > std::numeric_limits<T>::max() - b) ||
120 (b < 0 && a < std::numeric_limits<T>::min() - b))
121 {
122 return false;
123 }
124 }
125 out = a + b;
126 return true;
127 }
128
132 template<typename T>
133 bool SafeMultiply(T a, T b, T& out)
134 {
135 static_assert(std::is_integral<T>::value, "SafeMultiply requires integral types.");
136
137 if (a == 0 || b == 0)
138 {
139 out = 0;
140 return true;
141 }
142
143 if (std::is_unsigned<T>::value)
144 {
145 if (b > std::numeric_limits<T>::max() / a)
146 {
147 return false; // Overflow would occur
148 }
149 }
150 else
151 {
152 if (a == -1 && b == std::numeric_limits<T>::min())
153 {
154 return false;
155 }
156 if (b == -1 && a == std::numeric_limits<T>::min())
157 {
158 return false;
159 }
160
161 if ((a > 0 && b > 0 && a > std::numeric_limits<T>::max() / b) ||
162 (a > 0 && b < 0 && b < std::numeric_limits<T>::min() / a) ||
163 (a < 0 && b > 0 && a < std::numeric_limits<T>::min() / b) ||
164 (a < 0 && b < 0 && a < std::numeric_limits<T>::max() / b))
165 {
166 return false;
167 }
168 }
169
170 out = a * b;
171 return true;
172 }
173
185 template <typename It>
186 inline std::vector<std::string> Wrap(It begin,
187 It end,
188 const std::string::size_type width,
189 std::string::size_type firstlinewidth = 0,
190 std::string::size_type firstlineindent = 0)
191 {
192 std::vector<std::string> output;
193 std::string line(firstlineindent, ' ');
194 bool empty = true;
195
196 if (firstlinewidth == 0)
197 {
198 firstlinewidth = width;
199 }
200
201 auto currentwidth = firstlinewidth;
202
203 for (auto it = begin; it != end; ++it)
204 {
205 if (it->empty())
206 {
207 continue;
208 }
209
210 if (*it == "\n")
211 {
212 if (!empty)
213 {
214 output.push_back(line);
215 line.clear();
216 empty = true;
217 currentwidth = width;
218 }
219
220 continue;
221 }
222
223 auto itemsize = Glyphs(*it);
224
225 // Refactored to prevent integer overflow
226 bool needsWrap = false;
227 if (itemsize >= currentwidth)
228 {
229 needsWrap = true;
230 }
231 else
232 {
233 size_t remainingWidth = (currentwidth > itemsize) ? (currentwidth - itemsize) : 0;
234 size_t nextLength = 0;
235 if (!SafeAdd<std::string::size_type>(line.length(), static_cast<std::string::size_type>(1), nextLength) || nextLength > remainingWidth)
236 {
237 needsWrap = true;
238 }
239 }
240
241 if (needsWrap)
242 {
243 if (!empty)
244 {
245 output.push_back(line);
246 line.clear();
247 empty = true;
248 currentwidth = width;
249 }
250 }
251
252 if (itemsize > 0)
253 {
254 if (!empty)
255 {
256 line += ' ';
257 }
258
259 line += *it;
260 empty = false;
261 }
262 }
263
264 if (!empty)
265 {
266 output.push_back(line);
267 }
268
269 return output;
270 }
271
272 namespace detail
273 {
274 template <typename T>
275 std::string Join(const T& array, const std::string &delimiter)
276 {
277 std::string res;
278 for (auto &element : array)
279 {
280 if (!res.empty())
281 {
282 res += delimiter;
283 }
284
285 res += element;
286 }
287
288 return res;
289 }
290 }
291
301 inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
302 {
303 // Preserve existing line breaks
304 const auto newlineloc = in.find('\n');
305 if (newlineloc != in.npos)
306 {
307 auto first = Wrap(std::string(in, 0, newlineloc), width);
308 auto second = Wrap(std::string(in, newlineloc + 1), width);
309 first.insert(
310 std::end(first),
311 std::make_move_iterator(std::begin(second)),
312 std::make_move_iterator(std::end(second)));
313 return first;
314 }
315
316 std::istringstream stream(in);
317 std::string::size_type indent = 0;
318
319 for (auto c : in)
320 {
321 if (!std::isspace(static_cast<unsigned char>(c)))
322 {
323 break;
324 }
325 ++indent;
326 }
327
328 return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(),
329 width, firstlinewidth, indent);
330 }
331
332#ifdef ARGS_NOEXCEPT
334 enum class Error
335 {
336 None,
337 Usage,
338 Parse,
339 Validation,
340 Required,
341 Map,
342 Extra,
343 Help,
344 Subparser,
345 Completion,
346 };
347#else
350 class Error : public std::runtime_error
351 {
352 public:
353 Error(const std::string &problem) : std::runtime_error(problem) {}
354 virtual ~Error() {}
355 };
356
359 class UsageError : public Error
360 {
361 public:
362 UsageError(const std::string &problem) : Error(problem) {}
363 virtual ~UsageError() {}
364 };
365
368 class ParseError : public Error
369 {
370 public:
371 ParseError(const std::string &problem) : Error(problem) {}
372 virtual ~ParseError() {}
373 };
374
377 class ValidationError : public Error
378 {
379 public:
380 ValidationError(const std::string &problem) : Error(problem) {}
381 virtual ~ValidationError() {}
382 };
383
387 {
388 public:
389 RequiredError(const std::string &problem) : ValidationError(problem) {}
390 virtual ~RequiredError() {}
391 };
392
395 class MapError : public ParseError
396 {
397 public:
398 MapError(const std::string &problem) : ParseError(problem) {}
399 virtual ~MapError() {}
400 };
401
404 class ExtraError : public ParseError
405 {
406 public:
407 ExtraError(const std::string &problem) : ParseError(problem) {}
408 virtual ~ExtraError() {}
409 };
410
413 class Help : public Error
414 {
415 public:
416 Help(const std::string &flag) : Error(flag) {}
417 virtual ~Help() {}
418 };
419
422 class SubparserError : public Error
423 {
424 public:
425 SubparserError() : Error("") {}
426 virtual ~SubparserError() {}
427 };
428
431 class Completion : public Error
432 {
433 public:
434 Completion(const std::string &flag) : Error(flag) {}
435 virtual ~Completion() {}
436 };
437#endif
438
442 {
443 const bool isShort;
444 const char shortFlag;
445 const std::string longFlag;
446 EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {}
447 EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {}
448 EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {}
449
452 static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags)
453 {
454 std::unordered_set<std::string> longFlags;
455 for (const EitherFlag &flag: flags)
456 {
457 if (!flag.isShort)
458 {
459 longFlags.insert(flag.longFlag);
460 }
461 }
462 return longFlags;
463 }
464
467 static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags)
468 {
469 std::unordered_set<char> shortFlags;
470 for (const EitherFlag &flag: flags)
471 {
472 if (flag.isShort)
473 {
474 shortFlags.insert(flag.shortFlag);
475 }
476 }
477 return shortFlags;
478 }
479
480 std::string str() const
481 {
482 return isShort ? std::string(1, shortFlag) : longFlag;
483 }
484
485 std::string str(const std::string &shortPrefix, const std::string &longPrefix) const
486 {
487 return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag;
488 }
489 };
490
491
492
500 {
501 private:
502 const std::unordered_set<char> shortFlags;
503 const std::unordered_set<std::string> longFlags;
504
505 public:
510 template <typename ShortIt, typename LongIt>
511 Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) :
512 shortFlags(shortFlagsStart, shortFlagsEnd),
513 longFlags(longFlagsStart, longFlagsEnd)
514 {
515 if (shortFlags.empty() && longFlags.empty())
516 {
517#ifndef ARGS_NOEXCEPT
518 throw UsageError("empty Matcher");
519#endif
520 }
521 }
522
523#ifdef ARGS_NOEXCEPT
525 Error GetError() const noexcept
526 {
527 return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None;
528 }
529#endif
530
535 template <typename Short, typename Long>
536 Matcher(Short &&shortIn, Long &&longIn) :
537 Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn))
538 {}
539
552 Matcher(std::initializer_list<EitherFlag> in) :
553 Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {}
554
555 Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags))
556 {}
557
558 ~Matcher() {}
559
562 bool Match(const char flag) const
563 {
564 return shortFlags.find(flag) != shortFlags.end();
565 }
566
569 bool Match(const std::string &flag) const
570 {
571 return longFlags.find(flag) != longFlags.end();
572 }
573
576 bool Match(const EitherFlag &flag) const
577 {
578 return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag);
579 }
580
583 std::vector<EitherFlag> GetFlagStrings() const
584 {
585 std::vector<EitherFlag> flagStrings;
586 flagStrings.reserve(shortFlags.size() + longFlags.size());
587 for (const char flag: shortFlags)
588 {
589 flagStrings.emplace_back(flag);
590 }
591 for (const std::string &flag: longFlags)
592 {
593 flagStrings.emplace_back(flag);
594 }
595 return flagStrings;
596 }
597
601 {
602 if (!longFlags.empty())
603 {
604 return *longFlags.begin();
605 }
606
607 if (!shortFlags.empty())
608 {
609 return *shortFlags.begin();
610 }
611
612 // should be unreachable
613 return ' ';
614 }
615
619 {
620 if (!shortFlags.empty())
621 {
622 return *shortFlags.begin();
623 }
624
625 if (!longFlags.empty())
626 {
627 return *longFlags.begin();
628 }
629
630 // should be unreachable
631 return ' ';
632 }
633 };
634
637 enum class Options
638 {
641 None = 0x0,
642
645 Single = 0x01,
646
649 Required = 0x02,
650
653 HiddenFromUsage = 0x04,
654
658
661 Global = 0x10,
662
665 KickOut = 0x20,
666
670
674 };
675
676 inline Options operator | (Options lhs, Options rhs)
677 {
678 return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs));
679 }
680
681 inline Options operator & (Options lhs, Options rhs)
682 {
683 return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs));
684 }
685
686 class FlagBase;
687 class PositionalBase;
688 class Command;
689 class ArgumentParser;
690
694 {
697 unsigned int width = 80;
700 unsigned int progindent = 2;
703 unsigned int progtailindent = 4;
706 unsigned int descriptionindent = 4;
709 unsigned int flagindent = 6;
712 unsigned int helpindent = 40;
715 unsigned int eachgroupindent = 2;
716
719 unsigned int gutter = 1;
720
723 bool showTerminator = true;
724
728
732
735 std::string shortPrefix;
736
739 std::string longPrefix;
740
743 std::string shortSeparator;
744
747 std::string longSeparator;
748
751 std::string programName;
752
756
760
763 std::string proglineOptions = "{OPTIONS}";
764
767 std::string proglineCommand = "COMMAND";
768
771 std::string proglineValueOpen = " <";
772
775 std::string proglineValueClose = ">";
776
779 std::string proglineRequiredOpen = "";
780
783 std::string proglineRequiredClose = "";
784
787 std::string proglineNonrequiredOpen = "[";
788
791 std::string proglineNonrequiredClose = "]";
792
795 bool proglineShowFlags = false;
796
800
803 std::string usageString;
804
807 std::string optionsString = "OPTIONS:";
808
811 bool useValueNameOnce = false;
812
815 bool showValueName = true;
816
820
823 std::string valueOpen = "[";
824
827 std::string valueClose = "]";
828
831 bool addChoices = false;
832
835 std::string choiceString = "\nOne of: ";
836
839 bool addDefault = false;
840
843 std::string defaultString = "\nDefault: ";
844 };
845
850 struct Nargs
851 {
852 const size_t min;
853 const size_t max;
854
855 Nargs(size_t min_, size_t max_) : min{min_}, max{max_}
856 {
857#ifndef ARGS_NOEXCEPT
858 if (max < min)
859 {
860 throw UsageError("Nargs: max > min");
861 }
862#endif
863 }
864
865 Nargs(size_t num_) : min{num_}, max{num_}
866 {
867 }
868
869 friend bool operator == (const Nargs &lhs, const Nargs &rhs)
870 {
871 return lhs.min == rhs.min && lhs.max == rhs.max;
872 }
873
874 friend bool operator != (const Nargs &lhs, const Nargs &rhs)
875 {
876 return !(lhs == rhs);
877 }
878 };
879
882 class Base
883 {
884 private:
885 Options options = {};
886
887 protected:
888 bool matched = false;
889 const std::string help;
890#ifdef ARGS_NOEXCEPT
892 mutable Error error = Error::None;
893 mutable std::string errorMsg;
894#endif
895
896 public:
897 Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {}
898 virtual ~Base() {}
899
900 Options GetOptions() const noexcept
901 {
902 return options;
903 }
904
905 bool IsRequired() const noexcept
906 {
907 return (GetOptions() & Options::Required) != Options::None;
908 }
909
910 virtual bool Matched() const noexcept
911 {
912 return matched;
913 }
914
915 virtual void Validate(const std::string &, const std::string &) const
916 {
917 }
918
919 operator bool() const noexcept
920 {
921 return Matched();
922 }
923
924 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const
925 {
926 std::tuple<std::string, std::string, unsigned> description;
927 std::get<1>(description) = help;
928 std::get<2>(description) = indentLevel;
929 return { std::move(description) };
930 }
931
932 virtual std::vector<Command*> GetCommands()
933 {
934 return {};
935 }
936
937 virtual bool IsGroup() const
938 {
939 return false;
940 }
941
942 virtual FlagBase *Match(const EitherFlag &)
943 {
944 return nullptr;
945 }
946
947 virtual PositionalBase *GetNextPositional()
948 {
949 return nullptr;
950 }
951
952 virtual std::vector<FlagBase*> GetAllFlags()
953 {
954 return {};
955 }
956
957 virtual bool HasFlag() const
958 {
959 return false;
960 }
961
962 virtual bool HasPositional() const
963 {
964 return false;
965 }
966
967 virtual bool HasCommand() const
968 {
969 return false;
970 }
971
972 virtual std::vector<std::string> GetProgramLine(const HelpParams &) const
973 {
974 return {};
975 }
976
978 void KickOut(bool kickout_) noexcept
979 {
980 if (kickout_)
981 {
982 options = options | Options::KickOut;
983 }
984 else
985 {
986 options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut));
987 }
988 }
989
991 bool KickOut() const noexcept
992 {
993 return (options & Options::KickOut) != Options::None;
994 }
995
996 virtual void Reset() noexcept
997 {
998 matched = false;
999#ifdef ARGS_NOEXCEPT
1000 error = Error::None;
1001 errorMsg.clear();
1002#endif
1003 }
1004
1005#ifdef ARGS_NOEXCEPT
1007 virtual Error GetError() const
1008 {
1009 return error;
1010 }
1011
1013 virtual std::string GetErrorMsg() const
1014 {
1015 return errorMsg;
1016 }
1017#endif
1018 };
1019
1022 class NamedBase : public Base
1023 {
1024 protected:
1025 const std::string name;
1026 bool kickout = false;
1027 std::string defaultString;
1028 bool defaultStringManual = false;
1029 std::vector<std::string> choicesStrings;
1030 bool choicesStringManual = false;
1031
1032 virtual std::string GetDefaultString(const HelpParams&) const { return {}; }
1033
1034 virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; }
1035
1036 virtual std::string GetNameString(const HelpParams&) const { return Name(); }
1037
1038 void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const
1039 {
1040 if (isManual && !manual.empty())
1041 {
1042 dest += str;
1043 dest += manual;
1044 }
1045 else if (!isManual && isGenerated && !generated.empty())
1046 {
1047 dest += str;
1048 dest += generated;
1049 }
1050 }
1051
1052 public:
1053 NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {}
1054 virtual ~NamedBase() {}
1055
1059 void HelpDefault(const std::string &str)
1060 {
1061 defaultStringManual = true;
1062 defaultString = str;
1063 }
1064
1067 std::string HelpDefault(const HelpParams &params) const
1068 {
1069 return defaultStringManual ? defaultString : GetDefaultString(params);
1070 }
1071
1075 void HelpChoices(const std::vector<std::string> &array)
1076 {
1077 choicesStringManual = true;
1078 choicesStrings = array;
1079 }
1080
1083 std::vector<std::string> HelpChoices(const HelpParams &params) const
1084 {
1085 return choicesStringManual ? choicesStrings : GetChoicesStrings(params);
1086 }
1087
1088 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned indentLevel) const override
1089 {
1090 std::tuple<std::string, std::string, unsigned> description;
1091 std::get<0>(description) = GetNameString(params);
1092 std::get<1>(description) = help;
1093 std::get<2>(description) = indentLevel;
1094
1095 AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString);
1096 AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString);
1097
1098 return { std::move(description) };
1099 }
1100
1101 virtual std::string Name() const
1102 {
1103 return name;
1104 }
1105 };
1106
1107 namespace detail
1108 {
1109 template<typename T>
1110 using vector = std::vector<T, std::allocator<T>>;
1111
1112 template<typename K, typename T>
1113 using unordered_map = std::unordered_map<K, T, std::hash<K>,
1114 std::equal_to<K>, std::allocator<std::pair<const K, T> > >;
1115
1116 template<typename S, typename T>
1118 {
1119 template<typename SS, typename TT>
1120 static auto test(int)
1121 -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
1122
1123 template<typename, typename>
1124 static auto test(...) -> std::false_type;
1125
1126 public:
1127 using type = decltype(test<S,T>(0));
1128 };
1129
1130 template <typename T>
1131 using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type;
1132
1133 template <typename T>
1134 typename std::enable_if<IsConvertableToString<T>::value, std::string>::type
1135 ToString(const T &value)
1136 {
1137 std::ostringstream s;
1138 s << value;
1139 return s.str();
1140 }
1141
1142 template <typename T>
1143 typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type
1144 ToString(const T &)
1145 {
1146 return {};
1147 }
1148
1149 template <typename T>
1150 std::vector<std::string> MapKeysToStrings(const T &map)
1151 {
1152 std::vector<std::string> res;
1153 using K = typename std::decay<decltype(std::begin(map)->first)>::type;
1154 if (IsConvertableToString<K>::value)
1155 {
1156 for (const auto &p : map)
1157 {
1158 res.push_back(detail::ToString(p.first));
1159 }
1160
1161 std::sort(res.begin(), res.end());
1162 }
1163 return res;
1164 }
1165 }
1166
1169 class FlagBase : public NamedBase
1170 {
1171 protected:
1172 const Matcher matcher;
1173
1174 virtual std::string GetNameString(const HelpParams &params) const override
1175 {
1176 const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name();
1177 std::string flags;
1178 const auto flagStrings = matcher.GetFlagStrings();
1179 const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce;
1180 for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it)
1181 {
1182 auto &flag = *it;
1183 if (it != flagStrings.begin())
1184 {
1185 flags += ", ";
1186 }
1187
1188 flags += flag.isShort ? params.shortPrefix : params.longPrefix;
1189 flags += flag.str();
1190
1191 if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end()))
1192 {
1193 flags += flag.isShort ? params.shortSeparator : params.longSeparator;
1194 flags += params.valueOpen + postfix + params.valueClose;
1195 }
1196 }
1197
1198 return flags;
1199 }
1200
1201 public:
1202 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_)) {}
1203
1204 FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {}
1205
1206 virtual ~FlagBase() {}
1207
1208 virtual FlagBase *Match(const EitherFlag &flag) override
1209 {
1210 if (matcher.Match(flag))
1211 {
1212 if ((GetOptions() & Options::Single) != Options::None && matched)
1213 {
1214 std::ostringstream problem;
1215 problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once";
1216#ifdef ARGS_NOEXCEPT
1217 error = Error::Extra;
1218 errorMsg = problem.str();
1219#else
1220 throw ExtraError(problem.str());
1221#endif
1222 }
1223 matched = true;
1224 return this;
1225 }
1226 return nullptr;
1227 }
1228
1229 virtual std::vector<FlagBase*> GetAllFlags() override
1230 {
1231 return { this };
1232 }
1233
1234 const Matcher &GetMatcher() const
1235 {
1236 return matcher;
1237 }
1238
1239 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1240 {
1241 if (!Matched() && IsRequired())
1242 {
1243 std::ostringstream problem;
1244 problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required";
1245#ifdef ARGS_NOEXCEPT
1246 error = Error::Required;
1247 errorMsg = problem.str();
1248#else
1249 throw RequiredError(problem.str());
1250#endif
1251 }
1252 }
1253
1254 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1255 {
1256 if (!params.proglineShowFlags)
1257 {
1258 return {};
1259 }
1260
1261 const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name();
1262 const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny();
1263 std::string res = flag.str(params.shortPrefix, params.longPrefix);
1264 if (!postfix.empty())
1265 {
1266 res += params.proglineValueOpen + postfix + params.proglineValueClose;
1267 }
1268
1269 return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose
1270 : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose };
1271 }
1272
1273 virtual bool HasFlag() const override
1274 {
1275 return true;
1276 }
1277
1278#ifdef ARGS_NOEXCEPT
1280 virtual Error GetError() const override
1281 {
1282 const auto nargs = NumberOfArguments();
1283 if (nargs.min > nargs.max)
1284 {
1285 return Error::Usage;
1286 }
1287
1288 const auto matcherError = matcher.GetError();
1289 if (matcherError != Error::None)
1290 {
1291 return matcherError;
1292 }
1293
1294 return error;
1295 }
1296#endif
1297
1302 virtual Nargs NumberOfArguments() const noexcept = 0;
1303
1308 virtual void ParseValue(const std::vector<std::string> &value) = 0;
1309 };
1310
1314 {
1315 public:
1316 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {}
1317 ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {}
1318 virtual ~ValueFlagBase() {}
1319
1320 virtual Nargs NumberOfArguments() const noexcept override
1321 {
1322 return 1;
1323 }
1324 };
1325
1327 {
1328 public:
1329 std::vector<std::string> reply;
1330 size_t cword = 0;
1331 std::string syntax;
1332
1333 template <typename GroupClass>
1334 CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden)
1335 {
1336 group_.AddCompletion(*this);
1337 }
1338
1339 virtual ~CompletionFlag() {}
1340
1341 virtual Nargs NumberOfArguments() const noexcept override
1342 {
1343 return 2;
1344 }
1345
1346 virtual void ParseValue(const std::vector<std::string> &value_) override
1347 {
1348 syntax = value_.at(0);
1349 const std::string &raw = value_.at(1);
1350 bool failed = false;
1351
1352 const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c)
1353 {
1354 return std::isspace(static_cast<unsigned char>(c)) != 0;
1355 });
1356
1357 if (firstNonSpace != raw.end() && *firstNonSpace == '-')
1358 {
1359 failed = true;
1360 }
1361
1362 size_t parsed = 0;
1363 if (!failed)
1364 {
1365 std::istringstream ss(raw);
1366 ss >> parsed;
1367 if (ss.fail())
1368 {
1369 failed = true;
1370 }
1371 else
1372 {
1373 char extra;
1374 if (ss >> extra)
1375 {
1376 failed = true;
1377 }
1378 else if (!ss.eof())
1379 {
1380 failed = true;
1381 }
1382 }
1383 }
1384
1385 if (failed)
1386 {
1387#ifdef ARGS_NOEXCEPT
1388 error = Error::Parse;
1389 errorMsg = "Argument 'completion' received invalid value type '" + raw + "'";
1390#else
1391 std::ostringstream problem;
1392 problem << "Argument 'completion' received invalid value type '" << raw << "'";
1393 throw ParseError(problem.str());
1394#endif
1395 return;
1396 }
1397
1398 cword = parsed;
1399 }
1400
1403 std::string Get() noexcept
1404 {
1405 return detail::Join(reply, "\n");
1406 }
1407
1408 virtual void Reset() noexcept override
1409 {
1410 ValueFlagBase::Reset();
1411 cword = 0;
1412 syntax.clear();
1413 reply.clear();
1414 }
1415 };
1416
1417
1421 {
1422 protected:
1423 bool ready;
1424
1425 public:
1426 PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {}
1427 virtual ~PositionalBase() {}
1428
1429 bool Ready()
1430 {
1431 return ready;
1432 }
1433
1434 virtual void ParseValue(const std::string &value_) = 0;
1435
1436 virtual void Reset() noexcept override
1437 {
1438 matched = false;
1439 ready = true;
1440#ifdef ARGS_NOEXCEPT
1441 error = Error::None;
1442 errorMsg.clear();
1443#endif
1444 }
1445
1446 virtual PositionalBase *GetNextPositional() override
1447 {
1448 return Ready() ? this : nullptr;
1449 }
1450
1451 virtual bool HasPositional() const override
1452 {
1453 return true;
1454 }
1455
1456 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1457 {
1458 return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose
1459 : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose };
1460 }
1461
1462 virtual void Validate(const std::string &, const std::string &) const override
1463 {
1464 if (IsRequired() && !Matched())
1465 {
1466 std::ostringstream problem;
1467 problem << "Option '" << Name() << "' is required";
1468#ifdef ARGS_NOEXCEPT
1469 error = Error::Required;
1470 errorMsg = problem.str();
1471#else
1472 throw RequiredError(problem.str());
1473#endif
1474 }
1475 }
1476 };
1477
1480 class Group : public Base
1481 {
1482 private:
1483 std::vector<Base*> children;
1484 std::function<bool(const Group &)> validator;
1485
1486 public:
1490 {
1491 static bool Xor(const Group &group)
1492 {
1493 return group.MatchedChildren() == 1;
1494 }
1495
1496 static bool AtLeastOne(const Group &group)
1497 {
1498 return group.MatchedChildren() >= 1;
1499 }
1500
1501 static bool AtMostOne(const Group &group)
1502 {
1503 return group.MatchedChildren() <= 1;
1504 }
1505
1506 static bool All(const Group &group)
1507 {
1508 return group.Children().size() == group.MatchedChildren();
1509 }
1510
1511 static bool AllOrNone(const Group &group)
1512 {
1513 return (All(group) || None(group));
1514 }
1515
1516 static bool AllChildGroups(const Group &group)
1517 {
1518 return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool {
1519 return child->IsGroup() && !child->Matched();
1520 });
1521 }
1522
1523 static bool DontCare(const Group &)
1524 {
1525 return true;
1526 }
1527
1528 static bool CareTooMuch(const Group &)
1529 {
1530 return false;
1531 }
1532
1533 static bool None(const Group &group)
1534 {
1535 return group.MatchedChildren() == 0;
1536 }
1537 };
1539 Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) {}
1541 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_)
1542 {
1543 group_.Add(*this);
1544 }
1545 virtual ~Group() {}
1546
1549 void Add(Base &child)
1550 {
1551 children.emplace_back(&child);
1552 }
1553
1556 const std::vector<Base *> &Children() const
1557 {
1558 return children;
1559 }
1560
1566 virtual FlagBase *Match(const EitherFlag &flag) override
1567 {
1568 for (Base *child: Children())
1569 {
1570 if (FlagBase *match = child->Match(flag))
1571 {
1572 return match;
1573 }
1574 }
1575 return nullptr;
1576 }
1577
1578 virtual std::vector<FlagBase*> GetAllFlags() override
1579 {
1580 std::vector<FlagBase*> res;
1581 for (Base *child: Children())
1582 {
1583 auto childRes = child->GetAllFlags();
1584 res.insert(res.end(), childRes.begin(), childRes.end());
1585 }
1586 return res;
1587 }
1588
1589 virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override
1590 {
1591 for (Base *child: Children())
1592 {
1593 child->Validate(shortPrefix, longPrefix);
1594 }
1595 }
1596
1602 {
1603 for (Base *child: Children())
1604 {
1605 if (auto next = child->GetNextPositional())
1606 {
1607 return next;
1608 }
1609 }
1610 return nullptr;
1611 }
1612
1617 virtual bool HasFlag() const override
1618 {
1619 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); });
1620 }
1621
1626 virtual bool HasPositional() const override
1627 {
1628 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); });
1629 }
1630
1635 virtual bool HasCommand() const override
1636 {
1637 return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); });
1638 }
1639
1642 std::vector<Base *>::size_type MatchedChildren() const
1643 {
1644 // Cast to avoid warnings from -Wsign-conversion
1645 return static_cast<std::vector<Base *>::size_type>(
1646 std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}));
1647 }
1648
1651 virtual bool Matched() const noexcept override
1652 {
1653 return validator(*this);
1654 }
1655
1658 bool Get() const
1659 {
1660 return Matched();
1661 }
1662
1665 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
1666 {
1667 std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions;
1668
1669 // Push that group description on the back if not empty
1670 unsigned addindent = 0;
1671 if (!help.empty())
1672 {
1673 descriptions.emplace_back(help, "", indent);
1674 addindent = 1;
1675 }
1676
1677 for (Base *child: Children())
1678 {
1679 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
1680 {
1681 continue;
1682 }
1683
1684 auto groupDescriptions = child->GetDescription(params, indent + addindent);
1685 descriptions.insert(
1686 std::end(descriptions),
1687 std::make_move_iterator(std::begin(groupDescriptions)),
1688 std::make_move_iterator(std::end(groupDescriptions)));
1689 }
1690 return descriptions;
1691 }
1692
1695 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
1696 {
1697 std::vector <std::string> names;
1698 for (Base *child: Children())
1699 {
1700 if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None)
1701 {
1702 continue;
1703 }
1704
1705 auto groupNames = child->GetProgramLine(params);
1706 names.insert(
1707 std::end(names),
1708 std::make_move_iterator(std::begin(groupNames)),
1709 std::make_move_iterator(std::end(groupNames)));
1710 }
1711 return names;
1712 }
1713
1714 virtual std::vector<Command*> GetCommands() override
1715 {
1716 std::vector<Command*> res;
1717 for (const auto &child : Children())
1718 {
1719 auto subparsers = child->GetCommands();
1720 res.insert(std::end(res), std::begin(subparsers), std::end(subparsers));
1721 }
1722 return res;
1723 }
1724
1725 virtual bool IsGroup() const override
1726 {
1727 return true;
1728 }
1729
1730 virtual void Reset() noexcept override
1731 {
1732 Base::Reset();
1733
1734 for (auto &child: Children())
1735 {
1736 child->Reset();
1737 }
1738#ifdef ARGS_NOEXCEPT
1739 error = Error::None;
1740 errorMsg.clear();
1741#endif
1742 }
1743
1744#ifdef ARGS_NOEXCEPT
1746 virtual Error GetError() const override
1747 {
1748 if (error != Error::None)
1749 {
1750 return error;
1751 }
1752
1753 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
1754 if (it == Children().end())
1755 {
1756 return Error::None;
1757 } else
1758 {
1759 return (*it)->GetError();
1760 }
1761 }
1762
1764 virtual std::string GetErrorMsg() const override
1765 {
1766 if (error != Error::None)
1767 {
1768 return errorMsg;
1769 }
1770
1771 auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;});
1772 if (it == Children().end())
1773 {
1774 return "";
1775 } else
1776 {
1777 return (*it)->GetErrorMsg();
1778 }
1779 }
1780#endif
1781
1782 };
1783
1786 class GlobalOptions : public Group
1787 {
1788 public:
1789 GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global)
1790 {
1791 Add(options_);
1792 }
1793 };
1794
1812 class Subparser : public Group
1813 {
1814 private:
1815 std::vector<std::string> args;
1816 std::vector<std::string> kicked;
1817 ArgumentParser *parser = nullptr;
1818 const HelpParams &helpParams;
1819 const Command &command;
1820 bool isParsed = false;
1821
1822 public:
1823 Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_)
1824 : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_)
1825 {
1826 }
1827
1828 Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_)
1829 {
1830 }
1831
1832 Subparser(const Subparser&) = delete;
1833 Subparser(Subparser&&) = delete;
1834 Subparser &operator = (const Subparser&) = delete;
1835 Subparser &operator = (Subparser&&) = delete;
1836
1837 const Command &GetCommand()
1838 {
1839 return command;
1840 }
1841
1844 bool IsParsed() const
1845 {
1846 return isParsed;
1847 }
1848
1851 void Parse();
1852
1857 const std::vector<std::string> &KickedOut() const noexcept
1858 {
1859 return kicked;
1860 }
1861 };
1862
1867 class Command : public Group
1868 {
1869 private:
1870 friend class Subparser;
1871
1872 std::string name;
1873 std::string help;
1874 std::string description;
1875 std::string epilog;
1876 std::string proglinePostfix;
1877
1878 std::function<void(Subparser&)> parserCoroutine;
1879 bool commandIsRequired = true;
1880 Command *selectedCommand = nullptr;
1881
1882 mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
1883 mutable std::vector<std::string> subparserProgramLine;
1884 mutable bool subparserHasFlag = false;
1885 mutable bool subparserHasPositional = false;
1886 mutable bool subparserHasCommand = false;
1887#ifdef ARGS_NOEXCEPT
1888 mutable Error subparserError = Error::None;
1889#endif
1890 mutable Subparser *subparser = nullptr;
1891
1892 protected:
1893
1894 class RaiiSubparser
1895 {
1896 public:
1897 RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_);
1898 RaiiSubparser(const Command &command_, const HelpParams &params_);
1899
1900 ~RaiiSubparser()
1901 {
1902 command.subparser = oldSubparser;
1903 }
1904
1905 Subparser &Parser()
1906 {
1907 return parser;
1908 }
1909
1910 private:
1911 const Command &command;
1912 Subparser parser;
1913 Subparser *oldSubparser;
1914 };
1915
1916 Command() = default;
1917
1918 std::function<void(Subparser&)> &GetCoroutine()
1919 {
1920 return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
1921 }
1922
1923 Command &SelectedCommand()
1924 {
1925 Command *res = this;
1926 while (res->selectedCommand != nullptr)
1927 {
1928 res = res->selectedCommand;
1929 }
1930
1931 return *res;
1932 }
1933
1934 const Command &SelectedCommand() const
1935 {
1936 const Command *res = this;
1937 while (res->selectedCommand != nullptr)
1938 {
1939 res = res->selectedCommand;
1940 }
1941
1942 return *res;
1943 }
1944
1945 void UpdateSubparserHelp(const HelpParams &params) const
1946 {
1947 if (parserCoroutine)
1948 {
1949 RaiiSubparser coro(*this, params);
1950#ifndef ARGS_NOEXCEPT
1951 try
1952 {
1953 parserCoroutine(coro.Parser());
1954 }
1955 catch (args::SubparserError&)
1956 {
1957 }
1958#else
1959 parserCoroutine(coro.Parser());
1960#endif
1961 }
1962 }
1963
1964 public:
1965 Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {})
1966 : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_))
1967 {
1968 base_.Add(*this);
1969 }
1970
1973 const std::string &ProglinePostfix() const
1974 { return proglinePostfix; }
1975
1978 void ProglinePostfix(const std::string &proglinePostfix_)
1979 { this->proglinePostfix = proglinePostfix_; }
1980
1983 const std::string &Description() const
1984 { return description; }
1988 void Description(const std::string &description_)
1989 { this->description = description_; }
1990
1993 const std::string &Epilog() const
1994 { return epilog; }
1995
1998 void Epilog(const std::string &epilog_)
1999 { this->epilog = epilog_; }
2000
2003 const std::string &Name() const
2004 { return name; }
2005
2008 const std::string &Help() const
2009 { return help; }
2010
2015 void RequireCommand(bool value)
2016 { commandIsRequired = value; }
2017
2018 virtual bool IsGroup() const override
2019 { return false; }
2020
2021 virtual bool Matched() const noexcept override
2022 { return Base::Matched(); }
2023
2024 operator bool() const noexcept
2025 { return Matched(); }
2026
2027 void Match() noexcept
2028 { matched = true; }
2029
2030 void SelectCommand(Command *c) noexcept
2031 {
2032 selectedCommand = c;
2033
2034 if (c != nullptr)
2035 {
2036 c->Match();
2037 }
2038 }
2039
2040 virtual FlagBase *Match(const EitherFlag &flag) override
2041 {
2042 if (selectedCommand != nullptr)
2043 {
2044 if (auto *res = selectedCommand->Match(flag))
2045 {
2046 return res;
2047 }
2048
2049 for (auto *child: Children())
2050 {
2051 if ((child->GetOptions() & Options::Global) != Options::None)
2052 {
2053 if (auto *res = child->Match(flag))
2054 {
2055 return res;
2056 }
2057 }
2058 }
2059
2060 return nullptr;
2061 }
2062
2063 if (subparser != nullptr)
2064 {
2065 return subparser->Match(flag);
2066 }
2067
2068 return Matched() ? Group::Match(flag) : nullptr;
2069 }
2070
2071 virtual std::vector<FlagBase*> GetAllFlags() override
2072 {
2073 std::vector<FlagBase*> res;
2074
2075 if (!Matched())
2076 {
2077 return res;
2078 }
2079
2080 for (auto *child: Children())
2081 {
2082 if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None)
2083 {
2084 auto childFlags = child->GetAllFlags();
2085 res.insert(res.end(), childFlags.begin(), childFlags.end());
2086 }
2087 }
2088
2089 if (selectedCommand != nullptr)
2090 {
2091 auto childFlags = selectedCommand->GetAllFlags();
2092 res.insert(res.end(), childFlags.begin(), childFlags.end());
2093 }
2094
2095 if (subparser != nullptr)
2096 {
2097 auto childFlags = subparser->GetAllFlags();
2098 res.insert(res.end(), childFlags.begin(), childFlags.end());
2099 }
2100
2101 return res;
2102 }
2103
2105 {
2106 if (selectedCommand != nullptr)
2107 {
2108 if (auto *res = selectedCommand->GetNextPositional())
2109 {
2110 return res;
2111 }
2112
2113 for (auto *child: Children())
2114 {
2115 if ((child->GetOptions() & Options::Global) != Options::None)
2116 {
2117 if (auto *res = child->GetNextPositional())
2118 {
2119 return res;
2120 }
2121 }
2122 }
2123
2124 return nullptr;
2125 }
2126
2127 if (subparser != nullptr)
2128 {
2129 return subparser->GetNextPositional();
2130 }
2131
2132 return Matched() ? Group::GetNextPositional() : nullptr;
2133 }
2134
2135 virtual bool HasFlag() const override
2136 {
2137 return subparserHasFlag || Group::HasFlag();
2138 }
2139
2140 virtual bool HasPositional() const override
2141 {
2142 return subparserHasPositional || Group::HasPositional();
2143 }
2144
2145 virtual bool HasCommand() const override
2146 {
2147 return true;
2148 }
2149
2150 std::vector<std::string> GetCommandProgramLine(const HelpParams &params) const
2151 {
2152 UpdateSubparserHelp(params);
2153
2154 std::vector<std::string> res;
2155
2156 if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags)
2157 {
2158 res.push_back(params.proglineOptions);
2159 }
2160
2161 auto group_res = Group::GetProgramLine(params);
2162 std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res));
2163
2164 res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end());
2165
2166 if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand))
2167 {
2168 res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]");
2169 }
2170
2171 if (!Name().empty())
2172 {
2173 res.insert(res.begin(), Name());
2174 }
2175
2176 if (!ProglinePostfix().empty())
2177 {
2178 std::string line;
2179 for (auto c : ProglinePostfix())
2180 {
2181 if (std::isspace(static_cast<unsigned char>(c)))
2182 {
2183 if (!line.empty())
2184 {
2185 res.push_back(line);
2186 line.clear();
2187 }
2188
2189 if (c == '\n')
2190 {
2191 res.push_back("\n");
2192 }
2193 }
2194 else
2195 {
2196 line += c;
2197 }
2198 }
2199
2200 if (!line.empty())
2201 {
2202 res.push_back(line);
2203 }
2204 }
2205
2206 return res;
2207 }
2208
2209 virtual std::vector<std::string> GetProgramLine(const HelpParams &params) const override
2210 {
2211 if (!Matched())
2212 {
2213 return {};
2214 }
2215
2216 return GetCommandProgramLine(params);
2217 }
2218
2219 virtual std::vector<Command*> GetCommands() override
2220 {
2221 if (selectedCommand != nullptr)
2222 {
2223 return selectedCommand->GetCommands();
2224 }
2225
2226 if (Matched())
2227 {
2228 return Group::GetCommands();
2229 }
2230
2231 return { this };
2232 }
2233
2234 virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const override
2235 {
2236 std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
2237 unsigned addindent = 0;
2238
2239 UpdateSubparserHelp(params);
2240
2241 if (!Matched())
2242 {
2243 if (params.showCommandFullHelp)
2244 {
2245 std::ostringstream s;
2246 bool empty = true;
2247 for (const auto &progline: GetCommandProgramLine(params))
2248 {
2249 if (!empty)
2250 {
2251 s << ' ';
2252 }
2253 else
2254 {
2255 empty = false;
2256 }
2257
2258 s << progline;
2259 }
2260
2261 descriptions.emplace_back(s.str(), "", indent);
2262 }
2263 else
2264 {
2265 descriptions.emplace_back(Name(), help, indent);
2266 }
2267
2268 if (!params.showCommandChildren && !params.showCommandFullHelp)
2269 {
2270 return descriptions;
2271 }
2272
2273 addindent = 1;
2274 }
2275
2276 if (params.showCommandFullHelp && !Matched())
2277 {
2278 descriptions.emplace_back("", "", indent + addindent);
2279 descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent);
2280 descriptions.emplace_back("", "", indent + addindent);
2281 }
2282
2283 for (Base *child: Children())
2284 {
2285 if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None)
2286 {
2287 continue;
2288 }
2289
2290 auto groupDescriptions = child->GetDescription(params, indent + addindent);
2291 descriptions.insert(
2292 std::end(descriptions),
2293 std::make_move_iterator(std::begin(groupDescriptions)),
2294 std::make_move_iterator(std::end(groupDescriptions)));
2295 }
2296
2297 for (auto childDescription: subparserDescription)
2298 {
2299 std::get<2>(childDescription) += indent + addindent;
2300 descriptions.push_back(std::move(childDescription));
2301 }
2302
2303 if (params.showCommandFullHelp && !Matched())
2304 {
2305 descriptions.emplace_back("", "", indent + addindent);
2306 if (!Epilog().empty())
2307 {
2308 descriptions.emplace_back(Epilog(), "", indent + addindent);
2309 descriptions.emplace_back("", "", indent + addindent);
2310 }
2311 }
2312
2313 return descriptions;
2314 }
2315
2316 virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override
2317 {
2318 if (!Matched())
2319 {
2320 return;
2321 }
2322
2323 auto onValidationError = [&]
2324 {
2325 std::ostringstream problem;
2326 problem << "Group validation failed somewhere!";
2327#ifdef ARGS_NOEXCEPT
2328 error = Error::Validation;
2329 errorMsg = problem.str();
2330#else
2331 throw ValidationError(problem.str());
2332#endif
2333 };
2334
2335 for (Base *child: Children())
2336 {
2337 if (child->IsGroup() && !child->Matched())
2338 {
2339 onValidationError();
2340 }
2341
2342 child->Validate(shortprefix, longprefix);
2343 }
2344
2345 if (subparser != nullptr)
2346 {
2347 subparser->Validate(shortprefix, longprefix);
2348 if (!subparser->Matched())
2349 {
2350 onValidationError();
2351 }
2352 }
2353
2354 if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand))
2355 {
2356 std::ostringstream problem;
2357 problem << "Command is required";
2358#ifdef ARGS_NOEXCEPT
2359 error = Error::Validation;
2360 errorMsg = problem.str();
2361#else
2362 throw ValidationError(problem.str());
2363#endif
2364 }
2365 }
2366
2367 virtual void Reset() noexcept override
2368 {
2369 Group::Reset();
2370 selectedCommand = nullptr;
2371 subparserProgramLine.clear();
2372 subparserDescription.clear();
2373 subparserHasFlag = false;
2374 subparserHasPositional = false;
2375 subparserHasCommand = false;
2376#ifdef ARGS_NOEXCEPT
2377 subparserError = Error::None;
2378#endif
2379 }
2380
2381#ifdef ARGS_NOEXCEPT
2383 virtual Error GetError() const override
2384 {
2385 if (!Matched())
2386 {
2387 return Error::None;
2388 }
2389
2390 if (error != Error::None)
2391 {
2392 return error;
2393 }
2394
2395 if (subparserError != Error::None)
2396 {
2397 return subparserError;
2398 }
2399
2400 return Group::GetError();
2401 }
2402#endif
2403 };
2404
2408 {
2409 friend class Subparser;
2410
2411 private:
2412 std::string longprefix;
2413 std::string shortprefix;
2414
2415 std::string longseparator;
2416
2417 std::string terminator;
2418
2419 bool allowJoinedShortValue = true;
2420 bool allowJoinedLongValue = true;
2421 bool allowSeparateShortValue = true;
2422 bool allowSeparateLongValue = true;
2423
2424 bool readCompletion = false;
2425 CompletionFlag *completion = nullptr;
2426
2427 protected:
2428 enum class OptionType
2429 {
2430 LongFlag,
2431 ShortFlag,
2433 };
2434
2435 OptionType ParseOption(const std::string &s, bool allowEmpty = false)
2436 {
2437 if (s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length()))
2438 {
2439 return OptionType::LongFlag;
2440 }
2441
2442 if (s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length()))
2443 {
2444 return OptionType::ShortFlag;
2445 }
2446
2447 return OptionType::Positional;
2448 }
2449
2450 template <typename It>
2451 bool Complete(FlagBase &flag, It it, It end)
2452 {
2453 auto nextIt = it;
2454 if (!readCompletion || (++nextIt != end))
2455 {
2456 return false;
2457 }
2458
2459 const auto &chunk = *it;
2460 for (auto &choice : flag.HelpChoices(helpParams))
2461 {
2462 AddCompletionReply(chunk, choice);
2463 }
2464
2465#ifndef ARGS_NOEXCEPT
2466 throw Completion(completion->Get());
2467#else
2468 return true;
2469#endif
2470 }
2471
2481 template <typename It>
2482 std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end,
2483 const bool allowSeparate, const bool allowJoined,
2484 const bool hasJoined, const std::string &joinedArg,
2485 const bool canDiscardJoined, std::vector<std::string> &values)
2486 {
2487 values.clear();
2488
2489 Nargs nargs = flag.NumberOfArguments();
2490
2491 if (hasJoined && !allowJoined && nargs.min != 0)
2492 {
2493 return "Flag '" + arg + "' was passed a joined argument, but these are disallowed";
2494 }
2495
2496 if (hasJoined)
2497 {
2498 if (!canDiscardJoined || nargs.max != 0)
2499 {
2500 values.push_back(joinedArg);
2501 }
2502 } else if (!allowSeparate)
2503 {
2504 if (nargs.min != 0)
2505 {
2506 return "Flag '" + arg + "' was passed a separate argument, but these are disallowed";
2507 }
2508 } else
2509 {
2510 auto valueIt = it;
2511 ++valueIt;
2512
2513 while (valueIt != end &&
2514 values.size() < nargs.max &&
2515 (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional))
2516 {
2517 if (Complete(flag, valueIt, end))
2518 {
2519 it = end;
2520 return "";
2521 }
2522
2523 values.push_back(*valueIt);
2524 ++it;
2525 ++valueIt;
2526 }
2527 }
2528
2529 if (values.size() > nargs.max)
2530 {
2531 return "Passed an argument into a non-argument flag: " + arg;
2532 } else if (values.size() < nargs.min)
2533 {
2534 if (nargs.min == 1 && nargs.max == 1)
2535 {
2536 return "Flag '" + arg + "' requires an argument but received none";
2537 } else if (nargs.min == 1)
2538 {
2539 return "Flag '" + arg + "' requires at least one argument but received none";
2540 } else if (nargs.min != nargs.max)
2541 {
2542 return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) +
2543 " arguments but received " + std::to_string(values.size());
2544 } else
2545 {
2546 return "Flag '" + arg + "' requires " + std::to_string(nargs.min) +
2547 " arguments but received " + std::to_string(values.size());
2548 }
2549 }
2550
2551 return {};
2552 }
2553
2554 template <typename It>
2555 bool ParseLong(It &it, It end)
2556 {
2557 const auto &chunk = *it;
2558 const auto argchunk = chunk.substr(longprefix.size());
2559 // Try to separate it, in case of a separator:
2560 const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator);
2561 // If the separator is in the argument, separate it.
2562 const auto arg = (separator != argchunk.npos ?
2563 std::string(argchunk, 0, separator)
2564 : argchunk);
2565 const auto joined = (separator != argchunk.npos ?
2566 argchunk.substr(separator + longseparator.size())
2567 : std::string());
2568
2569 if (auto flag = Match(arg))
2570 {
2571#ifdef ARGS_NOEXCEPT
2572 // Match() may set the flag's error (e.g. Error::Extra when
2573 // Options::Single is violated). In non-noexcept mode that
2574 // path throws and parsing stops before the value is read;
2575 // in noexcept mode we must mirror that and skip the value
2576 // parsing so the previously-stored value is preserved.
2577 if (flag->GetError() != Error::None)
2578 {
2579 return false;
2580 }
2581#endif
2582 std::vector<std::string> values;
2583 const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue,
2584 separator != argchunk.npos, joined, false, values);
2585 if (!errorMessage.empty())
2586 {
2587#ifndef ARGS_NOEXCEPT
2588 throw ParseError(errorMessage);
2589#else
2590 error = Error::Parse;
2591 errorMsg = errorMessage;
2592 return false;
2593#endif
2594 }
2595
2596 if (!readCompletion)
2597 {
2598 flag->ParseValue(values);
2599#ifdef ARGS_NOEXCEPT
2600 // Non-noexcept ParseValue paths throw on Help, reader
2601 // failure, or Map miss, which halts parsing. Mirror
2602 // that here so a later parser-level error (e.g. an
2603 // unknown flag) cannot shadow the flag's error in
2604 // ArgumentParser::GetError().
2605 if (flag->GetError() != Error::None)
2606 {
2607 return false;
2608 }
2609#endif
2610 }
2611
2612 if (flag->KickOut())
2613 {
2614 ++it;
2615 return false;
2616 }
2617 } else
2618 {
2619 const std::string errorMessage("Flag could not be matched: " + arg);
2620#ifndef ARGS_NOEXCEPT
2621 throw ParseError(errorMessage);
2622#else
2623 error = Error::Parse;
2624 errorMsg = errorMessage;
2625 return false;
2626#endif
2627 }
2628
2629 return true;
2630 }
2631
2632 template <typename It>
2633 bool ParseShort(It &it, It end)
2634 {
2635 const auto &chunk = *it;
2636 const auto argchunk = chunk.substr(shortprefix.size());
2637 for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit)
2638 {
2639 const auto arg = *argit;
2640
2641 if (auto flag = Match(arg))
2642 {
2643#ifdef ARGS_NOEXCEPT
2644 // See ParseLong: if Match recorded an error
2645 // (e.g. Options::Single violation), bail before the
2646 // value is parsed so the prior value is preserved.
2647 if (flag->GetError() != Error::None)
2648 {
2649 return false;
2650 }
2651#endif
2652 const std::string value(argit + 1, std::end(argchunk));
2653 std::vector<std::string> values;
2654 const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end,
2655 allowSeparateShortValue, allowJoinedShortValue,
2656 !value.empty(), value, !value.empty(), values);
2657
2658 if (!errorMessage.empty())
2659 {
2660#ifndef ARGS_NOEXCEPT
2661 throw ParseError(errorMessage);
2662#else
2663 error = Error::Parse;
2664 errorMsg = errorMessage;
2665 return false;
2666#endif
2667 }
2668
2669 if (!readCompletion)
2670 {
2671 flag->ParseValue(values);
2672#ifdef ARGS_NOEXCEPT
2673 // See ParseLong: ensure a flag-level error from
2674 // ParseValue (Help, Parse, Map) halts parsing so
2675 // it cannot be shadowed by a later parser error.
2676 if (flag->GetError() != Error::None)
2677 {
2678 return false;
2679 }
2680#endif
2681 }
2682
2683 if (flag->KickOut())
2684 {
2685 ++it;
2686 return false;
2687 }
2688
2689 if (!values.empty())
2690 {
2691 break;
2692 }
2693 } else
2694 {
2695 const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'");
2696#ifndef ARGS_NOEXCEPT
2697 throw ParseError(errorMessage);
2698#else
2699 error = Error::Parse;
2700 errorMsg = errorMessage;
2701 return false;
2702#endif
2703 }
2704 }
2705
2706 return true;
2707 }
2708
2709 bool AddCompletionReply(const std::string &cur, const std::string &choice)
2710 {
2711 if (cur.empty() || choice.find(cur) == 0)
2712 {
2713 if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos)
2714 {
2715 completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size()));
2716 } else
2717 {
2718 completion->reply.push_back(choice);
2719 }
2720 return true;
2721 }
2722
2723 return false;
2724 }
2725
2726 template <typename It>
2727 bool Complete(It it, It end)
2728 {
2729 auto nextIt = it;
2730 if (!readCompletion || (++nextIt != end))
2731 {
2732 return false;
2733 }
2734
2735 const auto &chunk = *it;
2736 auto pos = GetNextPositional();
2737 std::vector<Command *> commands = GetCommands();
2738 const auto optionType = ParseOption(chunk, true);
2739
2740 if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional))
2741 {
2742 for (auto &cmd : commands)
2743 {
2744 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2745 {
2746 AddCompletionReply(chunk, cmd->Name());
2747 }
2748 }
2749 } else
2750 {
2751 bool hasPositionalCompletion = true;
2752
2753 if (!commands.empty())
2754 {
2755 for (auto &cmd : commands)
2756 {
2757 if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2758 {
2759 AddCompletionReply(chunk, cmd->Name());
2760 }
2761 }
2762 } else if (pos)
2763 {
2764 if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None)
2765 {
2766 auto choices = pos->HelpChoices(helpParams);
2767 hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional;
2768 for (auto &choice : choices)
2769 {
2770 AddCompletionReply(chunk, choice);
2771 }
2772 }
2773 }
2774
2775 if (hasPositionalCompletion)
2776 {
2777 auto flags = GetAllFlags();
2778 for (auto flag : flags)
2779 {
2780 if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None)
2781 {
2782 continue;
2783 }
2784
2785 auto &matcher = flag->GetMatcher();
2786 if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix)))
2787 {
2788 for (auto &flagName : matcher.GetFlagStrings())
2789 {
2790 if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix)))
2791 {
2792 break;
2793 }
2794 }
2795 }
2796 }
2797
2798 if (optionType == OptionType::LongFlag && allowJoinedLongValue)
2799 {
2800 const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator);
2801 if (separator != chunk.npos)
2802 {
2803 std::string arg(chunk, 0, separator);
2804 if (auto flag = this->Match(arg.substr(longprefix.size())))
2805 {
2806 for (auto &choice : flag->HelpChoices(helpParams))
2807 {
2808 AddCompletionReply(chunk, arg + longseparator + choice);
2809 }
2810 }
2811 }
2812 } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue)
2813 {
2814 if (chunk.size() > shortprefix.size() + 1)
2815 {
2816 auto arg = chunk.at(shortprefix.size());
2817 //TODO: support -abcVALUE where a and b take no value
2818 if (auto flag = this->Match(arg))
2819 {
2820 for (auto &choice : flag->HelpChoices(helpParams))
2821 {
2822 AddCompletionReply(chunk, shortprefix + arg + choice);
2823 }
2824 }
2825 }
2826 }
2827 }
2828 }
2829
2830#ifndef ARGS_NOEXCEPT
2831 throw Completion(completion->Get());
2832#else
2833 return true;
2834#endif
2835 }
2836
2837 template <typename It>
2838 It Parse(It begin, It end)
2839 {
2840 bool terminated = false;
2841 std::vector<Command *> commands = GetCommands();
2842
2843 // Check all arg chunks
2844 for (auto it = begin; it != end; ++it)
2845 {
2846 if (Complete(it, end))
2847 {
2848 return end;
2849 }
2850
2851 const auto &chunk = *it;
2852
2853 if (!terminated && chunk == terminator)
2854 {
2855 terminated = true;
2856 } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag)
2857 {
2858 if (!ParseLong(it, end))
2859 {
2860 return it;
2861 }
2862 } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag)
2863 {
2864 if (!ParseShort(it, end))
2865 {
2866 return it;
2867 }
2868 } else if (!terminated && !commands.empty())
2869 {
2870 auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; });
2871 if (itCommand == commands.end())
2872 {
2873 const std::string errorMessage("Unknown command: " + chunk);
2874#ifndef ARGS_NOEXCEPT
2875 throw ParseError(errorMessage);
2876#else
2877 error = Error::Parse;
2878 errorMsg = errorMessage;
2879 return it;
2880#endif
2881 }
2882
2883 SelectCommand(*itCommand);
2884
2885 if (const auto &coroutine = GetCoroutine())
2886 {
2887 ++it;
2888 RaiiSubparser coro(*this, std::vector<std::string>(it, end));
2889 coroutine(coro.Parser());
2890#ifdef ARGS_NOEXCEPT
2891 error = GetError();
2892 if (error != Error::None)
2893 {
2894 return end;
2895 }
2896
2897 if (!coro.Parser().IsParsed())
2898 {
2899 error = Error::Usage;
2900 return end;
2901 }
2902#else
2903 if (!coro.Parser().IsParsed())
2904 {
2905 throw UsageError("Subparser::Parse was not called");
2906 }
2907#endif
2908
2909 break;
2910 }
2911
2912 commands = GetCommands();
2913 } else
2914 {
2915 auto pos = GetNextPositional();
2916 if (pos)
2917 {
2918 pos->ParseValue(chunk);
2919
2920 if (pos->KickOut())
2921 {
2922 return ++it;
2923 }
2924 } else
2925 {
2926 const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk);
2927#ifndef ARGS_NOEXCEPT
2928 throw ParseError(errorMessage);
2929#else
2930 error = Error::Parse;
2931 errorMsg = errorMessage;
2932 return it;
2933#endif
2934 }
2935 }
2936
2937 if (!readCompletion && completion != nullptr && completion->Matched())
2938 {
2939#ifdef ARGS_NOEXCEPT
2940 if (completion->GetError() != Error::None)
2941 {
2942 error = completion->GetError();
2943 if (errorMsg.empty())
2944 {
2945 errorMsg = completion->GetErrorMsg();
2946 }
2947 return it;
2948 }
2949
2950 error = Error::Completion;
2951#endif
2952 readCompletion = true;
2953 ++it;
2954 const auto argsLeft = static_cast<size_t>(std::distance(it, end));
2955 if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft)
2956 {
2957#ifndef ARGS_NOEXCEPT
2958 throw Completion("");
2959#else
2960 return end;
2961#endif
2962 }
2963
2964 ++it;
2965 std::vector<std::string> curArgs;
2966 curArgs.reserve(completion->cword);
2967 auto curIt = it;
2968 for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt)
2969 {
2970 curArgs.push_back(*curIt);
2971 }
2972
2973 if (completion->syntax == "bash")
2974 {
2975 // bash tokenizes --flag=value as --flag=value
2976 // Security fix: Use size_t arithmetic throughout to avoid conversion issues
2977 for (size_t idx = 0; idx < curArgs.size(); )
2978 {
2979 if (idx > 0 && curArgs[idx] == "=")
2980 {
2981 size_t prev_idx = idx - 1; // Safe since we checked idx > 0
2982 curArgs[prev_idx] += "=";
2983 size_t next_idx = 0;
2984 if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size())
2985 {
2986 curArgs[prev_idx] += curArgs[next_idx];
2987 // Erase the '=' token and the following value token.
2988 size_t erase_end = 0;
2989 if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end))
2990 {
2991 curArgs.erase(curArgs.begin() + idx,
2992 curArgs.begin() + erase_end);
2993 }
2994 } else
2995 {
2996 // Safe erase of single '=' token at the end
2997 curArgs.erase(curArgs.begin() + idx);
2998 }
2999 // Do not increment idx - next element slides into current position
3000 } else
3001 {
3002 ++idx;
3003 }
3004 }
3005
3006 }
3007#ifndef ARGS_NOEXCEPT
3008 try
3009 {
3010 Parse(curArgs.begin(), curArgs.end());
3011 throw Completion("");
3012 }
3013 catch (Completion &)
3014 {
3015 throw;
3016 }
3017 catch (args::Error&)
3018 {
3019 throw Completion("");
3020 }
3021#else
3022 return Parse(curArgs.begin(), curArgs.end());
3023#endif
3024 }
3025 }
3026
3027 Validate(shortprefix, longprefix);
3028 return end;
3029 }
3030
3031 public:
3032 HelpParams helpParams;
3033
3034 ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string())
3035 {
3036 Description(description_);
3037 Epilog(epilog_);
3038 LongPrefix("--");
3039 ShortPrefix("-");
3040 LongSeparator("=");
3041 Terminator("--");
3042 SetArgumentSeparations(true, true, true, true);
3043 matched = true;
3044 }
3045
3046 void AddCompletion(CompletionFlag &completionFlag)
3047 {
3048 completion = &completionFlag;
3049 Add(completionFlag);
3050 }
3051
3054 const std::string &Prog() const
3055 { return helpParams.programName; }
3058 void Prog(const std::string &prog_)
3059 { this->helpParams.programName = prog_; }
3060
3063 const std::string &LongPrefix() const
3064 { return longprefix; }
3067 void LongPrefix(const std::string &longprefix_)
3068 {
3069 this->longprefix = longprefix_;
3070 this->helpParams.longPrefix = longprefix_;
3071 }
3072
3075 const std::string &ShortPrefix() const
3076 { return shortprefix; }
3079 void ShortPrefix(const std::string &shortprefix_)
3080 {
3081 this->shortprefix = shortprefix_;
3082 this->helpParams.shortPrefix = shortprefix_;
3083 }
3084
3087 const std::string &LongSeparator() const
3088 { return longseparator; }
3091 void LongSeparator(const std::string &longseparator_)
3092 {
3093 if (longseparator_.empty())
3094 {
3095 const std::string errorMessage("longseparator can not be set to empty");
3096#ifdef ARGS_NOEXCEPT
3097 error = Error::Usage;
3098 errorMsg = errorMessage;
3099#else
3100 throw UsageError(errorMessage);
3101#endif
3102 } else
3103 {
3104 this->longseparator = longseparator_;
3105 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3106 }
3107 }
3108
3111 const std::string &Terminator() const
3112 { return terminator; }
3115 void Terminator(const std::string &terminator_)
3116 { this->terminator = terminator_; }
3117
3123 bool &allowJoinedShortValue_,
3124 bool &allowJoinedLongValue_,
3125 bool &allowSeparateShortValue_,
3126 bool &allowSeparateLongValue_) const
3127 {
3128 allowJoinedShortValue_ = this->allowJoinedShortValue;
3129 allowJoinedLongValue_ = this->allowJoinedLongValue;
3130 allowSeparateShortValue_ = this->allowSeparateShortValue;
3131 allowSeparateLongValue_ = this->allowSeparateLongValue;
3132 }
3133
3142 const bool allowJoinedShortValue_,
3143 const bool allowJoinedLongValue_,
3144 const bool allowSeparateShortValue_,
3145 const bool allowSeparateLongValue_)
3146 {
3147 this->allowJoinedShortValue = allowJoinedShortValue_;
3148 this->allowJoinedLongValue = allowJoinedLongValue_;
3149 this->allowSeparateShortValue = allowSeparateShortValue_;
3150 this->allowSeparateLongValue = allowSeparateLongValue_;
3151
3152 this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " ";
3153 this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " ";
3154 }
3155
3158 void Help(std::ostream &help_) const
3159 {
3160 auto &command = SelectedCommand();
3161 const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description();
3162 const auto desc_indent = helpParams.descriptionindent;
3163 const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0;
3164 const auto description_text = Wrap(commandDescription, effective_desc_width);
3165 const auto epilog_text = Wrap(command.Epilog(), effective_desc_width);
3166
3167 const bool hasoptions = command.HasFlag();
3168 const bool hasarguments = command.HasPositional();
3169
3170 std::vector<std::string> prognameline;
3171 prognameline.push_back(helpParams.usageString);
3172 prognameline.push_back(Prog());
3173 auto commandProgLine = command.GetProgramLine(helpParams);
3174 prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
3175
3176 const auto prog_sum = helpParams.progindent + helpParams.progtailindent;
3177 const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0;
3178 const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0;
3179 const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
3180 effective_prog_width,
3181 effective_prog_first);
3182 auto progit = std::begin(proglines);
3183 if (progit != std::end(proglines))
3184 {
3185 help_ << std::string(helpParams.progindent, ' ') << *progit << '\n';
3186 ++progit;
3187 }
3188 for (; progit != std::end(proglines); ++progit)
3189 {
3190 help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n';
3191 }
3192
3193 help_ << '\n';
3194
3195 if (!description_text.empty())
3196 {
3197 for (const auto &line: description_text)
3198 {
3199 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3200 }
3201 help_ << "\n";
3202 }
3203
3204 bool lastDescriptionIsNewline = false;
3205
3206 if (!helpParams.optionsString.empty())
3207 {
3208 help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n";
3209 }
3210
3211 for (const auto &desc: command.GetDescription(helpParams, 0))
3212 {
3213 lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty();
3214 const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent;
3215 const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter;
3216 const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0;
3217 const auto flags = Wrap(std::get<0>(desc), effective_flag_width);
3218 const auto info_sum = helpParams.helpindent + groupindent;
3219 const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0;
3220 const auto info = Wrap(std::get<1>(desc), effective_info_width);
3221
3222 std::string::size_type flagssize = 0;
3223 for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit)
3224 {
3225 if (flagsit != std::begin(flags))
3226 {
3227 help_ << '\n';
3228 }
3229 help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit;
3230 flagssize = Glyphs(*flagsit);
3231 }
3232
3233 auto infoit = std::begin(info);
3234 // groupindent is on both sides of this inequality, and therefore can be removed
3235 if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription)
3236 {
3237 help_ << '\n';
3238 } else
3239 {
3240 // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here
3241 const auto indent_sum = helpParams.flagindent + flagssize;
3242 const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0;
3243 help_ << std::string(effective_space, ' ') << *infoit << '\n';
3244 ++infoit;
3245 }
3246 for (; infoit != std::end(info); ++infoit)
3247 {
3248 help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n';
3249 }
3250 }
3251 if (hasoptions && hasarguments && helpParams.showTerminator)
3252 {
3253 lastDescriptionIsNewline = false;
3254 const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0;
3255 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))
3256 {
3257 help_ << std::string(helpParams.flagindent, ' ') << item << '\n';
3258 }
3259 }
3260
3261 if (!lastDescriptionIsNewline)
3262 {
3263 help_ << "\n";
3264 }
3265
3266 for (const auto &line: epilog_text)
3267 {
3268 help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n";
3269 }
3270 }
3271
3276 std::string Help() const
3277 {
3278 std::ostringstream help_;
3279 Help(help_);
3280 return help_.str();
3281 }
3282
3283 virtual void Reset() noexcept override
3284 {
3285 Command::Reset();
3286 matched = true;
3287 readCompletion = false;
3288 }
3289
3296 template <typename It>
3297 It ParseArgs(It begin, It end)
3298 {
3299 // Reset all Matched statuses and errors
3300 Reset();
3301#ifdef ARGS_NOEXCEPT
3302 error = GetError();
3303 if (error != Error::None)
3304 {
3305 return end;
3306 }
3307#endif
3308 return Parse(begin, end);
3309 }
3310
3316 template <typename T>
3317 auto ParseArgs(const T &args) -> decltype(std::begin(args))
3318 {
3319 return ParseArgs(std::begin(args), std::end(args));
3320 }
3321
3328 bool ParseCLI(const int argc, const char * const * argv)
3329 {
3330 if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty())
3331 {
3332 Prog(argv[0]);
3333 }
3334
3335 std::vector<std::string> args;
3336 if (argc > 1 && argv != nullptr)
3337 {
3338 args.assign(argv + 1, argv + argc);
3339 }
3340
3341 return ParseArgs(args) == std::end(args);
3342 }
3343
3344 template <typename T>
3345 bool ParseCLI(const T &args)
3346 {
3347 return ParseArgs(args) == std::end(args);
3348 }
3349 };
3350
3351 inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_)
3352 : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser)
3353 {
3354 command.subparser = &parser;
3355 }
3356
3357 inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams &params_): command(command_), parser(command, params_), oldSubparser(command.subparser)
3358 {
3359 command.subparser = &parser;
3360 }
3361
3362 inline void Subparser::Parse()
3363 {
3364 isParsed = true;
3365 Reset();
3366 command.subparserDescription = GetDescription(helpParams, 0);
3367 command.subparserHasFlag = HasFlag();
3368 command.subparserHasPositional = HasPositional();
3369 command.subparserHasCommand = HasCommand();
3370 command.subparserProgramLine = GetProgramLine(helpParams);
3371 if (parser == nullptr)
3372 {
3373#ifndef ARGS_NOEXCEPT
3374 throw args::SubparserError();
3375#else
3376 error = Error::Subparser;
3377 return;
3378#endif
3379 }
3380
3381 auto it = parser->Parse(args.begin(), args.end());
3382 command.Validate(parser->ShortPrefix(), parser->LongPrefix());
3383 kicked.assign(it, args.end());
3384
3385#ifdef ARGS_NOEXCEPT
3386 command.subparserError = GetError();
3387#endif
3388 }
3389
3390 inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser)
3391 {
3392 parser.Help(os);
3393 return os;
3394 }
3395
3398 class Flag : public FlagBase
3399 {
3400 public:
3401 Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_)
3402 {
3403 group_.Add(*this);
3404 }
3405
3406 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)
3407 {
3408 }
3409
3410 virtual ~Flag() {}
3411
3414 bool Get() const
3415 {
3416 return Matched();
3417 }
3418
3419 virtual Nargs NumberOfArguments() const noexcept override
3420 {
3421 return 0;
3422 }
3423
3424 virtual void ParseValue(const std::vector<std::string>&) override
3425 {
3426 }
3427 };
3428
3433 class HelpFlag : public Flag
3434 {
3435 public:
3436 HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {}
3437
3438 virtual ~HelpFlag() {}
3439
3440 virtual void ParseValue(const std::vector<std::string> &)
3441 {
3442#ifdef ARGS_NOEXCEPT
3443 error = Error::Help;
3444 errorMsg = Name();
3445#else
3446 throw Help(Name());
3447#endif
3448 }
3449
3452 bool Get() const noexcept
3453 {
3454 return Matched();
3455 }
3456 };
3457
3460 class CounterFlag : public Flag
3461 {
3462 private:
3463 const int startcount;
3464 int count;
3465
3466 public:
3467 CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}):
3468 Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {}
3469
3470 virtual ~CounterFlag() {}
3471
3472 virtual FlagBase *Match(const EitherFlag &arg) override
3473 {
3474 auto me = FlagBase::Match(arg);
3475 if (me)
3476 {
3477#ifdef ARGS_NOEXCEPT
3478 // Suppress increment when FlagBase::Match recorded an
3479 // error on this same call (e.g. Options::Single violated).
3480 // In non-noexcept mode that path would have thrown before
3481 // reaching here and the count would not have advanced.
3482 if (GetError() != Error::None)
3483 {
3484 return me;
3485 }
3486#endif
3487 ++count;
3488 }
3489 return me;
3490 }
3491
3494 int &Get() noexcept
3495 {
3496 return count;
3497 }
3498
3499 int &operator *() noexcept {
3500 return count;
3501 }
3502
3503 const int &operator *() const noexcept {
3504 return count;
3505 }
3506
3507 virtual void Reset() noexcept override
3508 {
3509 FlagBase::Reset();
3510 count = startcount;
3511 }
3512 };
3513
3516 class ActionFlag : public FlagBase
3517 {
3518 private:
3519 std::function<void(const std::vector<std::string> &)> action;
3520 Nargs nargs;
3521
3522 public:
3523 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_ = {}):
3524 FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_)
3525 {
3526 group_.Add(*this);
3527 }
3528
3529 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}):
3530 FlagBase(name_, help_, std::move(matcher_), options_), nargs(1)
3531 {
3532 group_.Add(*this);
3533 action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); };
3534 }
3535
3536 ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}):
3537 FlagBase(name_, help_, std::move(matcher_), options_), nargs(0)
3538 {
3539 group_.Add(*this);
3540 action = [action_](const std::vector<std::string> &) { return action_(); };
3541 }
3542
3543 virtual Nargs NumberOfArguments() const noexcept override
3544 { return nargs; }
3545
3546 virtual void ParseValue(const std::vector<std::string> &value) override
3547 { action(value); }
3548 };
3549
3557 {
3558 private:
3559 template <typename T>
3560 static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type
3561 HasUnsignedNegativeSign(const std::string &value)
3562 {
3563 const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c)
3564 {
3565 return std::isspace(static_cast<unsigned char>(c)) != 0;
3566 });
3567
3568 return firstNonSpace != value.end() && *firstNonSpace == '-';
3569 }
3570
3571 template <typename T>
3572 static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type
3573 HasUnsignedNegativeSign(const std::string &)
3574 {
3575 return false;
3576 }
3577
3578 public:
3579 template <typename T>
3580 typename std::enable_if<
3581 std::is_integral<T>::value &&
3582 !std::is_same<T, bool>::value &&
3583 !std::is_same<T, char>::value &&
3584 !std::is_same<T, signed char>::value &&
3585 !std::is_same<T, unsigned char>::value,
3586 bool>::type
3587 ParseNumericValue(const std::string &value, T &destination)
3588 {
3589 if (HasUnsignedNegativeSign<T>(value))
3590 {
3591 return false;
3592 }
3593
3594 const char *begin = value.c_str();
3595
3596 // Security enhancement: Use C++17 std::from_chars when available (thread-safe, no errno)
3597 // Fallback to strtoll/strtoull for C++11/14 compatibility with careful errno handling
3598#if __cplusplus >= 201703L
3599 const char *end_pos = value.c_str() + value.length();
3600 T parsed = 0;
3601 auto result = std::from_chars(begin, end_pos, parsed);
3602
3603 // Find end of valid parse
3604 end_pos = result.ptr;
3605
3606 // Skip trailing whitespace following std::from_chars parse
3607 while (end_pos != value.c_str() + value.length() &&
3608 std::isspace(static_cast<unsigned char>(*end_pos)))
3609 {
3610 ++end_pos;
3611 }
3612
3613 if (end_pos != value.c_str() + value.length() ||
3614 result.ec == std::errc::invalid_argument ||
3615 result.ec == std::errc::result_out_of_range)
3616 {
3617 return false;
3618 }
3619
3620 destination = parsed;
3621 return true;
3622#else
3623 // C++11/14 fallback: Use strtoll/strtoull
3624 // SECURITY WARNING: errno is global and not thread-safe. This fallback is provided
3625 // for C++11/14 compatibility but multithreaded applications should compile with
3626 // C++17 and use std::from_chars for thread-safe parsing.
3627
3628 // Save errno to detect if strtoull/strtoll sets it
3629 const int saved_errno = errno;
3630 errno = 0;
3631
3632 char *end = nullptr;
3633
3634 if (std::is_unsigned<T>::value)
3635 {
3636 const unsigned long long parsed = std::strtoull(begin, &end, 0);
3637 while (end != nullptr && *end != '\0' && std::isspace(static_cast<unsigned char>(*end)))
3638 {
3639 ++end;
3640 }
3641 if (end == begin || *end != '\0' || errno == ERANGE || parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
3642 {
3643 errno = saved_errno; // Restore errno on error
3644 return false;
3645 }
3646
3647 destination = static_cast<T>(parsed);
3648 }
3649 else
3650 {
3651 const long long parsed = std::strtoll(begin, &end, 0);
3652 while (end != nullptr && *end != '\0' && std::isspace(static_cast<unsigned char>(*end)))
3653 {
3654 ++end;
3655 }
3656 if (end == begin || *end != '\0' || errno == ERANGE ||
3657 parsed < static_cast<long long>(std::numeric_limits<T>::min()) ||
3658 parsed > static_cast<long long>(std::numeric_limits<T>::max()))
3659 {
3660 errno = saved_errno; // Restore errno on error
3661 return false;
3662 }
3663
3664 destination = static_cast<T>(parsed);
3665 }
3666
3667 errno = saved_errno; // Restore errno on success
3668 return true;
3669#endif
3670 }
3671
3672 template <typename T>
3673 typename std::enable_if<
3674 !std::is_integral<T>::value ||
3675 std::is_same<T, bool>::value ||
3676 std::is_same<T, char>::value ||
3677 std::is_same<T, signed char>::value ||
3678 std::is_same<T, unsigned char>::value,
3679 bool>::type
3680 ParseNumericValue(const std::string &value, T &destination)
3681 {
3682 std::istringstream ss(value);
3683 ss >> destination;
3684 if (ss.fail())
3685 {
3686 return false;
3687 }
3688
3689 ss >> std::ws;
3690 return ss.peek() == std::char_traits<char>::eof();
3691 }
3692
3693 template <typename T>
3694 typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type
3695 operator ()(const std::string &name, const std::string &value, T &destination)
3696 {
3697 const bool success = ParseNumericValue(value, destination);
3698 if (!success)
3699 {
3700#ifdef ARGS_NOEXCEPT
3701 (void)name;
3702 return false;
3703#else
3704 std::ostringstream problem;
3705 problem << "Argument '" << name << "' received invalid value type '" << value << "'";
3706 throw ParseError(problem.str());
3707#endif
3708 }
3709 return true;
3710 }
3711
3712 template <typename T>
3713 typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type
3714 operator()(const std::string &, const std::string &value, T &destination)
3715 {
3716 destination = value;
3717 return true;
3718 }
3719 };
3720
3726 template <
3727 typename T,
3728 typename Reader = ValueReader>
3730 {
3731 protected:
3732 T value;
3733 T defaultValue;
3734
3735 virtual std::string GetDefaultString(const HelpParams&) const override
3736 {
3737 return detail::ToString(defaultValue);
3738 }
3739
3740 private:
3741 Reader reader;
3742
3743 public:
3744
3745 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_)
3746 {
3747 group_.Add(*this);
3748 }
3749
3750 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)
3751 {
3752 }
3753
3754 ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_)
3755 {
3756 }
3757
3758 virtual ~ValueFlag() {}
3759
3760 virtual void ParseValue(const std::vector<std::string> &values_) override
3761 {
3762 const std::string &value_ = values_.at(0);
3763
3764#ifdef ARGS_NOEXCEPT
3765 if (!reader(name, value_, this->value))
3766 {
3767 error = Error::Parse;
3768 }
3769#else
3770 reader(name, value_, this->value);
3771#endif
3772 }
3773
3774 virtual void Reset() noexcept override
3775 {
3776 ValueFlagBase::Reset();
3777 value = defaultValue;
3778 }
3779
3782 T &Get() noexcept
3783 {
3784 return value;
3785 }
3786
3789 T &operator *() noexcept
3790 {
3791 return value;
3792 }
3793
3796 const T &operator *() const noexcept
3797 {
3798 return value;
3799 }
3800
3803 T *operator ->() noexcept
3804 {
3805 return &value;
3806 }
3807
3810 const T *operator ->() const noexcept
3811 {
3812 return &value;
3813 }
3814
3817 const T &GetDefault() noexcept
3818 {
3819 return defaultValue;
3820 }
3821 };
3822
3828 template <
3829 typename T,
3830 typename Reader = ValueReader>
3831 class ImplicitValueFlag : public ValueFlag<T, Reader>
3832 {
3833 protected:
3834 T implicitValue;
3835
3836 public:
3837
3838 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {})
3839 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_)
3840 {
3841 }
3842
3843 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {})
3844 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_)
3845 {
3846 }
3847
3848 ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_)
3849 : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue()
3850 {
3851 }
3852
3853 virtual ~ImplicitValueFlag() {}
3854
3855 virtual Nargs NumberOfArguments() const noexcept override
3856 {
3857 return {0, 1};
3858 }
3859
3860 virtual void ParseValue(const std::vector<std::string> &value_) override
3861 {
3862 if (value_.empty())
3863 {
3864 this->value = implicitValue;
3865 } else
3866 {
3868 }
3869 }
3870 };
3871
3878 template <
3879 typename T,
3880 template <typename...> class List = detail::vector,
3881 typename Reader = ValueReader>
3883 {
3884 protected:
3885
3886 List<T> values;
3887 const List<T> defaultValues;
3888 Nargs nargs;
3889 Reader reader;
3890
3891 public:
3892
3893 typedef List<T> Container;
3894 typedef T value_type;
3895 typedef typename Container::allocator_type allocator_type;
3896 typedef typename Container::pointer pointer;
3897 typedef typename Container::const_pointer const_pointer;
3898 typedef T& reference;
3899 typedef const T& const_reference;
3900 typedef typename Container::size_type size_type;
3901 typedef typename Container::difference_type difference_type;
3902 typedef typename Container::iterator iterator;
3903 typedef typename Container::const_iterator const_iterator;
3904 typedef std::reverse_iterator<iterator> reverse_iterator;
3905 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
3906
3907 NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {})
3908 : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_)
3909 {
3910 group_.Add(*this);
3911 }
3912
3913 virtual ~NargsValueFlag() {}
3914
3915 virtual Nargs NumberOfArguments() const noexcept override
3916 {
3917 return nargs;
3918 }
3919
3920 virtual void ParseValue(const std::vector<std::string> &values_) override
3921 {
3922 values.clear();
3923
3924 for (const std::string &value : values_)
3925 {
3926 T v {};
3927#ifdef ARGS_NOEXCEPT
3928 if (!reader(name, value, v))
3929 {
3930 error = Error::Parse;
3931 return;
3932 }
3933#else
3934 reader(name, value, v);
3935#endif
3936 values.insert(std::end(values), v);
3937 }
3938 }
3939
3940 List<T> &Get() noexcept
3941 {
3942 return values;
3943 }
3944
3947 List<T> &operator *() noexcept
3948 {
3949 return values;
3950 }
3951
3954 const List<T> &operator *() const noexcept
3955 {
3956 return values;
3957 }
3958
3961 List<T> *operator ->() noexcept
3962 {
3963 return &values;
3964 }
3965
3968 const List<T> *operator ->() const noexcept
3969 {
3970 return &values;
3971 }
3972
3973 iterator begin() noexcept
3974 {
3975 return values.begin();
3976 }
3977
3978 const_iterator begin() const noexcept
3979 {
3980 return values.begin();
3981 }
3982
3983 const_iterator cbegin() const noexcept
3984 {
3985 return values.cbegin();
3986 }
3987
3988 iterator end() noexcept
3989 {
3990 return values.end();
3991 }
3992
3993 const_iterator end() const noexcept
3994 {
3995 return values.end();
3996 }
3997
3998 const_iterator cend() const noexcept
3999 {
4000 return values.cend();
4001 }
4002
4003 virtual void Reset() noexcept override
4004 {
4005 FlagBase::Reset();
4006 values = defaultValues;
4007 }
4008
4009 virtual FlagBase *Match(const EitherFlag &arg) override
4010 {
4011 const bool wasMatched = Matched();
4012 auto me = FlagBase::Match(arg);
4013 if (me && !wasMatched)
4014 {
4015 values.clear();
4016 }
4017 return me;
4018 }
4019 };
4020
4027 template <
4028 typename T,
4029 template <typename...> class List = detail::vector,
4030 typename Reader = ValueReader>
4032 {
4033 private:
4034 using Container = List<T>;
4035 Container values;
4036 const Container defaultValues;
4037 Reader reader;
4038
4039 public:
4040
4041 typedef T value_type;
4042 typedef typename Container::allocator_type allocator_type;
4043 typedef typename Container::pointer pointer;
4044 typedef typename Container::const_pointer const_pointer;
4045 typedef T& reference;
4046 typedef const T& const_reference;
4047 typedef typename Container::size_type size_type;
4048 typedef typename Container::difference_type difference_type;
4049 typedef typename Container::iterator iterator;
4050 typedef typename Container::const_iterator const_iterator;
4051 typedef std::reverse_iterator<iterator> reverse_iterator;
4052 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4053
4054 ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}):
4055 ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_)
4056 {
4057 group_.Add(*this);
4058 }
4059
4060 virtual ~ValueFlagList() {}
4061
4062 virtual void ParseValue(const std::vector<std::string> &values_) override
4063 {
4064 const std::string &value_ = values_.at(0);
4065
4066 T v{};
4067#ifdef ARGS_NOEXCEPT
4068 if (!reader(name, value_, v))
4069 {
4070 error = Error::Parse;
4071 return;
4072 }
4073#else
4074 reader(name, value_, v);
4075#endif
4076 values.insert(std::end(values), v);
4077 }
4078
4081 Container &Get() noexcept
4082 {
4083 return values;
4084 }
4085
4088 Container &operator *() noexcept
4089 {
4090 return values;
4091 }
4092
4095 const Container &operator *() const noexcept
4096 {
4097 return values;
4098 }
4099
4102 Container *operator ->() noexcept
4103 {
4104 return &values;
4105 }
4106
4109 const Container *operator ->() const noexcept
4110 {
4111 return &values;
4112 }
4113
4114 virtual std::string Name() const override
4115 {
4116 return name + std::string("...");
4117 }
4118
4119 virtual void Reset() noexcept override
4120 {
4121 ValueFlagBase::Reset();
4122 values = defaultValues;
4123 }
4124
4125 virtual FlagBase *Match(const EitherFlag &arg) override
4126 {
4127 const bool wasMatched = Matched();
4128 auto me = FlagBase::Match(arg);
4129 if (me && !wasMatched)
4130 {
4131 values.clear();
4132 }
4133 return me;
4134 }
4135
4136 iterator begin() noexcept
4137 {
4138 return values.begin();
4139 }
4140
4141 const_iterator begin() const noexcept
4142 {
4143 return values.begin();
4144 }
4145
4146 const_iterator cbegin() const noexcept
4147 {
4148 return values.cbegin();
4149 }
4150
4151 iterator end() noexcept
4152 {
4153 return values.end();
4154 }
4155
4156 const_iterator end() const noexcept
4157 {
4158 return values.end();
4159 }
4160
4161 const_iterator cend() const noexcept
4162 {
4163 return values.cend();
4164 }
4165 };
4166
4174 template <
4175 typename K,
4176 typename T,
4177 typename Reader = ValueReader,
4178 template <typename...> class Map = detail::unordered_map>
4179 class MapFlag : public ValueFlagBase
4180 {
4181 private:
4182 const Map<K, T> map;
4183 T value;
4184 const T defaultValue;
4185 Reader reader;
4186
4187 protected:
4188 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4189 {
4190 return detail::MapKeysToStrings(map);
4191 }
4192
4193 public:
4194
4195 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_)
4196 {
4197 group_.Add(*this);
4198 }
4199
4200 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)
4201 {
4202 }
4203
4204 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_)
4205 {
4206 }
4207
4208 virtual ~MapFlag() {}
4209
4210 virtual void ParseValue(const std::vector<std::string> &values_) override
4211 {
4212 const std::string &value_ = values_.at(0);
4213
4214 K key{};
4215#ifdef ARGS_NOEXCEPT
4216 if (!reader(name, value_, key))
4217 {
4218 error = Error::Parse;
4219 return;
4220 }
4221#else
4222 reader(name, value_, key);
4223#endif
4224 auto it = map.find(key);
4225 if (it == std::end(map))
4226 {
4227 std::ostringstream problem;
4228 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4229#ifdef ARGS_NOEXCEPT
4230 error = Error::Map;
4231 errorMsg = problem.str();
4232#else
4233 throw MapError(problem.str());
4234#endif
4235 } else
4236 {
4237 this->value = it->second;
4238 }
4239 }
4240
4243 T &Get() noexcept
4244 {
4245 return value;
4246 }
4247
4250 T &operator *() noexcept
4251 {
4252 return value;
4253 }
4254
4257 const T &operator *() const noexcept
4258 {
4259 return value;
4260 }
4261
4264 T *operator ->() noexcept
4265 {
4266 return &value;
4267 }
4268
4271 const T *operator ->() const noexcept
4272 {
4273 return &value;
4274 }
4275
4276 virtual void Reset() noexcept override
4277 {
4278 ValueFlagBase::Reset();
4279 value = defaultValue;
4280 }
4281 };
4282
4291 template <
4292 typename K,
4293 typename T,
4294 template <typename...> class List = detail::vector,
4295 typename Reader = ValueReader,
4296 template <typename...> class Map = detail::unordered_map>
4298 {
4299 private:
4300 using Container = List<T>;
4301 const Map<K, T> map;
4302 Container values;
4303 const Container defaultValues;
4304 Reader reader;
4305
4306 protected:
4307 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4308 {
4309 return detail::MapKeysToStrings(map);
4310 }
4311
4312 public:
4313 typedef T value_type;
4314 typedef typename Container::allocator_type allocator_type;
4315 typedef typename Container::pointer pointer;
4316 typedef typename Container::const_pointer const_pointer;
4317 typedef T& reference;
4318 typedef const T& const_reference;
4319 typedef typename Container::size_type size_type;
4320 typedef typename Container::difference_type difference_type;
4321 typedef typename Container::iterator iterator;
4322 typedef typename Container::const_iterator const_iterator;
4323 typedef std::reverse_iterator<iterator> reverse_iterator;
4324 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4325
4326 MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4327 ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4328 {
4329 group_.Add(*this);
4330 }
4331
4332 virtual ~MapFlagList() {}
4333
4334 virtual void ParseValue(const std::vector<std::string> &values_) override
4335 {
4336 const std::string &value_ = values_.at(0);
4337
4338 K key{};
4339#ifdef ARGS_NOEXCEPT
4340 if (!reader(name, value_, key))
4341 {
4342 error = Error::Parse;
4343 return;
4344 }
4345#else
4346 reader(name, value_, key);
4347#endif
4348 auto it = map.find(key);
4349 if (it == std::end(map))
4350 {
4351 std::ostringstream problem;
4352 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4353#ifdef ARGS_NOEXCEPT
4354 error = Error::Map;
4355 errorMsg = problem.str();
4356#else
4357 throw MapError(problem.str());
4358#endif
4359 } else
4360 {
4361 this->values.emplace_back(it->second);
4362 }
4363 }
4364
4367 Container &Get() noexcept
4368 {
4369 return values;
4370 }
4371
4374 Container &operator *() noexcept
4375 {
4376 return values;
4377 }
4378
4381 const Container &operator *() const noexcept
4382 {
4383 return values;
4384 }
4385
4388 Container *operator ->() noexcept
4389 {
4390 return &values;
4391 }
4392
4395 const Container *operator ->() const noexcept
4396 {
4397 return &values;
4398 }
4399
4400 virtual std::string Name() const override
4401 {
4402 return name + std::string("...");
4403 }
4404
4405 virtual void Reset() noexcept override
4406 {
4407 ValueFlagBase::Reset();
4408 values = defaultValues;
4409 }
4410
4411 virtual FlagBase *Match(const EitherFlag &arg) override
4412 {
4413 const bool wasMatched = Matched();
4414 auto me = FlagBase::Match(arg);
4415 if (me && !wasMatched)
4416 {
4417 values.clear();
4418 }
4419 return me;
4420 }
4421
4422 iterator begin() noexcept
4423 {
4424 return values.begin();
4425 }
4426
4427 const_iterator begin() const noexcept
4428 {
4429 return values.begin();
4430 }
4431
4432 const_iterator cbegin() const noexcept
4433 {
4434 return values.cbegin();
4435 }
4436
4437 iterator end() noexcept
4438 {
4439 return values.end();
4440 }
4441
4442 const_iterator end() const noexcept
4443 {
4444 return values.end();
4445 }
4446
4447 const_iterator cend() const noexcept
4448 {
4449 return values.cend();
4450 }
4451 };
4452
4458 template <
4459 typename T,
4460 typename Reader = ValueReader>
4462 {
4463 private:
4464 T value;
4465 const T defaultValue;
4466 Reader reader;
4467 public:
4468 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_)
4469 {
4470 group_.Add(*this);
4471 }
4472
4473 Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_)
4474 {
4475 }
4476
4477 virtual ~Positional() {}
4478
4479 virtual void ParseValue(const std::string &value_) override
4480 {
4481#ifdef ARGS_NOEXCEPT
4482 if (!reader(name, value_, this->value))
4483 {
4484 error = Error::Parse;
4485 return;
4486 }
4487#else
4488 reader(name, value_, this->value);
4489#endif
4490 ready = false;
4491 matched = true;
4492 }
4493
4496 T &Get() noexcept
4497 {
4498 return value;
4499 }
4500
4503 T &operator *() noexcept
4504 {
4505 return value;
4506 }
4507
4510 const T &operator *() const noexcept
4511 {
4512 return value;
4513 }
4514
4517 T *operator ->() noexcept
4518 {
4519 return &value;
4520 }
4521
4524 const T *operator ->() const noexcept
4525 {
4526 return &value;
4527 }
4528
4529 virtual void Reset() noexcept override
4530 {
4531 PositionalBase::Reset();
4532 value = defaultValue;
4533 }
4534 };
4535
4542 template <
4543 typename T,
4544 template <typename...> class List = detail::vector,
4545 typename Reader = ValueReader>
4547 {
4548 private:
4549 using Container = List<T>;
4550 Container values;
4551 const Container defaultValues;
4552 Reader reader;
4553
4554 public:
4555 typedef T value_type;
4556 typedef typename Container::allocator_type allocator_type;
4557 typedef typename Container::pointer pointer;
4558 typedef typename Container::const_pointer const_pointer;
4559 typedef T& reference;
4560 typedef const T& const_reference;
4561 typedef typename Container::size_type size_type;
4562 typedef typename Container::difference_type difference_type;
4563 typedef typename Container::iterator iterator;
4564 typedef typename Container::const_iterator const_iterator;
4565 typedef std::reverse_iterator<iterator> reverse_iterator;
4566 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4567
4568 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_)
4569 {
4570 group_.Add(*this);
4571 }
4572
4573 PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_)
4574 {
4575 }
4576
4577 virtual ~PositionalList() {}
4578
4579 virtual void ParseValue(const std::string &value_) override
4580 {
4581 T v{};
4582#ifdef ARGS_NOEXCEPT
4583 if (!reader(name, value_, v))
4584 {
4585 error = Error::Parse;
4586 return;
4587 }
4588#else
4589 reader(name, value_, v);
4590#endif
4591 values.insert(std::end(values), v);
4592 matched = true;
4593 }
4594
4595 virtual std::string Name() const override
4596 {
4597 return name + std::string("...");
4598 }
4599
4602 Container &Get() noexcept
4603 {
4604 return values;
4605 }
4606
4609 Container &operator *() noexcept
4610 {
4611 return values;
4612 }
4613
4616 const Container &operator *() const noexcept
4617 {
4618 return values;
4619 }
4620
4623 Container *operator ->() noexcept
4624 {
4625 return &values;
4626 }
4627
4630 const Container *operator ->() const noexcept
4631 {
4632 return &values;
4633 }
4634
4635 virtual void Reset() noexcept override
4636 {
4637 PositionalBase::Reset();
4638 values = defaultValues;
4639 }
4640
4641 virtual PositionalBase *GetNextPositional() override
4642 {
4643 const bool wasMatched = Matched();
4644 auto me = PositionalBase::GetNextPositional();
4645 if (me && !wasMatched)
4646 {
4647 values.clear();
4648 }
4649 return me;
4650 }
4651
4652 iterator begin() noexcept
4653 {
4654 return values.begin();
4655 }
4656
4657 const_iterator begin() const noexcept
4658 {
4659 return values.begin();
4660 }
4661
4662 const_iterator cbegin() const noexcept
4663 {
4664 return values.cbegin();
4665 }
4666
4667 iterator end() noexcept
4668 {
4669 return values.end();
4670 }
4671
4672 const_iterator end() const noexcept
4673 {
4674 return values.end();
4675 }
4676
4677 const_iterator cend() const noexcept
4678 {
4679 return values.cend();
4680 }
4681 };
4682
4690 template <
4691 typename K,
4692 typename T,
4693 typename Reader = ValueReader,
4694 template <typename...> class Map = detail::unordered_map>
4696 {
4697 private:
4698 const Map<K, T> map;
4699 T value;
4700 const T defaultValue;
4701 Reader reader;
4702
4703 protected:
4704 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4705 {
4706 return detail::MapKeysToStrings(map);
4707 }
4708
4709 public:
4710
4711 MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}):
4712 PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_)
4713 {
4714 group_.Add(*this);
4715 }
4716
4717 virtual ~MapPositional() {}
4718
4719 virtual void ParseValue(const std::string &value_) override
4720 {
4721 K key{};
4722#ifdef ARGS_NOEXCEPT
4723 if (!reader(name, value_, key))
4724 {
4725 error = Error::Parse;
4726 return;
4727 }
4728#else
4729 reader(name, value_, key);
4730#endif
4731 auto it = map.find(key);
4732 if (it == std::end(map))
4733 {
4734 std::ostringstream problem;
4735 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4736#ifdef ARGS_NOEXCEPT
4737 error = Error::Map;
4738 errorMsg = problem.str();
4739#else
4740 throw MapError(problem.str());
4741#endif
4742 } else
4743 {
4744 this->value = it->second;
4745 ready = false;
4746 matched = true;
4747 }
4748 }
4749
4752 T &Get() noexcept
4753 {
4754 return value;
4755 }
4756
4759 T &operator *() noexcept
4760 {
4761 return value;
4762 }
4763
4766 const T &operator *() const noexcept
4767 {
4768 return value;
4769 }
4770
4773 T *operator ->() noexcept
4774 {
4775 return &value;
4776 }
4777
4780 const T *operator ->() const noexcept
4781 {
4782 return &value;
4783 }
4784
4785 virtual void Reset() noexcept override
4786 {
4787 PositionalBase::Reset();
4788 value = defaultValue;
4789 }
4790 };
4791
4800 template <
4801 typename K,
4802 typename T,
4803 template <typename...> class List = detail::vector,
4804 typename Reader = ValueReader,
4805 template <typename...> class Map = detail::unordered_map>
4807 {
4808 private:
4809 using Container = List<T>;
4810
4811 const Map<K, T> map;
4812 Container values;
4813 const Container defaultValues;
4814 Reader reader;
4815
4816 protected:
4817 virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override
4818 {
4819 return detail::MapKeysToStrings(map);
4820 }
4821
4822 public:
4823 typedef T value_type;
4824 typedef typename Container::allocator_type allocator_type;
4825 typedef typename Container::pointer pointer;
4826 typedef typename Container::const_pointer const_pointer;
4827 typedef T& reference;
4828 typedef const T& const_reference;
4829 typedef typename Container::size_type size_type;
4830 typedef typename Container::difference_type difference_type;
4831 typedef typename Container::iterator iterator;
4832 typedef typename Container::const_iterator const_iterator;
4833 typedef std::reverse_iterator<iterator> reverse_iterator;
4834 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
4835
4836 MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}):
4837 PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_)
4838 {
4839 group_.Add(*this);
4840 }
4841
4842 virtual ~MapPositionalList() {}
4843
4844 virtual void ParseValue(const std::string &value_) override
4845 {
4846 K key{};
4847#ifdef ARGS_NOEXCEPT
4848 if (!reader(name, value_, key))
4849 {
4850 error = Error::Parse;
4851 return;
4852 }
4853#else
4854 reader(name, value_, key);
4855#endif
4856 auto it = map.find(key);
4857 if (it == std::end(map))
4858 {
4859 std::ostringstream problem;
4860 problem << "Could not find key '" << key << "' in map for arg '" << name << "'";
4861#ifdef ARGS_NOEXCEPT
4862 error = Error::Map;
4863 errorMsg = problem.str();
4864#else
4865 throw MapError(problem.str());
4866#endif
4867 } else
4868 {
4869 this->values.emplace_back(it->second);
4870 matched = true;
4871 }
4872 }
4873
4876 Container &Get() noexcept
4877 {
4878 return values;
4879 }
4880
4883 Container &operator *() noexcept
4884 {
4885 return values;
4886 }
4887
4890 const Container &operator *() const noexcept
4891 {
4892 return values;
4893 }
4894
4897 Container *operator ->() noexcept
4898 {
4899 return &values;
4900 }
4901
4904 const Container *operator ->() const noexcept
4905 {
4906 return &values;
4907 }
4908
4909 virtual std::string Name() const override
4910 {
4911 return name + std::string("...");
4912 }
4913
4914 virtual void Reset() noexcept override
4915 {
4916 PositionalBase::Reset();
4917 values = defaultValues;
4918 }
4919
4920 virtual PositionalBase *GetNextPositional() override
4921 {
4922 const bool wasMatched = Matched();
4923 auto me = PositionalBase::GetNextPositional();
4924 if (me && !wasMatched)
4925 {
4926 values.clear();
4927 }
4928 return me;
4929 }
4930
4931 iterator begin() noexcept
4932 {
4933 return values.begin();
4934 }
4935
4936 const_iterator begin() const noexcept
4937 {
4938 return values.begin();
4939 }
4940
4941 const_iterator cbegin() const noexcept
4942 {
4943 return values.cbegin();
4944 }
4945
4946 iterator end() noexcept
4947 {
4948 return values.end();
4949 }
4950
4951 const_iterator end() const noexcept
4952 {
4953 return values.end();
4954 }
4955
4956 const_iterator cend() const noexcept
4957 {
4958 return values.cend();
4959 }
4960 };
4961}
4962
4963#pragma pop_macro("min")
4964#pragma pop_macro("max")
4965#endif
A flag class that calls a function when it's matched.
Definition args.hxx:3517
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3543
virtual void ParseValue(const std::vector< std::string > &value) override
Parse values of this option.
Definition args.hxx:3546
The main user facing command line argument parser class.
Definition args.hxx:2408
const std::string & ShortPrefix() const
The prefix for short flags.
Definition args.hxx:3075
void SetArgumentSeparations(const bool allowJoinedShortValue_, const bool allowJoinedLongValue_, const bool allowSeparateShortValue_, const bool allowSeparateLongValue_)
Change allowed option separation.
Definition args.hxx:3141
void Prog(const std::string &prog_)
The program name for help generation.
Definition args.hxx:3058
It ParseArgs(It begin, It end)
Parse all arguments.
Definition args.hxx:3297
const std::string & Prog() const
The program name for help generation.
Definition args.hxx:3054
void LongPrefix(const std::string &longprefix_)
The prefix for long flags.
Definition args.hxx:3067
void LongSeparator(const std::string &longseparator_)
The separator for long flags.
Definition args.hxx:3091
void Help(std::ostream &help_) const
Pass the help menu into an ostream.
Definition args.hxx:3158
const std::string & LongPrefix() const
The prefix for long flags.
Definition args.hxx:3063
const std::string & LongSeparator() const
The separator for long flags.
Definition args.hxx:3087
bool ParseCLI(const int argc, const char *const *argv)
Convenience function to parse the CLI from argc and argv.
Definition args.hxx:3328
const std::string & Terminator() const
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3111
auto ParseArgs(const T &args) -> decltype(std::begin(args))
Parse all arguments.
Definition args.hxx:3317
void Terminator(const std::string &terminator_)
The terminator that forcibly separates flags from positionals.
Definition args.hxx:3115
void GetArgumentSeparations(bool &allowJoinedShortValue_, bool &allowJoinedLongValue_, bool &allowSeparateShortValue_, bool &allowSeparateLongValue_) const
Get the current argument separation parameters.
Definition args.hxx:3122
std::string Help() const
Generate a help menu as a string.
Definition args.hxx:3276
void ShortPrefix(const std::string &shortprefix_)
The prefix for short flags.
Definition args.hxx:3079
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:2482
Base class for all match types.
Definition args.hxx:883
void KickOut(bool kickout_) noexcept
Sets a kick-out value for building subparsers.
Definition args.hxx:978
bool KickOut() const noexcept
Gets the kick-out value for building subparsers.
Definition args.hxx:991
Main class for building subparsers.
Definition args.hxx:1868
const std::string & Name() const
The name of command.
Definition args.hxx:2003
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:2104
const std::string & ProglinePostfix() const
The description that appears on the prog line after options.
Definition args.hxx:1973
void Description(const std::string &description_)
The description that appears above options.
Definition args.hxx:1988
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:2140
const std::string & Description() const
The description that appears above options.
Definition args.hxx:1983
const std::string & Help() const
The description of command.
Definition args.hxx:2008
void Epilog(const std::string &epilog_)
The description that appears below options.
Definition args.hxx:1998
void ProglinePostfix(const std::string &proglinePostfix_)
The description that appears on the prog line after options.
Definition args.hxx:1978
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:2040
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:2145
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:2209
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:2135
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:2234
const std::string & Epilog() const
The description that appears below options.
Definition args.hxx:1993
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:2021
void RequireCommand(bool value)
If value is true, parser will fail if no command was parsed.
Definition args.hxx:2015
Definition args.hxx:1327
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1341
std::string Get() noexcept
Get the completion reply.
Definition args.hxx:1403
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:1346
An exception that contains autocompletion reply.
Definition args.hxx:432
A flag class that simply counts the number of times it's matched.
Definition args.hxx:3461
int & Get() noexcept
Get the count.
Definition args.hxx:3494
Base error class.
Definition args.hxx:351
Error that occurs when a singular flag is specified multiple times.
Definition args.hxx:405
Base class for all flag options.
Definition args.hxx:1170
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:3399
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3419
virtual void ParseValue(const std::vector< std::string > &) override
Parse values of this option.
Definition args.hxx:3424
bool Get() const
Get whether this was matched.
Definition args.hxx:3414
Class for using global options in ArgumentParser.
Definition args.hxx:1787
Class for all kinds of validating groups, including ArgumentParser.
Definition args.hxx:1481
virtual bool HasCommand() const override
Get whether this has any Command children.
Definition args.hxx:1635
std::vector< Base * >::size_type MatchedChildren() const
Count the number of matched children this group has.
Definition args.hxx:1642
virtual bool HasPositional() const override
Get whether this has any PositionalBase children.
Definition args.hxx:1626
virtual std::vector< std::string > GetProgramLine(const HelpParams &params) const override
Get the names of positional parameters.
Definition args.hxx:1695
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:1541
virtual bool HasFlag() const override
Get whether this has any FlagBase children.
Definition args.hxx:1617
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:1539
virtual bool Matched() const noexcept override
Whether or not this group matches validation.
Definition args.hxx:1651
virtual FlagBase * Match(const EitherFlag &flag) override
Return the first FlagBase that matches flag, or nullptr.
Definition args.hxx:1566
bool Get() const
Get validation.
Definition args.hxx:1658
const std::vector< Base * > & Children() const
Get all this group's children.
Definition args.hxx:1556
void Add(Base &child)
Append a child to this Group.
Definition args.hxx:1549
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:1665
virtual PositionalBase * GetNextPositional() override
Get the next ready positional, or nullptr if there is none.
Definition args.hxx:1601
Help flag class.
Definition args.hxx:3434
bool Get() const noexcept
Get whether this was matched.
Definition args.hxx:3452
virtual void ParseValue(const std::vector< std::string > &)
Parse values of this option.
Definition args.hxx:3440
An exception that indicates that the user has requested help.
Definition args.hxx:414
An optional argument-accepting flag class.
Definition args.hxx:3832
virtual void ParseValue(const std::vector< std::string > &value_) override
Parse values of this option.
Definition args.hxx:3860
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3855
Errors in map lookups.
Definition args.hxx:396
A mapping value flag list class.
Definition args.hxx:4298
Container * operator->() noexcept
Get the values.
Definition args.hxx:4388
Container & Get() noexcept
Get the value.
Definition args.hxx:4367
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4334
Container & operator*() noexcept
Get the value.
Definition args.hxx:4374
A mapping value flag class.
Definition args.hxx:4180
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4210
T & Get() noexcept
Get the value.
Definition args.hxx:4243
T * operator->() noexcept
Get the value.
Definition args.hxx:4264
T & operator*() noexcept
Get the value.
Definition args.hxx:4250
A positional argument mapping list class.
Definition args.hxx:4807
Container & operator*() noexcept
Get the value.
Definition args.hxx:4883
Container * operator->() noexcept
Get the values.
Definition args.hxx:4897
Container & Get() noexcept
Get the value.
Definition args.hxx:4876
A positional argument mapping class.
Definition args.hxx:4696
T * operator->() noexcept
Get the value.
Definition args.hxx:4773
T & Get() noexcept
Get the value.
Definition args.hxx:4752
T & operator*() noexcept
Get the value.
Definition args.hxx:4759
A class of "matchers", specifying short and flags that can possibly be matched.
Definition args.hxx:500
EitherFlag GetShortOrAny() const
(INTERNAL) Get short flag if it exists or any long flag
Definition args.hxx:618
std::vector< EitherFlag > GetFlagStrings() const
(INTERNAL) Get all flag strings as a vector, with the prefixes embedded
Definition args.hxx:583
Matcher(Short &&shortIn, Long &&longIn)
Specify short and long flags separately as iterables.
Definition args.hxx:536
Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd)
Specify short and long flags separately as iterators.
Definition args.hxx:511
bool Match(const std::string &flag) const
(INTERNAL) Check if there is a match of a long flag
Definition args.hxx:569
EitherFlag GetLongOrAny() const
(INTERNAL) Get long flag if it exists or any short flag
Definition args.hxx:600
bool Match(const char flag) const
(INTERNAL) Check if there is a match of a short flag
Definition args.hxx:562
bool Match(const EitherFlag &flag) const
(INTERNAL) Check if there is a match of a flag
Definition args.hxx:576
Matcher(std::initializer_list< EitherFlag > in)
Specify a mixed single initializer-list of both short and long flags.
Definition args.hxx:552
Base class for all match types that have a name.
Definition args.hxx:1023
void HelpDefault(const std::string &str)
Sets default value string that will be added to argument description.
Definition args.hxx:1059
void HelpChoices(const std::vector< std::string > &array)
Sets choices strings that will be added to argument description.
Definition args.hxx:1075
std::string HelpDefault(const HelpParams &params) const
Gets default value string that will be added to argument description.
Definition args.hxx:1067
std::vector< std::string > HelpChoices(const HelpParams &params) const
Gets choices strings that will be added to argument description.
Definition args.hxx:1083
A variadic arguments accepting flag class.
Definition args.hxx:3883
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:3915
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:3920
List< T > * operator->() noexcept
Get the values.
Definition args.hxx:3961
List< T > & operator*() noexcept
Get the value.
Definition args.hxx:3947
Errors that occur during regular parsing.
Definition args.hxx:369
Base class for positional options.
Definition args.hxx:1421
A positional argument class that pushes the found values into a list.
Definition args.hxx:4547
Container & operator*() noexcept
Get the value.
Definition args.hxx:4609
Container & Get() noexcept
Get the values.
Definition args.hxx:4602
Container * operator->() noexcept
Get the values.
Definition args.hxx:4623
A positional argument class.
Definition args.hxx:4462
T & operator*() noexcept
Get the value.
Definition args.hxx:4503
T & Get() noexcept
Get the value.
Definition args.hxx:4496
T * operator->() noexcept
Get the value.
Definition args.hxx:4517
Errors that when a required flag is omitted.
Definition args.hxx:387
Utility class for building subparsers with coroutines/callbacks.
Definition args.hxx:1813
void Parse()
Continue parsing arguments for new command.
Definition args.hxx:3362
const std::vector< std::string > & KickedOut() const noexcept
Returns a vector of kicked out arguments.
Definition args.hxx:1857
bool IsParsed() const
(INTERNAL) Determines whether Parse was called or not.
Definition args.hxx:1844
Errors that occur during usage.
Definition args.hxx:360
Errors that are detected from group validation after parsing finishes.
Definition args.hxx:378
Base class for value-accepting flag options.
Definition args.hxx:1314
virtual Nargs NumberOfArguments() const noexcept override
Defines how many values can be consumed by this option.
Definition args.hxx:1320
An argument-accepting flag class that pushes the found values into a list.
Definition args.hxx:4032
Container * operator->() noexcept
Get the values.
Definition args.hxx:4102
Container & Get() noexcept
Get the values.
Definition args.hxx:4081
Container & operator*() noexcept
Get the value.
Definition args.hxx:4088
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:4062
An argument-accepting flag class.
Definition args.hxx:3730
T & operator*() noexcept
Get the value.
Definition args.hxx:3789
T * operator->() noexcept
Get the value.
Definition args.hxx:3803
T & Get() noexcept
Get the value.
Definition args.hxx:3782
const T & GetDefault() noexcept
Get the default value.
Definition args.hxx:3817
virtual void ParseValue(const std::vector< std::string > &values_) override
Parse values of this option.
Definition args.hxx:3760
Definition args.hxx:1118
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:186
bool SafeMultiply(T a, T b, T &out)
Safe multiplication to prevent integer overflow.
Definition args.hxx:133
Options
Attributes for flags.
Definition args.hxx:638
@ 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.
bool SafeAdd(T a, T b, T &out)
Safe addition to prevent integer overflow.
Definition args.hxx:108
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:442
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:467
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:452
Default validators.
Definition args.hxx:1490
A simple structure of parameters for easy user-modifyable help menus.
Definition args.hxx:694
std::string programName
The program name for help generation.
Definition args.hxx:751
bool showCommandFullHelp
Show command's descriptions and epilog.
Definition args.hxx:759
bool proglineShowFlags
Show flags in program line.
Definition args.hxx:795
std::string usageString
Program line prefix.
Definition args.hxx:803
unsigned int width
The width of the help menu.
Definition args.hxx:697
std::string proglineNonrequiredClose
The postfix for progline non-required argument.
Definition args.hxx:791
unsigned int helpindent
The indent of the flag descriptions.
Definition args.hxx:712
bool proglinePreferShortFlags
Use short flags in program lines when possible.
Definition args.hxx:799
std::string proglineCommand
The prefix for progline when command has any subcommands.
Definition args.hxx:767
std::string proglineOptions
The postfix for progline when showProglineOptions is true and command has any flags.
Definition args.hxx:763
std::string longSeparator
The separator for long flags.
Definition args.hxx:747
bool showValueName
Show value name.
Definition args.hxx:815
bool showTerminator
Show the terminator when both options and positional parameters are present.
Definition args.hxx:723
std::string proglineValueOpen
The prefix for progline value.
Definition args.hxx:771
std::string valueOpen
The prefix for option value.
Definition args.hxx:823
unsigned int progtailindent
The indent of the program trailing lines for long parameters.
Definition args.hxx:703
std::string proglineNonrequiredOpen
The prefix for progline non-required argument.
Definition args.hxx:787
unsigned int descriptionindent
The indent of the description and epilogs.
Definition args.hxx:706
bool showCommandChildren
Show command's flags.
Definition args.hxx:755
bool showProglineOptions
Show the {OPTIONS} on the prog line when this is true.
Definition args.hxx:727
std::string valueClose
The postfix for option value.
Definition args.hxx:827
std::string longPrefix
The prefix for long flags.
Definition args.hxx:739
std::string proglineRequiredClose
The postfix for progline required argument.
Definition args.hxx:783
unsigned int flagindent
The indent of the flags.
Definition args.hxx:709
std::string optionsString
String shown in help before flags descriptions.
Definition args.hxx:807
bool addNewlineBeforeDescription
Add newline before flag description.
Definition args.hxx:819
std::string shortPrefix
The prefix for short flags.
Definition args.hxx:735
bool addDefault
Add default values to argument description.
Definition args.hxx:839
unsigned int gutter
The minimum gutter between each flag and its help.
Definition args.hxx:719
bool showProglinePositionals
Show the positionals on the prog line when this is true.
Definition args.hxx:731
std::string shortSeparator
The separator for short flags.
Definition args.hxx:743
std::string proglineValueClose
The postfix for progline value.
Definition args.hxx:775
bool useValueNameOnce
Display value name after all the long and short flags.
Definition args.hxx:811
std::string defaultString
The prefix for default values.
Definition args.hxx:843
std::string proglineRequiredOpen
The prefix for progline required argument.
Definition args.hxx:779
unsigned int eachgroupindent
The additional indent each group adds.
Definition args.hxx:715
unsigned int progindent
The indent of the program line.
Definition args.hxx:700
bool addChoices
Add choices to argument description.
Definition args.hxx:831
std::string choiceString
The prefix for choices.
Definition args.hxx:835
A number of arguments which can be consumed by an option.
Definition args.hxx:851
A default Reader class for argument classes.
Definition args.hxx:3557