Fancy argparser
Single file argument parser for c++
Loading...
Searching...
No Matches
argparse.h
Go to the documentation of this file.
1
24
25#pragma once
26
27// define your own macro ARGPARSE_NAMESPACE_NAME
28// if you doesn't like default namespace "argparse"
29#ifndef ARGPARSE_NAMESPACE_NAME
30#define ARGPARSE_NAMESPACE_NAME argparse
31#endif
32
33#include <string>
34#include <sstream>
35#include <vector>
36#include <tuple>
37#include <map>
38#include <algorithm>
39#include <stdexcept>
40#include <limits>
41#include <initializer_list>
42
43#if __cplusplus > 201402L || _MSVC_LANG > 201402L
44#include <any>
45#endif
46
51{
53 namespace
54 {
55 const size_t kSizeTypeEnd = static_cast<size_t>(-1);
56 const size_t kHelpWidth = 80;
57 const size_t kHelpNameWidthPercent = 30;
58
59 bool isNumber(const std::string& inStr)
60 {
61 const bool hasNegSign = inStr.at(0) == '-';
62 size_t dotPos = 0, expPos = 0;
63 size_t startPos = static_cast<size_t>(hasNegSign);
64 for (size_t curPos = startPos; curPos < inStr.size(); ++curPos)
65 {
66 const char curChar = inStr.at(curPos);
67 if (curChar >= '0' && curChar <= '9')
68 {
69 continue;
70 }
71 else if (curChar == '.'
72 && !dotPos && curPos != startPos && curPos != inStr.size() - 1)
73 {
74 dotPos = curPos;
75 }
76 else if ((curChar == 'e' || curChar == 'E')
77 && !expPos && curPos != startPos && curPos != inStr.size() - 1)
78 {
79 expPos = curPos;
80 if (!dotPos)
81 {
82 return false;
83 }
84 }
85 else
86 {
87 return false;
88 }
89 }
90 return true;
91 }
92
93 size_t getStringStreamLength(std::stringstream& showDesc)
94 {
95 showDesc.seekp(0, std::ios::end);
96 return showDesc.tellp();
97 }
98 }
99
100
111
114 const int kAnyArgCount = -1;
118
119
120 class ArgumentParser;
121 class ArgumentsObject;
122
125 class Argument
126 {
135 Argument(const std::string& positionalName = "",
136 const std::string& shortName = "",
137 const std::string& longName = "",
138 const int argsCount = 1,
140 const bool required = true,
141 const std::string& help = "")
142 : m_required(required)
143 , m_nargs(argsCount)
144 , m_type(argType)
145 , m_positionalName(positionalName)
146 , m_shortName(shortName)
147 , m_longName(longName)
148 , m_help(help)
149 {}
150 public:
151
159 static Argument CreateNamedArgument(const std::string& shortName = "",
160 const std::string& longName = "",
161 const int argsCount = 1,
163 const bool required = true,
164 const std::string& help = "")
165 {
166 return Argument("", shortName, longName, argsCount, argType, required, help);
167 }
168
175 static Argument CreatePositionalArgument(const std::string& positionalName = "",
176 const int argsCount = 1,
178 const bool required = true,
179 const std::string& help = "")
180 {
181 return Argument(positionalName, "", "", argsCount, argType, required, help);
182 }
183
193 bool m_required = true;
194
198 Argument& SetRequired(bool required)
199 {
200 m_required = required;
201 return *this;
202 }
203
210 int m_nargs = 1;
211
216 Argument& SetNumberOfArguments(int amount)
217 {
218 m_nargs = amount;
219 return *this;
220 }
221
225 {
227 return *this;
228 }
229
233 {
235 return *this;
236 }
237
241 {
242 m_nargs = 0;
243 return *this;
244 }
245
249
254 Argument& SetType(ArgTypeCast argType)
255 {
256 m_type = argType;
257 return *this;
258 }
259
262 std::string m_positionalName = "";
263
267 Argument& SetPositionalName(const std::string& name)
268 {
269 m_positionalName = name;
270 return *this;
271 }
272
276 std::string m_shortName = "";
277
283 Argument& SetShortName(const std::string& name)
284 {
285 m_shortName = name;
286 return *this;
287 }
288
292 std::string m_longName = "";
293
300 Argument& SetLongName(const std::string& name)
301 {
302 m_longName = name;
303 return *this;
304 }
305
308 std::string m_help = "";
309
313 Argument& SetHelp(const std::string& help)
314 {
315 m_help = help;
316 return *this;
317 }
318
321 std::vector<std::string> m_choicesString = {};
322
326 Argument& SetChoices(const std::vector<std::string>& choices)
327 {
329 {
330 throw std::runtime_error("wrong type");
331 }
332 m_choicesString = choices;
333 return *this;
334 }
335
341 Argument& SetChoices(std::initializer_list<const char*> choices)
342 {
343 return SetChoices(std::vector<std::string>(choices.begin(), choices.end()));
344 }
345
348 std::vector<int> m_choicesInt = {};
349
353 Argument& SetChoices(const std::vector<int>& choices)
354 {
356 {
357 throw std::runtime_error("wrong type");
358 }
359 m_choicesInt = choices;
360 return *this;
361 }
362
365 std::vector<long long> m_choicesLongLong = {};
366
370 Argument& SetChoices(const std::vector<long long>& choices)
371 {
373 {
374 throw std::runtime_error("wrong type");
375 }
376 m_choicesLongLong = choices;
377 return *this;
378 }
379
382 std::vector<double> m_choicesDouble = {};
383
387 Argument& SetChoices(const std::vector<double>& choices)
388 {
390 {
391 throw std::runtime_error("wrong type");
392 }
393 m_choicesDouble = choices;
394 return *this;
395 }
396
400 Argument& SetDefault(bool defaultArg)
401 {
403 {
404 throw std::runtime_error("wrong type");
405 }
406 m_defaultBool.push_back(defaultArg);
407 m_hasDefault = true;
408 return *this;
409 }
410
414 Argument& SetDefault(int defaultArg)
415 {
417 {
418 throw std::runtime_error("wrong type");
419 }
420 m_defaultInt.push_back(defaultArg);
421 m_hasDefault = true;
422 return *this;
423 }
424
428 Argument& SetDefault(long long defaultArg)
429 {
431 {
432 throw std::runtime_error("wrong type");
433 }
434 m_defaultLongLong.push_back(defaultArg);
435 m_hasDefault = true;
436 return *this;
437 }
438
442 Argument& SetDefault(double defaultArg)
443 {
445 {
446 throw std::runtime_error("wrong type");
447 }
448 m_defaultDouble.push_back(defaultArg);
449 m_hasDefault = true;
450 return *this;
451 }
452
456 Argument& SetDefault(std::string defaultArg)
457 {
459 {
460 throw std::runtime_error("wrong type");
461 }
462 m_defaultString.push_back(defaultArg);
463 m_hasDefault = true;
464 return *this;
465 }
466
470 Argument& SetDefault(const std::vector<bool>& defaultArg)
471 {
473 {
474 throw std::runtime_error("wrong type");
475 }
476 m_defaultBool = defaultArg;
477 m_hasDefault = true;
478 return *this;
479 }
480
484 Argument& SetDefault(const std::vector<int>& defaultArg)
485 {
487 {
488 throw std::runtime_error("wrong type");
489 }
490 m_defaultInt = defaultArg;
491 m_hasDefault = true;
492 return *this;
493 }
494
498 Argument& SetDefault(const std::vector<long long>& defaultArg)
499 {
501 {
502 throw std::runtime_error("wrong type");
503 }
504 m_defaultLongLong = defaultArg;
505 m_hasDefault = true;
506 return *this;
507 }
508
512 Argument& SetDefault(const std::vector<double>& defaultArg)
513 {
515 {
516 throw std::runtime_error("wrong type");
517 }
518 m_defaultDouble = defaultArg;
519 m_hasDefault = true;
520 return *this;
521 }
522
526 Argument& SetDefault(const std::vector<std::string>& defaultArg)
527 {
529 {
530 throw std::runtime_error("wrong type");
531 }
532 m_defaultString = defaultArg;
533 m_hasDefault = true;
534 return *this;
535 }
536
537
540 bool HasDefault() const
541 {
542 return m_hasDefault;
543 }
544
545 private:
546 bool m_hasDefault = false;
547 std::vector<bool> m_defaultBool = {};
548 std::vector<int> m_defaultInt = {};
549 std::vector<long long> m_defaultLongLong = {};
550 std::vector<double> m_defaultDouble = {};
551 std::vector<std::string> m_defaultString = {};
552
553 friend ArgumentsObject;
554 };
555
568 inline Argument CreateNamedArgument(const std::string& shortName = "",
569 const std::string& longName = "",
570 const int argsCount = 1,
572 const bool required = true,
573 const std::string& help = "")
574 {
575 return Argument::CreateNamedArgument(shortName, longName, argsCount, argType, required, help);
576 }
577
587 inline Argument CreatePositionalArgument(const std::string& positionalName = "",
588 const int argsCount = 1,
590 const bool required = true,
591 const std::string& help = "")
592 {
593 return Argument::CreatePositionalArgument(positionalName, argsCount, argType, required, help);
594 }
595
609 {
610 std::string shortName = "";
611 std::string longName = "";
612 int nargs = 1;
614 bool required = true;
615 std::string help = "";
616 };
617
622 {
624 spec.nargs, spec.type, spec.required, spec.help);
625 }
626
630 {
631 std::string name = "";
632 int nargs = 1;
634 bool required = true;
635 std::string help = "";
636 };
637
642 {
644 spec.type, spec.required, spec.help);
645 }
646
649 {
650 public:
654 {
655 return m_exists;
656 }
657
661 {
662 return m_count;
663 }
664
669 bool GetAsBool() const
670 {
671 ThrowIfEmpty(m_bool.empty(), "GetAsBool");
672 return m_bool.front();
673 }
674
679 int GetAsInt() const
680 {
681 ThrowIfEmpty(m_int.empty(), "GetAsInt");
682 return m_int.front();
683 }
684
689 long long GetAsLongLong() const
690 {
691 ThrowIfEmpty(m_longLong.empty(), "GetAsLongLong");
692 return m_longLong.front();
693 }
694
695
700 double GetAsDouble() const
701 {
702 ThrowIfEmpty(m_double.empty(), "GetAsDouble");
703 return m_double.front();
704 }
705
706
711 std::string GetAsString() const
712 {
713 ThrowIfEmpty(m_string.empty(), "GetAsString");
714 return m_string.front();
715 }
716
721 std::vector<bool> GetAsVecBool() const
722 {
723 return m_bool;
724 }
725
729 std::vector<int> GetAsVecInt() const
730 {
731 return m_int;
732 }
733
737 std::vector<long long> GetAsVecLongLong() const
738 {
739 return m_longLong;
740 }
741
745 std::vector<double> GetAsVecDouble() const
746 {
747 return m_double;
748 }
749
753 std::vector<std::string> GetAsVecString() const
754 {
755 return m_string;
756 }
757
758#if __cplusplus > 201402L || _MSVC_LANG > 201402L
762 std::any Get()
763 {
764 switch (m_type)
765 {
767 if (m_count == 1)
768 {
769 return m_string.front();
770 }
771 return m_string;
772 break;
773 case ArgTypeCast::e_int:
774 if (m_count == 1)
775 {
776 return m_int.front();
777 }
778 return m_int;
779 break;
780 case ArgTypeCast::e_longlong:
781 if (m_count == 1)
782 {
783 return m_longLong.front();
784 }
785 return m_longLong;
786 break;
787 case ArgTypeCast::e_double:
788 if (m_count == 1)
789 {
790 return m_double.front();
791 }
792 return m_double;
793 break;
794 case ArgTypeCast::e_bool:
795 default:
796 if (m_count == 1)
797 {
798 return m_bool.front();
799 }
800 return m_bool;
801 break;
802 }
803 }
804
805#endif // __cplusplus >=
806
807
808 protected:
809
811
815 static void ThrowIfEmpty(bool empty, const char* getter)
816 {
817 if (empty)
818 {
819 throw std::out_of_range(std::string("ArgumentParsed::") + getter
820 + "() called on an argument that holds no value");
821 }
822 }
823
825 bool m_exists{ false };
829 size_t m_count{ 0 };
830
832 std::vector<bool> m_bool = {};
834 std::vector<int> m_int = {};
836 std::vector<long long> m_longLong = {};
838 std::vector<double> m_double = {};
840 std::vector<std::string> m_string = {};
841
843 };
844
848 class ArgumentsObject
849 {
850 public:
853 bool IsArgValid() const
854 {
855 return m_isValid;
856 }
857
860 const std::string& GetErrorString()
861 {
862 return m_error;
863 }
864
867 size_t ParsedArgsCount() const
868 {
869 return m_parsed.size();
870 }
871
875 ArgumentParsed GetArg(const std::string& name)
876 {
878 arg.m_exists = false;
879 arg.m_count = 0;
880
881 const auto position = m_names.find(name);
882 if (position == m_names.end())
883 {
884 return arg;
885 }
886
887 const auto argument = m_parsed.find(position->second);
888 if (argument == m_parsed.end())
889 {
890 return arg;
891 }
892
893 return argument->second;
894 }
895
896 private:
897 ArgumentsObject() {}
898
899 void SetValid()
900 {
901 m_isValid = true;
902 }
903
904 void SetErrorString(const std::string& error)
905 {
906 m_parsed.clear();
907 m_error = error;
908 }
909
913 void ParseDefault(const Argument& argObj, const size_t position)
914 {
915 ArgumentParsed arg = ArgumentParsed();
916 arg.m_exists = true;
917 arg.m_type = argObj.m_type;
918
919 if (argObj.m_type == ArgTypeCast::e_String)
920 {
921 arg.m_string = argObj.m_defaultString;
922 arg.m_count = argObj.m_defaultString.size();
923 }
924 else if (argObj.m_type == ArgTypeCast::e_bool)
925 {
926 arg.m_bool = argObj.m_defaultBool;
927 arg.m_count = argObj.m_defaultBool.size();
928 }
929 else if (argObj.m_type == ArgTypeCast::e_double)
930 {
931 arg.m_double = argObj.m_defaultDouble;
932 arg.m_count = argObj.m_defaultDouble.size();
933 }
934 else if (argObj.m_type == ArgTypeCast::e_longlong)
935 {
936 arg.m_longLong = argObj.m_defaultLongLong;
937 arg.m_count = argObj.m_defaultLongLong.size();
938 }
939 else if (argObj.m_type == ArgTypeCast::e_int)
940 {
941 arg.m_int = argObj.m_defaultInt;
942 arg.m_count = argObj.m_defaultInt.size();
943 }
944
945 m_parsed.emplace(position, arg);
946 if (!argObj.m_shortName.empty())
947 {
948 m_names[argObj.m_shortName] = position;
949 }
950 if (!argObj.m_longName.empty())
951 {
952 m_names[argObj.m_longName] = position;
953 }
954 if (!argObj.m_positionalName.empty())
955 {
956 m_names[argObj.m_positionalName] = position;
957 }
958 }
959
964 void CreateParsingStub(const Argument& argObj, const size_t position)
965 {
966 const auto argument = m_parsed.find(position);
967 if (argument == m_parsed.end())
968 {
969 ArgumentParsed arg = ArgumentParsed();
970 arg.m_exists = true;
971 arg.m_count = 0;
972 arg.m_type = argObj.m_type;
973 m_parsed.emplace(position, arg);
974 if (!argObj.m_shortName.empty())
975 {
976 m_names[argObj.m_shortName] = position;
977 }
978 if (!argObj.m_longName.empty())
979 {
980 m_names[argObj.m_longName] = position;
981 }
982 if (!argObj.m_positionalName.empty())
983 {
984 m_names[argObj.m_positionalName] = position;
985 }
986 }
987 }
988
994 bool Parse(const Argument& argObj, const size_t position, const std::string& token)
995 {
996 const auto argument = m_parsed.find(position);
997 if (argument == m_parsed.end())
998 {
999 ArgumentParsed arg = ArgumentParsed();
1000 arg.m_exists = true;
1001 arg.m_count = 0;
1002 arg.m_type = argObj.m_type;
1003 m_parsed.emplace(position, arg);
1004 if (!argObj.m_shortName.empty())
1005 {
1006 m_names[argObj.m_shortName] = position;
1007 }
1008 if (!argObj.m_longName.empty())
1009 {
1010 m_names[argObj.m_longName] = position;
1011 }
1012 if (!argObj.m_positionalName.empty())
1013 {
1014 m_names[argObj.m_positionalName] = position;
1015 }
1016 return true;
1017 }
1018
1019 if (argument->second.m_type == ArgTypeCast::e_String)
1020 {
1021 if (argObj.m_nargs != 0)
1022 {
1023 if (argObj.m_choicesString.size())
1024 {
1025 auto it = std::find_if(argObj.m_choicesString.begin(), argObj.m_choicesString.end(),
1026 [&token](const std::string& str) -> bool
1027 {
1028 return token == str;
1029 });
1030 if (it == argObj.m_choicesString.end())
1031 {
1032 return InvalidateArgsOutOfChoice(argObj, token);
1033 }
1034 }
1035 argument->second.m_string.push_back(token);
1036 }
1037 else
1038 {
1039 return InvalidateArgsTooMany(argObj);
1040 }
1041 }
1042 else if (argument->second.m_type == ArgTypeCast::e_bool)
1043 {
1044 if (argObj.m_nargs != 0)
1045 {
1046 if (token == "True" || token == "TRUE" || token == "true")
1047 {
1048 argument->second.m_bool.push_back(true);
1049 }
1050 else if (token == "False" || token == "FALSE" || token == "false")
1051 {
1052 argument->second.m_bool.push_back(false);
1053 }
1054 else
1055 {
1056 return InvalidateArgsCannotParse(argObj, token);
1057 }
1058 }
1059 else
1060 {
1061 return InvalidateArgsTooMany(argObj);
1062 }
1063 }
1064 else if (argument->second.m_type == ArgTypeCast::e_int)
1065 {
1066 if (argObj.m_nargs != 0)
1067 {
1068 if (isNumber(token))
1069 {
1070 try
1071 {
1072 argument->second.m_int.push_back(std::stoi(token));
1073
1074 int value = argument->second.m_int.back();
1075
1076 if (argObj.m_choicesInt.size())
1077 {
1078 auto it = std::find_if(argObj.m_choicesInt.begin(), argObj.m_choicesInt.end(),
1079 [value](const int& integerValue) -> bool
1080 {
1081 return value == integerValue;
1082 });
1083 if (it == argObj.m_choicesInt.end())
1084 {
1085 return InvalidateArgsOutOfChoice(argObj, token);
1086 }
1087 }
1088 }
1089 catch (...)
1090 {
1091 return InvalidateArgsCannotParse(argObj, token);
1092 }
1093 }
1094 else
1095 {
1096 return InvalidateArgsCannotParse(argObj, token);
1097 }
1098 }
1099 else
1100 {
1101 return InvalidateArgsTooMany(argObj);
1102 }
1103 }
1104 else if (argument->second.m_type == ArgTypeCast::e_longlong)
1105 {
1106 if (argObj.m_nargs != 0)
1107 {
1108 if (isNumber(token))
1109 {
1110 try
1111 {
1112 argument->second.m_longLong.push_back(std::stoll(token));
1113
1114 long long value = argument->second.m_longLong.back();
1115
1116 if (argObj.m_choicesLongLong.size())
1117 {
1118 auto it = std::find_if(argObj.m_choicesLongLong.begin(), argObj.m_choicesLongLong.end(),
1119 [value](const long long& longLongVal) -> bool
1120 {
1121 return value == longLongVal;
1122 });
1123 if (it == argObj.m_choicesLongLong.end())
1124 {
1125 return InvalidateArgsOutOfChoice(argObj, token);
1126 }
1127 }
1128 }
1129 catch (...)
1130 {
1131 return InvalidateArgsCannotParse(argObj, token);
1132 }
1133 }
1134 else
1135 {
1136 return InvalidateArgsCannotParse(argObj, token);
1137 }
1138 }
1139 else
1140 {
1141 return InvalidateArgsTooMany(argObj);
1142 }
1143 }
1144 else if (argument->second.m_type == ArgTypeCast::e_double)
1145 {
1146 if (argObj.m_nargs != 0)
1147 {
1148 if (isNumber(token))
1149 {
1150 try
1151 {
1152 argument->second.m_double.push_back(std::stod(token));
1153
1154 double value = argument->second.m_double.back();
1155
1156 if (argObj.m_choicesDouble.size())
1157 {
1158 auto it = std::find_if(argObj.m_choicesDouble.begin(), argObj.m_choicesDouble.end(),
1159 [value](const double& doubleVal) -> bool
1160 {
1161 return std::numeric_limits<double>::epsilon() >= abs(doubleVal - value);
1162 });
1163 if (it == argObj.m_choicesDouble.end())
1164 {
1165 return InvalidateArgsOutOfChoice(argObj, token);
1166 }
1167 }
1168 }
1169 catch (...)
1170 {
1171 return InvalidateArgsCannotParse(argObj, token);
1172 }
1173 }
1174 else
1175 {
1176 return InvalidateArgsCannotParse(argObj, token);
1177 }
1178 }
1179 else
1180 {
1181 return InvalidateArgsTooMany(argObj);
1182 }
1183 }
1184
1185 argument->second.m_count += 1;
1186 return true;
1187 }
1188
1193 bool InvalidateArgsOutOfChoice(const Argument& argObj, const std::string& token)
1194 {
1195 const std::string& name = argObj.m_longName.empty() ? (argObj.m_shortName.empty() ? argObj.m_positionalName : argObj.m_shortName) : argObj.m_longName;
1196
1197 SetErrorString("Value '" + token + "' is out of choices for \"" + name + "\"");
1198
1199 return false;
1200 }
1201
1205 bool InvalidateArgsTooMany(const Argument& argObj)
1206 {
1207 const std::string& name = argObj.m_longName.empty() ? (argObj.m_shortName.empty() ? argObj.m_positionalName : argObj.m_shortName) : argObj.m_longName;
1208
1209 SetErrorString("too many arguments for \"" + name + "\"");
1210
1211 return false;
1212 }
1213
1218 bool InvalidateArgsCannotParse(const Argument& argObj, const std::string& token)
1219 {
1220 const std::string& name = argObj.m_longName.empty() ? (argObj.m_shortName.empty() ? argObj.m_positionalName : argObj.m_shortName) : argObj.m_longName;
1221
1222 SetErrorString("cannot parse [\"" + token + "\"] for argument \"" + name + "\"");
1223
1224 return false;
1225 }
1226
1227 bool m_isValid = false;
1228 std::string m_error;
1229 std::map<const size_t, ArgumentParsed> m_parsed;
1230 std::map<const std::string, size_t> m_names;
1231
1232 friend ArgumentParser;
1233 };
1234
1235
1240 {
1241 public:
1244 ArgumentParser(const std::string& name) noexcept
1245 : m_name(name)
1246 {}
1247
1251 ArgumentParser& SetDescription(const std::string& description) noexcept
1252 {
1253 m_description = description;
1254 return *this;
1255 }
1256
1260 ArgumentParser& SetAllowAbbrev(bool allowAbbrev) noexcept
1261 {
1262 m_allowAbbrev = allowAbbrev;
1263 return *this;
1264 }
1265
1270 ArgumentParser& SetIgnoreUnknownArgs(bool ignoreUnknownArgs) noexcept
1271 {
1272 m_ignoreUnknownArgs = ignoreUnknownArgs;
1273 return *this;
1274 }
1275
1279 ArgumentParser& SetAddHelp(bool addHelp) noexcept
1280 {
1281 m_addHelp = addHelp;
1282 return *this;
1283 }
1284
1285
1286
1291 ArgumentParser& SetEpilogue(const std::string& epilogue) noexcept
1292 {
1293 m_epilogue = epilogue;
1294 return *this;
1295 }
1296
1300 ArgumentParser& SetUsage(const std::string& usage) noexcept
1301 {
1302 m_usage = usage;
1303 return *this;
1304 }
1305
1311 ArgumentParser& SetPrefixChars(const char charSym) noexcept
1312 {
1313 m_prefix = charSym;
1314 return *this;
1315 }
1316
1319 void AddArgument(const Argument& arg)
1320 {
1321 if (arg.m_longName.empty() && arg.m_shortName.empty() && arg.m_positionalName.empty())
1322 {
1323 throw std::runtime_error("Short,long or positional names of argument are empty.\n"
1324 "At least one name should been specified.");
1325 }
1326 else if (!(arg.m_longName.empty() || arg.m_shortName.empty()) && !arg.m_positionalName.empty())
1327 {
1328 throw std::runtime_error("Positional argument " + arg.m_positionalName + " declared aside with short/long name\n"
1329 "Positional argument shouldn't have any short/long name.");
1330 }
1331 else if (arg.m_choicesDouble.size() + arg.m_choicesInt.size() + arg.m_choicesLongLong.size() + arg.m_choicesString.size())
1332 {
1333 if (arg.m_type == ArgTypeCast::e_bool)
1334 {
1335 throw std::runtime_error("No need to declare choice for bool type");
1336 }
1337 else if (arg.m_type == ArgTypeCast::e_int && arg.m_choicesDouble.size() + arg.m_choicesLongLong.size() + arg.m_choicesString.size())
1338 {
1339 throw std::runtime_error("Only int choices should been declared");
1340 }
1341 else if (arg.m_type == ArgTypeCast::e_longlong && arg.m_choicesDouble.size() + arg.m_choicesInt.size() + arg.m_choicesString.size())
1342 {
1343 throw std::runtime_error("Only long long choices should been declared");
1344 }
1345 else if (arg.m_type == ArgTypeCast::e_double && arg.m_choicesInt.size() + arg.m_choicesLongLong.size() + arg.m_choicesString.size())
1346 {
1347 throw std::runtime_error("Only double choices should been declared");
1348 }
1349 else if (arg.m_type == ArgTypeCast::e_String && arg.m_choicesInt.size() + arg.m_choicesLongLong.size() + arg.m_choicesDouble.size())
1350 {
1351 throw std::runtime_error("Only string choices should been declared");
1352 }
1353 }
1354 _addArg(arg);
1355 }
1356
1361 ArgumentsObject ParseArgs(const std::vector<std::string>& args)
1362 {
1363 std::string _pref{ m_prefix };
1364 std::string _doublePref{ m_prefix, m_prefix };
1365
1366 for (auto& el : m_knownArgumentNames)
1367 {
1368 if (el.second.argNameType == KnownNameType::e_Short)
1369 {
1370 m_knownArgumentNamesInternal[_pref + el.first] = el.second;
1371 }
1372 else
1373 {
1374 m_knownArgumentNamesInternal[_doublePref + el.first] = el.second;
1375 }
1376 }
1377
1378 if (m_addHelp)
1379 {
1380 bool shortHelpAlreadyExists = false, longHelpAlreadyExists = false;
1381 auto foundArgObject = m_knownArgumentNamesInternal.find(_pref + "h");
1382 shortHelpAlreadyExists = foundArgObject != m_knownArgumentNamesInternal.end();
1383 foundArgObject = m_knownArgumentNamesInternal.find(_doublePref + "help");
1384 longHelpAlreadyExists = foundArgObject != m_knownArgumentNamesInternal.end();
1385 if (!shortHelpAlreadyExists || !longHelpAlreadyExists)
1386 {
1387 Argument arg = Argument::CreateNamedArgument(shortHelpAlreadyExists ? "" : "h", longHelpAlreadyExists ? "" : "help", 0);
1388 arg.SetHelp("Show help!");
1389 arg.SetRequired(false);
1390 _addArg(arg);
1391 if (!shortHelpAlreadyExists)
1392 {
1393 m_knownArgumentNamesInternal[_pref + arg.m_shortName] = { m_arguments.size()-1, KnownNameType::e_Short };
1394 }
1395 if (!longHelpAlreadyExists)
1396 {
1397 m_knownArgumentNamesInternal[_doublePref + arg.m_longName] = { m_arguments.size()-1, KnownNameType::e_Long };
1398 }
1399 }
1400 }
1401
1402 bool positionalArgsEndFlag = false;
1403 size_t currentArgumentObjectIndex = kSizeTypeEnd;
1404 std::vector<std::string> positionalArgs;
1405 ArgumentsObject argObj;
1406 for (size_t i =0; i < args.size(); ++i)
1407 {
1408 const std::string& el = args[i];
1409 auto foundArgObject = m_knownArgumentNamesInternal.find(el);
1410 if (foundArgObject == m_knownArgumentNamesInternal.end() && m_allowAbbrev)
1411 {
1412 // SetAllowAbbrev: accept an unambiguous prefix of a long option,
1413 // e.g. "--verb" for "--verbose".
1414 bool ambiguous = false;
1415 std::string abbrev = ResolveAbbreviation(el, _doublePref, ambiguous);
1416 if (ambiguous)
1417 {
1418 argObj.SetErrorString("Ambiguous option \"" + el
1419 + "\" matches more than one argument");
1420 return argObj;
1421 }
1422 if (!abbrev.empty())
1423 {
1424 foundArgObject = m_knownArgumentNamesInternal.find(abbrev);
1425 }
1426 }
1427 if (foundArgObject != m_knownArgumentNamesInternal.end())
1428 {
1429 currentArgumentObjectIndex = foundArgObject->second.position;
1430
1431 Argument& argument = m_arguments[currentArgumentObjectIndex];
1432 if (argument.m_nargs == 0)
1433 {
1434 if (!argObj.Parse(argument, currentArgumentObjectIndex, el))
1435 {
1436 return argObj;
1437 }
1438 }
1439 else
1440 {
1441 argObj.CreateParsingStub(argument, currentArgumentObjectIndex);
1442 }
1443
1444 positionalArgsEndFlag = true;
1445 continue;
1446 }
1447 else if (!positionalArgsEndFlag)
1448 {
1449 if (isNumber(el))
1450 {
1451 positionalArgs.push_back(el);
1452 }
1453 else if (el.find(_pref) == 0 || el.find(_doublePref) == 0)
1454 {
1455 if (!_unknownArgumentHit(argObj, i+1, currentArgumentObjectIndex, positionalArgsEndFlag, el))
1456 {
1457 return argObj;
1458 }
1459 }
1460 else
1461 {
1462 positionalArgs.push_back(el);
1463 }
1464 continue;
1465 }
1466 else if (el.find(_pref) == 0 || el.find(_doublePref) == 0)
1467 {
1468 if (!_unknownArgumentHit(argObj, i+1, currentArgumentObjectIndex, positionalArgsEndFlag, el))
1469 {
1470 return argObj;
1471 }
1472 continue;
1473 }
1474
1475 if (currentArgumentObjectIndex != kSizeTypeEnd)
1476 {
1477 Argument& argument = m_arguments[currentArgumentObjectIndex];
1478
1479 if (!argObj.Parse(argument, currentArgumentObjectIndex, el))
1480 {
1481 return argObj;
1482 }
1483 }
1484 }
1485
1486 if (!positionalArgs.empty())
1487 {
1488 if (m_positionalArgumentNames.empty())
1489 {
1490 argObj.SetErrorString("Unknown positional argument:" + positionalArgs.front());
1491 return argObj;
1492 }
1493 size_t minimumRequiredPositionalCount = 0;
1494 size_t infiniteRequiredPositionalCount = 0;
1495 size_t optionalPositionalCount = 0;
1496
1497 for (auto& el : m_positionalArgumentNames)
1498 {
1499 if (m_arguments[el.positionInArguments].m_required)
1500 {
1501 minimumRequiredPositionalCount += m_arguments[el.positionInArguments].m_nargs == kFromOneToInfiniteArgCount ? 1 : m_arguments[el.positionInArguments].m_nargs;
1502 infiniteRequiredPositionalCount = m_arguments[el.positionInArguments].m_nargs == kFromOneToInfiniteArgCount;
1503 }
1504 else
1505 {
1506 ++optionalPositionalCount;
1507 }
1508 }
1509 if (minimumRequiredPositionalCount > positionalArgs.size())
1510 {
1511 argObj.SetErrorString("Too few positional arguments: required " + std::to_string(minimumRequiredPositionalCount) + " got " + std::to_string(positionalArgs.size()));
1512 return argObj;
1513 }
1514
1515 size_t totalTokensForRequiredNargs = 1;
1516 size_t additionalTokensForFirstRequiredNarg = 0;
1517 size_t howMuchOptionalArgsCanBeParsed = positionalArgs.size() - minimumRequiredPositionalCount;
1518 if (howMuchOptionalArgsCanBeParsed > optionalPositionalCount)
1519 {
1520 if (infiniteRequiredPositionalCount == 0)
1521 {
1522 argObj.SetErrorString("Too many positional arguments!");
1523 return argObj;
1524 }
1525 else
1526 {
1527 totalTokensForRequiredNargs = (howMuchOptionalArgsCanBeParsed - optionalPositionalCount) / infiniteRequiredPositionalCount;
1528 additionalTokensForFirstRequiredNarg = (howMuchOptionalArgsCanBeParsed - optionalPositionalCount) % infiniteRequiredPositionalCount;
1529 }
1530 howMuchOptionalArgsCanBeParsed = optionalPositionalCount;
1531 }
1532
1533
1534 size_t currentTokenPosition = 0;
1535 size_t optionalParsed = 0;
1536
1537 for (auto& el : m_positionalArgumentNames)
1538 {
1539 Argument& argument = m_arguments[el.positionInArguments];
1540 argObj.CreateParsingStub(argument, el.positionInArguments);
1541
1542 if (m_arguments[el.positionInArguments].m_required)
1543 {
1544
1545 if (m_arguments[el.positionInArguments].m_nargs != kFromOneToInfiniteArgCount)
1546 {
1547 for (size_t i = 0; i < static_cast<size_t>(m_arguments[el.positionInArguments].m_nargs); ++i)
1548 {
1549 if (!argObj.Parse(argument, el.positionInArguments, positionalArgs[currentTokenPosition]))
1550 {
1551 return argObj;
1552 }
1553 ++currentTokenPosition;
1554 }
1555 }
1556 else
1557 {
1558 size_t addtionalArg = 0;
1559 if (additionalTokensForFirstRequiredNarg > 0)
1560 {
1561 ++addtionalArg;
1562 --additionalTokensForFirstRequiredNarg;
1563 }
1564 for (size_t i = 0; i < totalTokensForRequiredNargs + addtionalArg; ++i)
1565 {
1566 if (!argObj.Parse(argument, el.positionInArguments, positionalArgs[currentTokenPosition]))
1567 {
1568 return argObj;
1569 }
1570 ++currentTokenPosition;
1571 }
1572 }
1573 }
1574 else if (optionalParsed <= howMuchOptionalArgsCanBeParsed)
1575 {
1576 if (!argObj.Parse(argument, el.positionInArguments, positionalArgs[currentTokenPosition]))
1577 {
1578 return argObj;
1579 }
1580 ++currentTokenPosition;
1581 ++optionalParsed;
1582 }
1583 }
1584 }
1585
1586 for (size_t i = 0; i < m_arguments.size(); ++i)
1587 {
1588 auto el = m_arguments[i];
1589 const std::string& name = el.m_longName.empty() ? (el.m_shortName.empty() ? el.m_positionalName : el.m_shortName) : el.m_longName;
1590
1591 ArgumentParsed parsedArg = argObj.GetArg(name);
1592
1593 if (parsedArg.GetArgumentExists())
1594 {
1595 if (static_cast<int>(parsedArg.GetArgumentCount()) == el.m_nargs
1596 || el.m_nargs == kAnyArgCount
1597 || (el.m_nargs == kFromOneToInfiniteArgCount && parsedArg.GetArgumentCount() >= 1))
1598 {
1599 continue;
1600 }
1601 else
1602 {
1603 argObj.SetErrorString("Wrong arguments count for argument with name \"" + name + "\" got = " + std::to_string(parsedArg.GetArgumentCount()));
1604 return argObj;
1605 }
1606 }
1607 else if (el.HasDefault())
1608 {
1609 argObj.ParseDefault(el, i);
1610 }
1611 else if (el.m_required)
1612 {
1613 argObj.SetErrorString("Required argument with name \"" + name + "\" does not exist");
1614 return argObj;
1615 }
1616 }
1617
1618 argObj.SetValid();
1619 return argObj;
1620 }
1621
1629 bool _unknownArgumentHit(ArgumentsObject& argObj, const size_t positionInInput, size_t& currentArgumentObjectIndex, bool& positionalArgsEndFlag, const std::string& el)
1630 {
1631 if (m_ignoreUnknownArgs)
1632 {
1633 currentArgumentObjectIndex = kSizeTypeEnd;
1634 positionalArgsEndFlag = true;
1635 return true;
1636 }
1637 std::stringstream ss;
1638 ss << "Unknown input argument: \"" << el << "\" at position " << positionInInput;
1639 argObj.SetErrorString(ss.str());
1640 return false;
1641 }
1642
1648 ArgumentsObject ParseArgs(const int argc, char** argv)
1649 {
1650
1651 std::vector<std::string> args;
1652 for (int i = 1; i < argc; ++i)
1653 {
1654 args.emplace_back(argv[i]);
1655 }
1656
1657 return ParseArgs(args);
1658 }
1659
1664 std::string GetHelp(size_t width = kHelpWidth, size_t nameWidthPercent = kHelpNameWidthPercent)
1665 {
1666 width = width < kHelpWidth ? kHelpWidth : width;
1667 size_t nameWidthInHelp = nameWidthPercent * width / 100;
1668 width -= nameWidthInHelp;
1669
1670
1671 // NOTE: do not initialize the stream with "usage: " -- the first
1672 // insertion below would overwrite it (the put pointer starts at 0).
1673 std::stringstream usage;
1674 if (!m_usage.empty())
1675 {
1676 // Caller-provided usage line (SetUsage) overrides the auto-generated one.
1677 usage << m_usage;
1678 }
1679 else
1680 {
1681 usage << m_name << " ";
1682 if (m_positionalArgumentNames.size())
1683 {
1684 for (auto& el : m_positionalArgumentNames)
1685 {
1686 Argument& arg = m_arguments[el.positionInArguments];
1687 MakeUsageForName(arg, usage);
1688 }
1689 }
1690 for (auto& el : m_arguments)
1691 {
1692 if (el.m_positionalName.empty())
1693 {
1694 MakeUsageForName(el, usage);
1695 }
1696 }
1697 }
1698
1699 if (!m_description.empty())
1700 {
1701 AddAdditionalDescription(usage, m_description, width+nameWidthInHelp);
1702 }
1703
1704 if (m_positionalArgumentNames.size())
1705 {
1706 usage << "\n\n" << "positional arguments:\n\n";
1707 for (auto& el : m_positionalArgumentNames)
1708 {
1709 Argument& arg = m_arguments[el.positionInArguments];
1710 MakeDescriptionForArg(arg, usage, nameWidthInHelp, width);
1711 }
1712 }
1713
1714 if (m_arguments.size() > m_positionalArgumentNames.size())
1715 {
1716 usage << "\n\n" << "named arguments:\n\n";
1717 for (auto& el : m_arguments)
1718 {
1719 if (el.m_positionalName.empty())
1720 {
1721 MakeDescriptionForArg(el, usage, nameWidthInHelp, width);
1722 }
1723 }
1724 }
1725
1726 if (!m_epilogue.empty())
1727 {
1728 AddAdditionalDescription(usage, m_epilogue, width+nameWidthInHelp);
1729 }
1730
1731 return TrimTrailingSpacesPerLine(usage.str());
1732 }
1733
1739 static std::string TrimTrailingSpacesPerLine(const std::string& text)
1740 {
1741 std::string result;
1742 result.reserve(text.size());
1743 size_t lineStart = 0;
1744 while (lineStart <= text.size())
1745 {
1746 size_t nl = text.find('\n', lineStart);
1747 size_t lineEnd = (nl == std::string::npos) ? text.size() : nl;
1748 size_t last = lineEnd;
1749 while (last > lineStart && (text[last - 1] == ' ' || text[last - 1] == '\t'))
1750 {
1751 --last;
1752 }
1753 result.append(text, lineStart, last - lineStart);
1754 if (nl == std::string::npos)
1755 {
1756 break;
1757 }
1758 result.push_back('\n');
1759 lineStart = nl + 1;
1760 }
1761 return result;
1762 }
1763
1764 private:
1765
1766
1775 std::string ResolveAbbreviation(
1776 const std::string& token, const std::string& doublePref, bool& ambiguous)
1777 {
1778 ambiguous = false;
1779 // Only long options (prefixed with the double prefix) can be abbreviated,
1780 // and the token must be a strict, non-empty prefix.
1781 if (token.size() <= doublePref.size()
1782 || token.compare(0, doublePref.size(), doublePref) != 0)
1783 {
1784 return std::string();
1785 }
1786
1787 std::string match;
1788 for (auto it = m_knownArgumentNamesInternal.begin();
1789 it != m_knownArgumentNamesInternal.end(); ++it)
1790 {
1791 if (it->second.argNameType == KnownNameType::e_Long
1792 && it->first.size() > token.size()
1793 && it->first.compare(0, token.size(), token) == 0)
1794 {
1795 if (!match.empty())
1796 {
1797 ambiguous = true;
1798 return std::string();
1799 }
1800 match = it->first;
1801 }
1802 }
1803 return match;
1804 }
1805
1809 void _addArg(const Argument& arg)
1810 {
1811 size_t currentSize = m_arguments.size();
1812 if (!arg.m_shortName.empty())
1813 {
1814 if (m_knownArgumentNames.find(arg.m_shortName) == m_knownArgumentNames.end())
1815 {
1816 m_knownArgumentNames[arg.m_shortName] = { currentSize, KnownNameType::e_Short };
1817 }
1818 else
1819 {
1820 throw std::runtime_error("Short name \"" + arg.m_shortName + "\" already exists");
1821 }
1822 }
1823 if (!arg.m_longName.empty())
1824 {
1825 if (m_knownArgumentNames.find(arg.m_longName) == m_knownArgumentNames.end())
1826 {
1827 m_knownArgumentNames[arg.m_longName] = { currentSize, KnownNameType::e_Long };
1828 }
1829 else
1830 {
1831 throw std::runtime_error("Long name \"" + arg.m_longName + "\" already exists");
1832 }
1833 }
1834 if (!arg.m_positionalName.empty())
1835 {
1836 auto it = std::find_if(m_positionalArgumentNames.begin(), m_positionalArgumentNames.end(), [&arg](PositionalNamesStruct& posarg)->bool {return posarg.argName == arg.m_positionalName; });
1837 if (it == m_positionalArgumentNames.end())
1838 {
1839 m_positionalArgumentNames.emplace_back(m_arguments.size(), arg.m_positionalName);
1840 }
1841 else if (arg.m_required && (arg.m_nargs == 0 || arg.m_nargs == kAnyArgCount))
1842 {
1843 throw std::runtime_error("Required positional argument with name \"" + arg.m_positionalName + "\" cannot be with zero count");
1844 }
1845 else if (!arg.m_required && arg.m_nargs != 1)
1846 {
1847 throw std::runtime_error("Non required positional argument with name \"" + arg.m_positionalName + "\" should be with count 1");
1848 }
1849 else
1850 {
1851 throw std::runtime_error("Positional name \"" + arg.m_positionalName + "\" already exists");
1852 }
1853 }
1854
1855 m_arguments.emplace_back(arg);
1856 }
1857
1861 void MakeUsageForName(Argument& arg, std::stringstream& usage)
1862 {
1863 if (!arg.m_required)
1864 {
1865 usage << "[";
1866 }
1867 std::stringstream showName;
1868 if (arg.m_choicesDouble.size() + arg.m_choicesInt.size() + arg.m_choicesLongLong.size() + arg.m_choicesString.size())
1869 {
1870 showName << "{";
1871 if (arg.m_type == ArgTypeCast::e_String)
1872 {
1873 MakeChoicesToString<std::string>(showName, arg.m_choicesString);
1874 }
1875 else if (arg.m_type == ArgTypeCast::e_int)
1876 {
1877 MakeChoicesToString<int>(showName, arg.m_choicesInt);
1878 }
1879 else if (arg.m_type == ArgTypeCast::e_double)
1880 {
1881 MakeChoicesToString<double>(showName, arg.m_choicesDouble);
1882 }
1883 else if (arg.m_type == ArgTypeCast::e_longlong)
1884 {
1885 MakeChoicesToString<long long>(showName, arg.m_choicesLongLong);
1886 }
1887 showName << "}";
1888 }
1889 else
1890 {
1891 if (!arg.m_positionalName.empty())
1892 {
1893 showName << arg.m_positionalName;
1894 }
1895 else if (!arg.m_shortName.empty())
1896 {
1897 showName << arg.m_shortName;
1898 }
1899 else
1900 {
1901 showName << arg.m_longName;
1902 }
1903 }
1904 if (!arg.m_positionalName.empty())
1905 {
1906 usage << showName.str();
1907 }
1908 if (!arg.m_shortName.empty())
1909 {
1910 usage << m_prefix << arg.m_shortName;
1911 }
1912
1913 if (!arg.m_longName.empty())
1914 {
1915 if (!arg.m_shortName.empty())
1916 {
1917 usage << ",";
1918 }
1919 usage << m_prefix << m_prefix << arg.m_longName;
1920 }
1921
1922 if (arg.m_nargs == kAnyArgCount)
1923 {
1924 usage << " [" << showName.str() << "[" << showName.str() << " ...]]";
1925 }
1926 else if (arg.m_nargs == kFromOneToInfiniteArgCount)
1927 {
1928 usage << " [" << showName.str() << " ...]";
1929 }
1930 else if (arg.m_nargs != 0)
1931 {
1932 usage << " [";
1933 usage << showName.str();
1934 for (size_t i = 1; i < static_cast<size_t>(arg.m_nargs); ++i)
1935 {
1936 usage << " " << showName.str();
1937 }
1938 usage << "]";
1939 }
1940 if (!arg.m_required)
1941 {
1942 usage << "]";
1943 }
1944 // Always separate tokens with exactly one trailing space. This also
1945 // keeps flags (nargs == 0) from gluing onto the next token.
1946 usage << " ";
1947 }
1948
1956 void MakeDescriptionForArg(Argument& arg, std::stringstream& description, size_t nameLen, size_t descLen)
1957 {
1958 std::stringstream showName;
1959 std::stringstream showDesc;
1960 if (!arg.m_positionalName.empty())
1961 {
1962 if (arg.m_choicesDouble.size() + arg.m_choicesInt.size() + arg.m_choicesLongLong.size() + arg.m_choicesString.size())
1963 {
1964 showName << "{";
1965 if (arg.m_type == ArgTypeCast::e_String)
1966 {
1967 MakeChoicesToString<std::string>(showName, arg.m_choicesString);
1968 }
1969 else if (arg.m_type == ArgTypeCast::e_int)
1970 {
1971 MakeChoicesToString<int>(showName, arg.m_choicesInt);
1972 }
1973 else if (arg.m_type == ArgTypeCast::e_double)
1974 {
1975 MakeChoicesToString<double>(showName, arg.m_choicesDouble);
1976 }
1977 else if (arg.m_type == ArgTypeCast::e_longlong)
1978 {
1979 MakeChoicesToString<long long>(showName, arg.m_choicesLongLong);
1980 }
1981 showName << "}";
1982 }
1983 else
1984 {
1985 showName << arg.m_positionalName;
1986 }
1987 }
1988 else if (!arg.m_shortName.empty())
1989 {
1990 showName << m_prefix << arg.m_shortName;
1991 if (!arg.m_longName.empty())
1992 {
1993 showName << "," << m_prefix << m_prefix << arg.m_longName;
1994 }
1995 }
1996 else
1997 {
1998 // Long name only: no short name, so no leading comma.
1999 showName << m_prefix << m_prefix << arg.m_longName;
2000 }
2001
2002 showDesc << arg.m_help;
2003 if (arg.m_nargs)
2004 {
2005 showDesc << (arg.m_help.empty() ? "" : " ") << "Type: " << m_enumToString[arg.m_type] << ". ";
2006 }
2007
2008 if ((!arg.m_shortName.empty() || !arg.m_longName.empty())
2009 && (arg.m_choicesDouble.size() + arg.m_choicesInt.size() + arg.m_choicesLongLong.size() + arg.m_choicesString.size()))
2010 {
2011 showDesc << " Choices:";
2012 if (arg.m_type == ArgTypeCast::e_String)
2013 {
2014 MakeChoicesToString<std::string>(showDesc, arg.m_choicesString);
2015 }
2016 else if (arg.m_type == ArgTypeCast::e_int)
2017 {
2018 MakeChoicesToString<int>(showDesc, arg.m_choicesInt);
2019 }
2020 else if (arg.m_type == ArgTypeCast::e_double)
2021 {
2022 MakeChoicesToString<double>(showDesc, arg.m_choicesDouble);
2023 }
2024 else if (arg.m_type == ArgTypeCast::e_longlong)
2025 {
2026 MakeChoicesToString<long long>(showDesc, arg.m_choicesLongLong);
2027 }
2028 showDesc << ". ";
2029 }
2030
2031 showDesc << (arg.m_nargs ? "Args count: " : "");
2032 switch (arg.m_nargs)
2033 {
2034 case kAnyArgCount:
2035 showDesc << "any. ";
2036 break;
2038 showDesc << " at least one. ";
2039 break;
2040 case 0:
2041 break;
2042 default:
2043 showDesc << arg.m_nargs << " ";
2044 break;
2045 }
2046 description << showName.str();
2047 size_t currentLen = getStringStreamLength(showName);
2048
2049 size_t spaceFillerSize = nameLen - currentLen;
2050 if (currentLen >= nameLen - 1)
2051 {
2052 description << "\n";
2053 spaceFillerSize = nameLen;
2054 }
2055
2056 // generate space after names printed
2057 std::string filler(spaceFillerSize, ' ');
2058 description << filler;
2059
2060 // generate space for new line of description
2061 currentLen = 0;
2062
2063 filler = std::string(nameLen, ' ');
2064
2065 for (std::string s; showDesc >> s; )
2066 {
2067 if (currentLen > descLen)
2068 {
2069 currentLen = 0;
2070 description << "\n" << filler;
2071 }
2072 description << s << " ";
2073 currentLen += s.size() + 1;
2074 }
2075
2076 description << "\n";
2077 }
2078
2079
2085 void AddAdditionalDescription(std::stringstream& description, const std::string& additionalDesc, size_t descLen)
2086 {
2087 description << "\n";
2088 size_t currentLen = 0;
2089 std::stringstream buffer{ additionalDesc };
2090 for (std::string s; buffer >> s; )
2091 {
2092 if (currentLen > descLen)
2093 {
2094 currentLen = 0;
2095 description << "\n";
2096 }
2097 description << s << " ";
2098 currentLen += s.size() + 1;
2099 }
2100 }
2101
2102
2108 template<typename T>
2109 void MakeChoicesToString(std::stringstream& outSstream, std::vector<T>& choices)
2110 {
2111 outSstream << choices.front();
2112 for (size_t i = 1; i < choices.size(); ++i)
2113 {
2114 outSstream << ", " << choices[i];
2115 }
2116 }
2117
2118 private:
2119 enum class KnownNameType :int
2120 {
2121 e_Short,
2122 e_Long
2123 };
2124 struct KnownNamesStruct
2125 {
2126 size_t position;
2127 KnownNameType argNameType;
2128 };
2129
2130 struct PositionalNamesStruct
2131 {
2132 PositionalNamesStruct(const size_t position, const std::string& name)
2133 :positionInArguments(position)
2134 , argName(name)
2135 {}
2136 size_t positionInArguments;
2137 std::string argName;
2138 };
2139
2140 private:
2142 bool m_allowAbbrev = true;
2144 bool m_addHelp = true;
2146 bool m_ignoreUnknownArgs = false;
2148 char m_prefix = '-';
2149
2152 std::string m_name;
2155 std::string m_description{ "" };
2158 std::string m_epilogue{ "" };
2161 std::string m_usage{ "" };
2163 std::vector<Argument> m_arguments;
2165 std::vector<PositionalNamesStruct> m_positionalArgumentNames;
2167 std::map<std::string, KnownNamesStruct> m_knownArgumentNames;
2170 std::map<std::string, KnownNamesStruct> m_knownArgumentNamesInternal;
2171
2172 std::map<const ArgTypeCast, const std::string> m_enumToString
2173 {
2174 {ArgTypeCast::e_String, "STRING" },
2175 {ArgTypeCast::e_int, "INT" },
2176 {ArgTypeCast::e_longlong, "LONG_LONG"},
2177 {ArgTypeCast::e_double, "DOUBLE"},
2178 {ArgTypeCast::e_bool, "BOOL"}
2179 };
2180 };
2181}
This class represents argument configuration which should be passed to ArgumentParser objects instanc...
Definition argparse.h:126
Argument & SetChoices(const std::vector< std::string > &choices)
Handy setter of valid choices for arguments with string type.
Definition argparse.h:326
std::string m_shortName
arguments short name for non positional argument only shot name used in input with 1 prefix
Definition argparse.h:276
Argument & SetDefault(long long defaultArg)
Handy setter for single default argument of long long type.
Definition argparse.h:428
std::vector< int > m_choicesInt
vector of integers to validate arguments input data.
Definition argparse.h:348
Argument & SetDefault(bool defaultArg)
Handy setter for single default argument of bool type.
Definition argparse.h:400
Argument & SetLongName(const std::string &name)
Handy setter for long named argument.
Definition argparse.h:300
Argument & SetShortName(const std::string &name)
Handy setter for short named argument.
Definition argparse.h:283
Argument & SetAnyNumberOfArgumentsButAtLeastOne()
Handy setter for argument count with self declared name.
Definition argparse.h:224
Argument & SetDefault(const std::vector< int > &defaultArg)
Handy setter for vector of default arguments of int type.
Definition argparse.h:484
static Argument CreatePositionalArgument(const std::string &positionalName="", const int argsCount=1, ArgTypeCast argType=ArgTypeCast::e_String, const bool required=true, const std::string &help="")
Default function for named arguments.
Definition argparse.h:175
std::vector< long long > m_choicesLongLong
vector of long longs to validate arguments input data.
Definition argparse.h:365
Argument & SetHelp(const std::string &help)
Handy setter for additional help.
Definition argparse.h:313
Argument & SetDefault(std::string defaultArg)
Handy setter for single default argument of string type.
Definition argparse.h:456
Argument & SetArgumentIsFlag()
Handy setter for argument which is actually a flag (e.g.
Definition argparse.h:240
Argument & SetDefault(const std::vector< bool > &defaultArg)
Handy setter for vector of default arguments of bool type.
Definition argparse.h:470
Argument & SetAnyNumberOfArguments()
Handy setter for argument count with self declared name.
Definition argparse.h:232
Argument & SetDefault(const std::vector< std::string > &defaultArg)
Handy setter for vector of default arguments of string type.
Definition argparse.h:526
Argument & SetDefault(const std::vector< double > &defaultArg)
Handy setter for vector of default arguments of double type.
Definition argparse.h:512
std::vector< std::string > m_choicesString
vector of strings to validate arguments input data.
Definition argparse.h:321
std::string m_help
additional help info for argument Will be part of generated help
Definition argparse.h:308
Argument & SetPositionalName(const std::string &name)
Handy setter for positional argument.
Definition argparse.h:267
ArgTypeCast m_type
Variable that hold type of argument.
Definition argparse.h:248
std::string m_positionalName
name of positional argument positional arguments name used only to access desired argument from code
Definition argparse.h:262
Argument & SetChoices(std::initializer_list< const char * > choices)
Overload so a braced list of string literals – e.g.
Definition argparse.h:341
Argument & SetNumberOfArguments(int amount)
setter function for m_nargs with desired amount
Definition argparse.h:216
Argument & SetChoices(const std::vector< double > &choices)
Handy setter of valid choices for arguments with double type.
Definition argparse.h:387
Argument & SetDefault(double defaultArg)
Handy setter for single default argument of double type.
Definition argparse.h:442
bool m_required
required flag argument If required argument is not set in command line then parsing will fail.
Definition argparse.h:193
Argument & SetDefault(int defaultArg)
Handy setter for single default argument of int type.
Definition argparse.h:414
int m_nargs
variable that indicates count of argument in input use "kAnyArgCount" or "kFromOneToInfiniteArgCount"...
Definition argparse.h:210
Argument & SetChoices(const std::vector< long long > &choices)
Handy setter of valid choices for arguments with long long type.
Definition argparse.h:370
Argument & SetChoices(const std::vector< int > &choices)
Handy setter of valid choices for arguments with int type.
Definition argparse.h:353
bool HasDefault() const
Getter to indicate does argument has any default value.
Definition argparse.h:540
static Argument CreateNamedArgument(const std::string &shortName="", const std::string &longName="", const int argsCount=1, ArgTypeCast argType=ArgTypeCast::e_String, const bool required=true, const std::string &help="")
Default constructor positional arguments.
Definition argparse.h:159
Argument & SetRequired(bool required)
Setter function to flag,.
Definition argparse.h:198
Argument & SetType(ArgTypeCast argType)
Setter function for type of current argument.
Definition argparse.h:254
std::string m_longName
arguments long name.
Definition argparse.h:292
std::vector< double > m_choicesDouble
vector of double to validate arguments input data.
Definition argparse.h:382
Argument & SetDefault(const std::vector< long long > &defaultArg)
Handy setter for vector of default arguments of long long type.
Definition argparse.h:498
Class which represent actual parsed argument in case of successfully parsing.
Definition argparse.h:649
std::vector< int > m_int
container of parse int arguments
Definition argparse.h:834
std::vector< double > GetAsVecDouble() const
Get result as vector double for double type arguments.
Definition argparse.h:745
std::vector< long long > m_longLong
container of parsed long long arguments
Definition argparse.h:836
std::vector< double > m_double
container of parsed double arguments
Definition argparse.h:838
int GetAsInt() const
Get result as single int for int type arguments.
Definition argparse.h:679
double GetAsDouble() const
Get result as single double for double type arguments.
Definition argparse.h:700
size_t GetArgumentCount()
get actual count of argument, in case of various arguments count.
Definition argparse.h:660
static void ThrowIfEmpty(bool empty, const char *getter)
Guard for the scalar getters.
Definition argparse.h:815
bool GetAsBool() const
Get result as single bool for bool type arguments.
Definition argparse.h:669
std::vector< long long > GetAsVecLongLong() const
Get result as vector long long for long long type arguments.
Definition argparse.h:737
std::vector< bool > m_bool
container of parsed bool arguments
Definition argparse.h:832
std::vector< std::string > GetAsVecString() const
Get result as vector string for string type arguments.
Definition argparse.h:753
std::vector< int > GetAsVecInt() const
Get result as vector int for int type arguments.
Definition argparse.h:729
std::string GetAsString() const
Get result as single string for string type arguments.
Definition argparse.h:711
bool GetArgumentExists()
Does argument exists.
Definition argparse.h:653
std::vector< bool > GetAsVecBool() const
Get result as vector bool for bool type arguments.
Definition argparse.h:721
long long GetAsLongLong() const
Get result as single long long for long long type arguments.
Definition argparse.h:689
bool m_exists
flag about is argument exists
Definition argparse.h:825
ArgTypeCast m_type
type of argument
Definition argparse.h:827
size_t m_count
count of arguments properties
Definition argparse.h:829
std::vector< std::string > m_string
container of parsed string arguments
Definition argparse.h:840
Main class of argument parser hold all user arguments from code and orchestrate other classes in orde...
Definition argparse.h:1240
std::string GetHelp(size_t width=kHelpWidth, size_t nameWidthPercent=kHelpNameWidthPercent)
Function to get help string.
Definition argparse.h:1664
ArgumentsObject ParseArgs(const int argc, char **argv)
This function just converts argc and argv to vector of token.
Definition argparse.h:1648
ArgumentParser & SetDescription(const std::string &description) noexcept
Overload default description for auto-generated command line.
Definition argparse.h:1251
ArgumentParser & SetPrefixChars(const char charSym) noexcept
Function to override default prefix.
Definition argparse.h:1311
ArgumentParser(const std::string &name) noexcept
Constructor for ArgumentParser.
Definition argparse.h:1244
void AddArgument(const Argument &arg)
Function to add arguments specification to command line parser.
Definition argparse.h:1319
ArgumentParser & SetIgnoreUnknownArgs(bool ignoreUnknownArgs) noexcept
Setter to ignore unknown argument while parsing.
Definition argparse.h:1270
ArgumentParser & SetAllowAbbrev(bool allowAbbrev) noexcept
Allows long options to be abbreviated if the abbreviation is unambiguous.
Definition argparse.h:1260
static std::string TrimTrailingSpacesPerLine(const std::string &text)
Removes trailing spaces/tabs from every line of the help text.
Definition argparse.h:1739
ArgumentsObject ParseArgs(const std::vector< std::string > &args)
Main function of parsing argument.
Definition argparse.h:1361
ArgumentParser & SetUsage(const std::string &usage) noexcept
Program usage examples.
Definition argparse.h:1300
bool _unknownArgumentHit(ArgumentsObject &argObj, const size_t positionInInput, size_t &currentArgumentObjectIndex, bool &positionalArgsEndFlag, const std::string &el)
function which resolves unknown arguments presence
Definition argparse.h:1629
ArgumentParser & SetAddHelp(bool addHelp) noexcept
Add a - h / –help option to the parser.
Definition argparse.h:1279
ArgumentParser & SetEpilogue(const std::string &epilogue) noexcept
This function allows to set final message after program description and before argument list descript...
Definition argparse.h:1291
Class that carries result of real parsing Indicates if parsing is successful and allows to get Argume...
Definition argparse.h:849
const std::string & GetErrorString()
Function to get error message in case of parsing failure.
Definition argparse.h:860
size_t ParsedArgsCount() const
I don't know when you could need this info.
Definition argparse.h:867
bool IsArgValid() const
Indicates if parsing was successful.
Definition argparse.h:853
ArgumentParsed GetArg(const std::string &name)
Getter function to get ArgumentParsed object.
Definition argparse.h:875
namespace of argument parser constants and Classes can be changed if ARGPARSE_NAMESPACE_NAME macro sp...
Definition argparse.h:51
const int kFromOneToInfiniteArgCount
constant to indicate arguments with various count from 1 to infinite
Definition argparse.h:117
ArgTypeCast
Supported types for argument If needed type is not in this list, then just use e_String.
Definition argparse.h:104
Argument CreatePositionalArgument(const std::string &positionalName="", const int argsCount=1, ArgTypeCast argType=ArgTypeCast::e_String, const bool required=true, const std::string &help="")
Helper function to create positional argument.
Definition argparse.h:587
Argument CreateNamedArgument(const std::string &shortName="", const std::string &longName="", const int argsCount=1, ArgTypeCast argType=ArgTypeCast::e_String, const bool required=true, const std::string &help="")
Helper function to create named argument.
Definition argparse.h:568
const int kAnyArgCount
constant to indicate arguments with various count from 0 to infinite
Definition argparse.h:114
Aggregate description of a named argument, for keyword-style construction.
Definition argparse.h:609
Aggregate description of a positional argument, for keyword-style construction with C++20 designated ...
Definition argparse.h:630