argagg
argagg.hpp
Go to the documentation of this file.
1 /*
2  * @file
3  * @brief
4  * Defines a very simple command line argument parser.
5  *
6  * @copyright
7  * Copyright (c) 2017 Viet The Nguyen
8  *
9  * @copyright
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to
12  * deal in the Software without restriction, including without limitation the
13  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
14  * sell copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * @copyright
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * @copyright
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
28  * IN THE SOFTWARE.
29  */
30 #pragma once
31 #ifndef ARGAGG_ARGAGG_ARGAGG_HPP
32 #define ARGAGG_ARGAGG_ARGAGG_HPP
33 
34 #ifdef __unix__
35 #include <stdio.h>
36 #include <unistd.h>
37 #endif // #ifdef __unix__
38 
39 #include <algorithm>
40 #include <array>
41 #include <cstdlib>
42 #include <cstring>
43 #include <cctype>
44 #include <iterator>
45 #include <ostream>
46 #include <sstream>
47 #include <stdexcept>
48 #include <string>
49 #include <unordered_map>
50 #include <utility>
51 #include <vector>
52 
53 
101 namespace argagg {
102 
103 
110 : public std::runtime_error {
112 };
113 
114 
122 : public std::runtime_error {
124 };
125 
126 
134 : public std::runtime_error {
136 };
137 
138 
147 : public std::runtime_error {
149 };
150 
151 
158 namespace convert {
159 
165  template <typename T>
166  T arg(const char* arg);
167 
168 }
169 
170 
179 
185  const char* arg;
186 
196  template <typename T>
197  T as() const;
198 
208  template <typename T>
209  T as(const T& t) const;
210 
223  template <typename T>
224  operator T () const;
225 
226 };
227 
228 
240 
246 
251  std::size_t count() const;
252 
258 
263  const option_result& operator [] (std::size_t index) const;
264 
274  template <typename T>
275  T as() const;
276 
285  template <typename T>
286  T as(const T& t) const;
287 
300  template <typename T>
301  operator T () const;
302 
303 };
304 
305 
312 
318  const char* program;
319 
326 
332 
337  bool has_option(const std::string& name) const;
338 
346 
353  const option_results& operator [] (const std::string& name) const;
354 
359  std::size_t count() const;
360 
365  const char* operator [] (std::size_t index) const;
366 
371  template <typename T>
372  T as(std::size_t i = 0) const;
373 
378  template <typename T>
379  std::vector<T> all_as() const;
380 
381 };
382 
383 
388 struct definition {
389 
395 
402 
408 
415  unsigned int num_args;
416 
421  bool wants_no_arguments() const;
422 
427  bool requires_arguments() const;
428 
429 };
430 
431 
440  const char* s);
441 
442 
449  const char* s);
450 
451 
457 bool flag_is_short(
458  const char* s);
459 
460 
472 struct parser_map {
473 
480 
487 
492  bool known_short_flag(
493  const char flag) const;
494 
501  const char flag) const;
502 
507  bool known_long_flag(
508  const std::string& flag) const;
509 
516  const std::string& flag) const;
517 
518 };
519 
520 
529  const std::vector<definition>& definitions);
530 
531 
536 struct parser {
537 
544 
554  parser_results parse(int argc, const char** argv) const;
555 
563  parser_results parse(int argc, char** argv) const;
564 
565 };
566 
567 
592 
599 
606 
613  ~fmt_ostream();
614 
615 };
616 
617 
634 
635 
636 } // namespace argagg
637 
638 
644 
645 
646 // ---- end of declarations, header-only implementations follow ----
647 
648 
649 namespace argagg {
650 
651 
652 template <typename T>
654 {
655  if (this->arg) {
656  return convert::arg<T>(this->arg);
657  } else {
658  throw option_lacks_argument_error("option has no argument");
659  }
660 }
661 
662 
663 template <typename T>
664 T option_result::as(const T& t) const
665 {
666  if (this->arg) {
667  try {
668  return convert::arg<T>(this->arg);
669  } catch (...) {
670  return t;
671  }
672  } else {
673  // I actually think this will never happen. To call this method you have
674  // to access a specific option_result for an option. If there's a
675  // specific option_result then the option was found. If the option
676  // requires an argument then it will definitely have an argument
677  // otherwise the parser would have complained.
678  return t;
679  }
680 }
681 
682 
683 template <typename T>
684 option_result::operator T () const
685 {
686  return this->as<T>();
687 }
688 
689 
690 template <> inline
691 option_result::operator bool () const
692 {
693  return this->arg != nullptr;
694 }
695 
696 
697 inline
699 {
700  return this->all.size();
701 }
702 
703 
704 inline
706 {
707  return this->all[index];
708 }
709 
710 
711 inline
713 {
714  return this->all[index];
715 }
716 
717 
718 template <typename T>
720 {
721  if (this->all.size() == 0) {
722  throw std::out_of_range("no option arguments to convert");
723  }
724  return this->all.back().as<T>();
725 }
726 
727 
728 template <typename T>
729 T option_results::as(const T& t) const
730 {
731  if (this->all.size() == 0) {
732  return t;
733  }
734  return this->all.back().as<T>(t);
735 }
736 
737 
738 template <typename T>
739 option_results::operator T () const
740 {
741  return this->as<T>();
742 }
743 
744 
745 template <> inline
746 option_results::operator bool () const
747 {
748  return this->all.size() > 0;
749 }
750 
751 
752 inline
754 {
755  const auto it = this->options.find(name);
756  return ( it != this->options.end()) && it->second.all.size() > 0;
757 }
758 
759 
760 inline
762 {
763  return this->options.at(name);
764 }
765 
766 
767 inline
768 const option_results&
770 {
771  return this->options.at(name);
772 }
773 
774 
775 inline
777 {
778  return this->pos.size();
779 }
780 
781 
782 inline
784 {
785  return this->pos[index];
786 }
787 
788 
789 template <typename T>
791 {
792  return convert::arg<T>(this->pos[i]);
793 }
794 
795 
796 template <typename T>
798 {
799  std::vector<T> v(this->pos.size());
801  this->pos.begin(), this->pos.end(), v.begin(),
802  [](const char* arg) {
803  return convert::arg<T>(arg);
804  });
805  return v;
806 }
807 
808 
809 inline
811 {
812  return this->num_args == 0;
813 }
814 
815 
816 inline
818 {
819  return this->num_args > 0;
820 }
821 
822 
823 inline
825  const char* s)
826 {
827  auto len = std::strlen(s);
828 
829  // The shortest possible flag has two characters: a hyphen and an
830  // alpha-numeric character.
831  if (len < 2) {
832  return false;
833  }
834 
835  // All flags must start with a hyphen.
836  if (s[0] != '-') {
837  return false;
838  }
839 
840  // Shift the name forward by a character to account for the initial hyphen.
841  // This means if s was originally "-v" then name will be "v".
842  const char* name = s + 1;
843 
844  // Check if we're dealing with a long flag.
845  bool is_long = false;
846  if (s[1] == '-') {
847  is_long = true;
848 
849  // Just -- is not a valid flag.
850  if (len == 2) {
851  return false;
852  }
853 
854  // Shift the name forward to account for the extra hyphen. This means if s
855  // was originally "--output" then name will be "output".
856  name = s + 2;
857  }
858 
859  // The first character of the flag name must be alpha-numeric. This is to
860  // prevent things like "---a" from being valid flags.
861  len = std::strlen(name);
862  if (!std::isalnum(name[0])) {
863  return false;
864  }
865 
866  // At this point in is_valid_flag_definition() we would check if the short
867  // flag has only one character. At command line specification you can group
868  // short flags together or even add an argument to a short flag without a
869  // space delimiter. Thus we don't check if this has only one character
870  // because it might not.
871 
872  // If this is a long flag then we expect all characters *up to* an equal sign
873  // to be alpha-numeric or a hyphen. After the equal sign you are specify the
874  // argument to a long flag which can be basically anything.
875  if (is_long) {
876  bool encountered_equal = false;
877  return std::all_of(name, name + len, [&](const char& c) {
878  if (encountered_equal) {
879  return true;
880  } else {
881  if (c == '=') {
882  encountered_equal = true;
883  return true;
884  }
885  return std::isalnum(c) || c == '-';
886  }
887  });
888  }
889 
890  // At this point we are not dealing with a long flag. We already checked that
891  // the first character is alpha-numeric so we've got the case of a single
892  // short flag covered. This might be a short flag group though and we might
893  // be tempted to check that each character of the short flag group is
894  // alpha-numeric. However, you can specify the argument for a short flag
895  // without a space delimiter (e.g. "-I/usr/local/include") so you can't tell
896  // if the rest of a short flag group is part of the argument or not unless
897  // you know what is a defined flag or not. We leave that kind of processing
898  // to the parser.
899  return true;
900 }
901 
902 
903 inline
905  const char* s)
906 {
907  auto len = std::strlen(s);
908 
909  // The shortest possible flag has two characters: a hyphen and an
910  // alpha-numeric character.
911  if (len < 2) {
912  return false;
913  }
914 
915  // All flags must start with a hyphen.
916  if (s[0] != '-') {
917  return false;
918  }
919 
920  // Shift the name forward by a character to account for the initial hyphen.
921  // This means if s was originally "-v" then name will be "v".
922  const char* name = s + 1;
923 
924  // Check if we're dealing with a long flag.
925  bool is_long = false;
926  if (s[1] == '-') {
927  is_long = true;
928 
929  // Just -- is not a valid flag.
930  if (len == 2) {
931  return false;
932  }
933 
934  // Shift the name forward to account for the extra hyphen. This means if s
935  // was originally "--output" then name will be "output".
936  name = s + 2;
937  }
938 
939  // The first character of the flag name must be alpha-numeric. This is to
940  // prevent things like "---a" from being valid flags.
941  len = std::strlen(name);
942  if (!std::isalnum(name[0])) {
943  return false;
944  }
945 
946  // If this is a short flag then it must only have one character.
947  if (!is_long && len > 1) {
948  return false;
949  }
950 
951  // The rest of the characters must be alpha-numeric, but long flags are
952  // allowed to have hyphens too.
953  return std::all_of(name + 1, name + len, [&](const char& c) {
954  return std::isalnum(c) || (c == '-' && is_long);
955  });
956 }
957 
958 
959 inline
961  const char* s)
962 {
963  return s[0] == '-' && std::isalnum(s[1]);
964 }
965 
966 
967 inline
969  const char flag) const
970 {
971  return this->short_map[flag] != nullptr;
972 }
973 
974 
975 inline
977  const char flag) const
978 {
979  return this->short_map[flag];
980 }
981 
982 
983 inline
985  const std::string& flag) const
986 {
987  const auto existing_long_flag = this->long_map.find(flag);
988  return existing_long_flag != long_map.end();
989 }
990 
991 
992 inline
994  const std::string& flag) const
995 {
996  const auto existing_long_flag = this->long_map.find(flag);
997  if (existing_long_flag == long_map.end()) {
998  return nullptr;
999  }
1000  return existing_long_flag->second;
1001 }
1002 
1003 
1004 inline
1006  const std::vector<definition>& definitions)
1007 {
1009  parser_map map {{{nullptr}}, std::move(long_map)};
1010 
1011  for (auto& defn : definitions) {
1012 
1013  if (defn.flags.size() == 0) {
1014  std::ostringstream msg;
1015  msg << "option \"" << defn.name << "\" has no flag definitions";
1016  throw invalid_flag(msg.str());
1017  }
1018 
1019  for (auto& flag : defn.flags) {
1020 
1021  if (!is_valid_flag_definition(flag.data())) {
1022  std::ostringstream msg;
1023  msg << "flag \"" << flag << "\" specified for option \"" << defn.name
1024  << "\" is invalid";
1025  throw invalid_flag(msg.str());
1026  }
1027 
1028  if (flag_is_short(flag.data())) {
1029  const int short_flag_letter = flag[1];
1030  const auto existing_short_flag = map.short_map[short_flag_letter];
1031  bool short_flag_already_exists = (existing_short_flag != nullptr);
1032  if (short_flag_already_exists) {
1033  std::ostringstream msg;
1034  msg << "duplicate short flag \"" << flag
1035  << "\" found, specified by both option \"" << defn.name
1036  << "\" and option \"" << existing_short_flag->name;
1037  throw invalid_flag(msg.str());
1038  }
1039  map.short_map[short_flag_letter] = &defn;
1040  continue;
1041  }
1042 
1043  // If we're here then this is a valid, long-style flag.
1044  if (map.known_long_flag(flag)) {
1045  const auto existing_long_flag = map.get_definition_for_long_flag(flag);
1046  std::ostringstream msg;
1047  msg << "duplicate long flag \"" << flag
1048  << "\" found, specified by both option \"" << defn.name
1049  << "\" and option \"" << existing_long_flag->name;
1050  throw invalid_flag(msg.str());
1051  }
1052  map.long_map.insert(std::make_pair(flag, &defn));
1053  }
1054  }
1055 
1056  return map;
1057 }
1058 
1059 
1060 inline
1061 parser_results parser::parse(int argc, const char** argv) const
1062 {
1063  // Inspect each definition to see if its valid. You may wonder "why don't
1064  // you do this validation on construction?" I had thought about it but
1065  // realized that since I've made the parser an aggregate type (granted it
1066  // just "aggregates" a single vector) I would need to track any changes to
1067  // the definitions vector and re-run the validity check in order to
1068  // maintain this expected "validity invariant" on the object. That would
1069  // then require hiding the definitions vector as a private entry and then
1070  // turning the parser into a thin interface (by re-exposing setters and
1071  // getters) to the vector methods just so that I can catch when the
1072  // definition has been modified. It seems much simpler to just enforce the
1073  // validity when you actually want to parser because it's at the moment of
1074  // parsing that you know the definitions are complete.
1076 
1077  // Initialize the parser results that we'll be returning. Store the program
1078  // name (assumed to be the first command line argument) and initialize
1079  // everything else as empty.
1082  parser_results results {argv[0], std::move(options), std::move(pos)};
1083 
1084  // Add an empty option result for each definition.
1085  for (const auto& defn : this->definitions) {
1086  option_results opt_results {{}};
1087  results.options.insert(
1088  std::make_pair(defn.name, opt_results));
1089  }
1090 
1091  // Don't start off ignoring flags. We only ignore flags after a -- shows up
1092  // in the command line arguments.
1093  bool ignore_flags = false;
1094 
1095  // Keep track of any options that are expecting arguments.
1096  const char* last_flag_expecting_args = nullptr;
1097  option_result* last_option_expecting_args = nullptr;
1098  unsigned int num_option_args_to_consume = 0;
1099 
1100  // Get pointers to pointers so we can treat the raw pointer array as an
1101  // iterator for standard library algorithms. This isn't used yet but can be
1102  // used to template this function to work on iterators over strings or
1103  // C-strings.
1104  const char** arg_i = argv + 1;
1105  const char** arg_end = argv + argc;
1106 
1107  while (arg_i != arg_end) {
1108  auto arg_i_cstr = *arg_i;
1109  auto arg_i_len = std::strlen(arg_i_cstr);
1110 
1111  // Some behavior to note: if the previous option is expecting an argument
1112  // then the next entry will be treated as a positional argument even if
1113  // it looks like a flag.
1114  bool treat_as_positional_argument = (
1115  ignore_flags
1116  || num_option_args_to_consume > 0
1117  || !cmd_line_arg_is_option_flag(arg_i_cstr)
1118  );
1119  if (treat_as_positional_argument) {
1120 
1121  // If last option is expecting some specific positive number of
1122  // arguments then give this argument to that option, *regardless of
1123  // whether or not the argument looks like a flag or is the special "--"
1124  // argument*.
1125  if (num_option_args_to_consume > 0) {
1126  last_option_expecting_args->arg = arg_i_cstr;
1127  --num_option_args_to_consume;
1128  ++arg_i;
1129  continue;
1130  }
1131 
1132  // Now we check if this is just "--" which is a special argument that
1133  // causes all following arguments to be treated as non-options and is
1134  // itselve discarded.
1135  if (std::strncmp(arg_i_cstr, "--", 2) == 0 && arg_i_len == 2) {
1136  ignore_flags = true;
1137  ++arg_i;
1138  continue;
1139  }
1140 
1141  // If there are no expectations for option arguments then simply use
1142  // this argument as a positional argument.
1143  results.pos.push_back(arg_i_cstr);
1144  ++arg_i;
1145  continue;
1146  }
1147 
1148  // Reset the "expecting argument" state.
1149  last_flag_expecting_args = nullptr;
1150  last_option_expecting_args = nullptr;
1151  num_option_args_to_consume = 0;
1152 
1153  // If we're at this point then we're definitely dealing with something
1154  // that is flag-like and has hyphen as the first character and has a
1155  // length of at least two characters. How we handle this potential flag
1156  // depends on whether or not it is a long-option so we check that first.
1157  bool is_long_flag = (arg_i_cstr[1] == '-');
1158 
1159  if (is_long_flag) {
1160 
1161  // Long flags have a complication: their arguments can be specified
1162  // using an '=' character right inside the argument. That means an
1163  // argument like "--output=foobar.txt" is actually an option with flag
1164  // "--output" and argument "foobar.txt". So we look for the first
1165  // instance of the '=' character and keep it in long_flag_arg. If
1166  // long_flag_arg is nullptr then we didn't find '='. We need the
1167  // flag_len to construct long_flag_str below.
1168  auto long_flag_arg = std::strchr(arg_i_cstr, '=');
1169  std::size_t flag_len = arg_i_len;
1170  if (long_flag_arg != nullptr) {
1171  flag_len = long_flag_arg - arg_i_cstr;
1172  }
1173  std::string long_flag_str(arg_i_cstr, flag_len);
1174 
1175  if (!map.known_long_flag(long_flag_str)) {
1176  std::ostringstream msg;
1177  msg << "found unexpected flag: " << long_flag_str;
1178  throw unexpected_option_error(msg.str());
1179  }
1180 
1181  const auto defn = map.get_definition_for_long_flag(long_flag_str);
1182 
1183  if (long_flag_arg != nullptr && defn->num_args == 0) {
1184  std::ostringstream msg;
1185  msg << "found argument for option not expecting an argument: "
1186  << arg_i_cstr;
1187  throw unexpected_argument_error(msg.str());
1188  }
1189 
1190  // We've got a legitimate, known long flag option so we add an option
1191  // result. This option result initially has an arg of nullptr, but that
1192  // might change in the following block.
1193  auto& opt_results = results.options[defn->name];
1194  option_result opt_result {nullptr};
1195  opt_results.all.push_back(std::move(opt_result));
1196 
1197  if (defn->requires_arguments()) {
1198  bool there_is_an_equal_delimited_arg = (long_flag_arg != nullptr);
1199  if (there_is_an_equal_delimited_arg) {
1200  // long_flag_arg would be "=foo" in the "--output=foo" case so we
1201  // increment by 1 to get rid of the equal sign.
1202  opt_results.all.back().arg = long_flag_arg + 1;
1203  } else {
1204  last_flag_expecting_args = arg_i_cstr;
1205  last_option_expecting_args = &(opt_results.all.back());
1206  num_option_args_to_consume = defn->num_args;
1207  }
1208  }
1209 
1210  ++arg_i;
1211  continue;
1212  }
1213 
1214  // If we've made it here then we're looking at either a short flag or a
1215  // group of short flags. Short flags can be grouped together so long as
1216  // they don't require any arguments unless the option that does is the
1217  // last in the group ("-o x -v" is okay, "-vo x" is okay, "-ov x" is
1218  // not). So starting after the dash we're going to process each character
1219  // as if it were a separate flag. Note "sf_idx" stands for "short flag
1220  // index".
1221  for (std::size_t sf_idx = 1; sf_idx < arg_i_len; ++sf_idx) {
1222  const auto short_flag = arg_i_cstr[sf_idx];
1223 
1224  if (!std::isalnum(short_flag)) {
1225  std::ostringstream msg;
1226  msg << "found non-alphanumeric character '" << arg_i_cstr[sf_idx]
1227  << "' in flag group '" << arg_i_cstr << "'";
1228  throw std::domain_error(msg.str());
1229  }
1230 
1231  if (!map.known_short_flag(short_flag)) {
1232  std::ostringstream msg;
1233  msg << "found unexpected flag '" << arg_i_cstr[sf_idx]
1234  << "' in flag group '" << arg_i_cstr << "'";
1235  throw unexpected_option_error(msg.str());
1236  }
1237 
1238  auto defn = map.get_definition_for_short_flag(short_flag);
1239  auto& opt_results = results.options[defn->name];
1240 
1241  // Create an option result with an empty argument (for now) and add it
1242  // to this option's results.
1243  option_result opt_result {nullptr};
1244  opt_results.all.push_back(std::move(opt_result));
1245 
1246  if (defn->requires_arguments()) {
1247 
1248  // If this short flag's option requires an argument and we're the
1249  // last flag in the short flag group then just put the parser into
1250  // "expecting argument for last option" state and move onto the next
1251  // command line argument.
1252  bool is_last_short_flag_in_group = (sf_idx == arg_i_len - 1);
1253  if (is_last_short_flag_in_group) {
1254  last_flag_expecting_args = arg_i_cstr;
1255  last_option_expecting_args = &(opt_results.all.back());
1256  num_option_args_to_consume = defn->num_args;
1257  break;
1258  }
1259 
1260  // If this short flag's option requires an argument and we're NOT the
1261  // last flag in the short flag group then we automatically consume
1262  // the rest of the short flag group as the argument for this flag.
1263  // This is how we get the POSIX behavior of being able to specify a
1264  // flag's arguments without a white space delimiter (e.g.
1265  // "-I/usr/local/include").
1266  opt_results.all.back().arg = arg_i_cstr + sf_idx + 1;
1267  break;
1268  }
1269  }
1270 
1271  ++arg_i;
1272  continue;
1273  }
1274 
1275  // If we're done with all of the arguments but are still expecting
1276  // arguments for a previous option then we haven't satisfied that option.
1277  // This is an error.
1278  if (num_option_args_to_consume > 0) {
1279  std::ostringstream msg;
1280  msg << "last option \"" << last_flag_expecting_args
1281  << "\" expects an argument but the parser ran out of command line "
1282  << "arguments to parse";
1283  throw option_lacks_argument_error(msg.str());
1284  }
1285 
1286  return results;
1287 }
1288 
1289 
1290 inline
1291 parser_results parser::parse(int argc, char** argv) const
1292 {
1293  return parse(argc, const_cast<const char**>(argv));
1294 }
1295 
1296 
1297 namespace convert {
1298 
1299 
1306  template <typename T> inline
1307  T long_(const char* arg)
1308  {
1309  char* endptr = nullptr;
1310  errno = 0;
1311  T ret = static_cast<T>(std::strtol(arg, &endptr, 0));
1312  if (endptr == arg) {
1313  std::ostringstream msg;
1314  msg << "unable to convert argument to integer: \"" << arg << "\"";
1315  throw std::invalid_argument(msg.str());
1316  }
1317  if (errno == ERANGE) {
1318  throw std::out_of_range("argument numeric value out of range");
1319  }
1320  return ret;
1321  }
1322 
1323 
1330  template <typename T> inline
1331  T long_long_(const char* arg)
1332  {
1333  char* endptr = nullptr;
1334  errno = 0;
1335  T ret = static_cast<T>(std::strtoll(arg, &endptr, 0));
1336  if (endptr == arg) {
1337  std::ostringstream msg;
1338  msg << "unable to convert argument to integer: \"" << arg << "\"";
1339  throw std::invalid_argument(msg.str());
1340  }
1341  if (errno == ERANGE) {
1342  throw std::out_of_range("argument numeric value out of range");
1343  }
1344  return ret;
1345  }
1346 
1347 
1348 #define DEFINE_CONVERSION_FROM_LONG_(TYPE) \
1349  template <> inline \
1350  TYPE arg(const char* arg) \
1351  { \
1352  return long_<TYPE>(arg); \
1353  }
1354 
1356  DEFINE_CONVERSION_FROM_LONG_(unsigned char)
1357  DEFINE_CONVERSION_FROM_LONG_(signed char)
1359  DEFINE_CONVERSION_FROM_LONG_(unsigned short)
1361  DEFINE_CONVERSION_FROM_LONG_(unsigned int)
1363  DEFINE_CONVERSION_FROM_LONG_(unsigned long)
1364 
1365 #undef DEFINE_CONVERSION_FROM_LONG_
1366 
1367 
1368 #define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE) \
1369  template <> inline \
1370  TYPE arg(const char* arg) \
1371  { \
1372  return long_long_<TYPE>(arg); \
1373  }
1374 
1376  DEFINE_CONVERSION_FROM_LONG_LONG_(unsigned long long)
1377 
1378 #undef DEFINE_CONVERSION_FROM_LONG_LONG_
1379 
1380 
1381  template <> inline
1382  bool arg(const char* arg)
1383  {
1384  return argagg::convert::arg<int>(arg) != 0;
1385  }
1386 
1387 
1388  template <> inline
1389  float arg(const char* arg)
1390  {
1391  char* endptr = nullptr;
1392  errno = 0;
1393  float ret = std::strtof(arg, &endptr);
1394  if (endptr == arg) {
1395  std::ostringstream msg;
1396  msg << "unable to convert argument to integer: \"" << arg << "\"";
1397  throw std::invalid_argument(msg.str());
1398  }
1399  if (errno == ERANGE) {
1400  throw std::out_of_range("argument numeric value out of range");
1401  }
1402  return ret;
1403  }
1404 
1405 
1406  template <> inline
1407  double arg(const char* arg)
1408  {
1409  char* endptr = nullptr;
1410  errno = 0;
1411  double ret = std::strtod(arg, &endptr);
1412  if (endptr == arg) {
1413  std::ostringstream msg;
1414  msg << "unable to convert argument to integer: \"" << arg << "\"";
1415  throw std::invalid_argument(msg.str());
1416  }
1417  if (errno == ERANGE) {
1418  throw std::out_of_range("argument numeric value out of range");
1419  }
1420  return ret;
1421  }
1422 
1423 
1424  template <> inline
1425  const char* arg(const char* arg)
1426  {
1427  return arg;
1428  }
1429 
1430 
1431  template <> inline
1432  std::string arg(const char* arg)
1433  {
1434  return std::string(arg);
1435  }
1436 
1437 }
1438 
1439 
1440 inline
1442 : std::ostringstream(), output(output)
1443 {
1444 }
1445 
1446 
1447 inline
1449 {
1450  output << fmt_string(this->str());
1451 }
1452 
1453 
1454 #ifdef __unix__
1455 
1456 
1457 inline
1459 {
1460  constexpr int read_end = 0;
1461  constexpr int write_end = 1;
1462 
1463  // TODO (vnguyen): This function overall needs to handle possible error
1464  // returns from the various syscalls.
1465 
1466  int read_pipe[2];
1467  int write_pipe[2];
1468  if (pipe(read_pipe) == -1) {
1469  return s;
1470  }
1471  if (pipe(write_pipe) == -1) {
1472  return s;
1473  }
1474 
1475  auto parent_pid = fork();
1476  bool is_fmt_proc = (parent_pid == 0);
1477  if (is_fmt_proc) {
1478  dup2(write_pipe[read_end], STDIN_FILENO);
1479  dup2(read_pipe[write_end], STDOUT_FILENO);
1480  close(write_pipe[read_end]);
1481  close(write_pipe[write_end]);
1482  close(read_pipe[read_end]);
1483  close(read_pipe[write_end]);
1484  const char* argv[] = {"fmt", NULL};
1485  execvp(const_cast<char*>(argv[0]), const_cast<char**>(argv));
1486  }
1487 
1488  close(write_pipe[read_end]);
1489  close(read_pipe[write_end]);
1490  auto fmt_write_fd = write_pipe[write_end];
1491  auto write_result = write(fmt_write_fd, s.c_str(), s.length());
1492  if (write_result != static_cast<ssize_t>(s.length())) {
1493  return s;
1494  }
1495  close(fmt_write_fd);
1496 
1497  auto fmt_read_fd = read_pipe[read_end];
1498  std::ostringstream os;
1499  char buf[64];
1500  while (true) {
1501  auto read_count = read(
1502  fmt_read_fd, reinterpret_cast<void*>(buf), sizeof(buf));
1503  if (read_count <= 0) {
1504  break;
1505  }
1506  os.write(buf, static_cast<std::streamsize>(read_count));
1507  }
1508  close(fmt_read_fd);
1509 
1510  return os.str();
1511 }
1512 
1513 
1514 #else // #ifdef __unix__
1515 
1516 
1517 inline
1519 {
1520  return s;
1521 }
1522 
1523 
1524 #endif // #ifdef __unix__
1525 
1526 
1527 } // namespace argagg
1528 
1529 
1530 inline
1532 {
1533  for (auto& definition : x.definitions) {
1534  os << " ";
1535  for (auto& flag : definition.flags) {
1536  os << flag;
1537  if (flag != definition.flags.back()) {
1538  os << ", ";
1539  }
1540  }
1541  os << std::endl;
1542  os << " " << definition.help << std::endl;
1543  }
1544  return os;
1545 }
1546 
1547 
1548 #endif // ARGAGG_ARGAGG_ARGAGG_HPP
T all_of(T... args)
#define DEFINE_CONVERSION_FROM_LONG_LONG_(TYPE)
Definition: argagg.hpp:1368
#define DEFINE_CONVERSION_FROM_LONG_(TYPE)
Definition: argagg.hpp:1348
std::ostream & operator<<(std::ostream &os, const argagg::parser &x)
Writes the option help to the given stream.
Definition: argagg.hpp:1531
T begin(T... args)
T c_str(T... args)
T endl(T... args)
T isalnum(T... args)
T make_pair(T... args)
T move(T... args)
T long_(const char *arg)
Templated function for conversion to T using the std::strtol() function. This is used for anything lo...
Definition: argagg.hpp:1307
bool arg(const char *arg)
Explicit instantiations of this function are used to convert arguments to types.
Definition: argagg.hpp:1382
T arg(const char *arg)
Explicit instantiations of this function are used to convert arguments to types.
Definition: argagg.hpp:1382
T long_long_(const char *arg)
Templated function for conversion to T using the std::strtoll() function. This is used for anything l...
Definition: argagg.hpp:1331
There are only two hard things in Computer Science: cache invalidation and naming things (Phil Karlto...
Definition: argagg.hpp:101
parser_map validate_definitions(const std::vector< definition > &definitions)
Validates a collection (specifically an std::vector) of definition objects by checking if the contain...
Definition: argagg.hpp:1005
bool flag_is_short(const char *s)
Tests whether or not a valid flag is short. Assumes the provided cstring is already a valid flag.
Definition: argagg.hpp:960
bool cmd_line_arg_is_option_flag(const char *s)
Checks whether or not a command line argument should be processed as an option flag....
Definition: argagg.hpp:824
std::string fmt_string(const std::string &s)
Processes the provided string using the fmt util and returns the resulting output as a string....
Definition: argagg.hpp:1458
bool is_valid_flag_definition(const char *s)
Checks whether a flag in an option definition is valid. I suggest reading through the function source...
Definition: argagg.hpp:904
STL namespace.
T size(T... args)
T str(T... args)
T strchr(T... args)
T strlen(T... args)
T strncmp(T... args)
T strtof(T... args)
T strtol(T... args)
An option definition which essentially represents what an option is.
Definition: argagg.hpp:388
bool requires_arguments() const
Returns true if this option requires arguments.
Definition: argagg.hpp:817
bool wants_no_arguments() const
Returns true if this option does not want any arguments.
Definition: argagg.hpp:810
const std::string name
Name of the option. Option parser results are keyed by this name.
Definition: argagg.hpp:394
std::vector< std::string > flags
List of strings to match that correspond to this option. Should be fully specified with hyphens (e....
Definition: argagg.hpp:401
std::string help
Help string for this option.
Definition: argagg.hpp:407
unsigned int num_args
Number of arguments this option requires. Must be 0 or 1. All other values have undefined behavior....
Definition: argagg.hpp:415
A convenience output stream that will accumulate what is streamed to it and then, on destruction,...
Definition: argagg.hpp:591
std::ostream & output
Reference to the final output stream that the formatted string will be streamed to.
Definition: argagg.hpp:598
~fmt_ostream()
Special destructor that will format the accumulated string using fmt (via the argagg::fmt_string() fu...
Definition: argagg.hpp:1448
fmt_ostream(std::ostream &output)
Construct to output to the provided output stream when this object is destroyed.
Definition: argagg.hpp:1441
This exception is thrown when an option's flag is invalid. This can be the case if the flag is not pr...
Definition: argagg.hpp:147
This exception is thrown when an option requires an argument but is not provided one....
Definition: argagg.hpp:134
Represents a single option parse result.
Definition: argagg.hpp:178
const char * arg
Argument parsed for this single option. If no argument was parsed this will be set to nullptr.
Definition: argagg.hpp:185
T as() const
Converts the argument parsed for this single option instance into the given type using the type match...
Definition: argagg.hpp:653
Represents multiple option parse results for a single option. If treated as a single parse result it ...
Definition: argagg.hpp:239
std::size_t count() const
Gets the number of times the option shows up.
Definition: argagg.hpp:698
option_result & operator[](std::size_t index)
Gets a single option parse result by index.
Definition: argagg.hpp:705
std::vector< option_result > all
All option parse results for this option.
Definition: argagg.hpp:245
T as() const
Converts the argument parsed for the LAST option parse result for the parent definition to the provid...
Definition: argagg.hpp:719
Contains two maps which aid in option parsing. The first map, short_map, maps from a short flag (just...
Definition: argagg.hpp:472
const definition * get_definition_for_long_flag(const std::string &flag) const
If the long flag exists in the map object then it is returned by this method. If it doesn't then null...
Definition: argagg.hpp:993
std::unordered_map< std::string, const definition * > long_map
Maps from a long flag (an std::string) to a pointer to the original definition that the flag represen...
Definition: argagg.hpp:486
bool known_short_flag(const char flag) const
Returns true if the provided short flag exists in the map object.
Definition: argagg.hpp:968
bool known_long_flag(const std::string &flag) const
Returns true if the provided long flag exists in the map object.
Definition: argagg.hpp:984
const definition * get_definition_for_short_flag(const char flag) const
If the short flag exists in the map object then it is returned by this method. If it doesn't then nul...
Definition: argagg.hpp:976
std::array< const definition *, 256 > short_map
Maps from a short flag (just a character) to a pointer to the original definition that the flag repre...
Definition: argagg.hpp:479
Represents all results of the parser including options and positional arguments.
Definition: argagg.hpp:311
std::size_t count() const
Gets the number of positional arguments.
Definition: argagg.hpp:776
std::vector< const char * > pos
Vector of positional arguments.
Definition: argagg.hpp:331
std::vector< T > all_as() const
Gets all positional arguments converted to the given type.
Definition: argagg.hpp:797
option_results & operator[](const std::string &name)
Get the parser results for the given definition. If the definition never showed up then the exception...
Definition: argagg.hpp:761
bool has_option(const std::string &name) const
Used to check if an option was specified at all.
Definition: argagg.hpp:753
T as(std::size_t i=0) const
Gets a positional argument converted to the given type.
Definition: argagg.hpp:790
std::unordered_map< std::string, option_results > options
Maps from definition name to the structure which contains the parser results for that definition.
Definition: argagg.hpp:325
const char * program
Returns the name of the program from the original arguments list. This is always the first argument.
Definition: argagg.hpp:318
A list of option definitions used to inform how to parse arguments.
Definition: argagg.hpp:536
std::vector< definition > definitions
Vector of the option definitions which inform this parser how to parse the command line arguments.
Definition: argagg.hpp:543
parser_results parse(int argc, const char **argv) const
Parses the provided command line arguments and returns the results as parser_results.
Definition: argagg.hpp:1061
This exception is thrown when a long option is parsed and is given an argument using the "=" syntax b...
Definition: argagg.hpp:110
This exception is thrown when an option is parsed unexpectedly such as when an argument was expected ...
Definition: argagg.hpp:122
T transform(T... args)
T write(T... args)