C++ Utils  0.1
utils/args.h
Go to the documentation of this file.
00001 
00037 #ifndef UTILS_ARGS_H
00038 #define UTILS_ARGS_H
00039 
00040 #include "utils/stringutils.h"
00041 
00042 #include <getopt.h>
00043 #include <algorithm>
00044 #include <map>
00045 #include <iomanip>
00046 #include <iostream>
00047 #include <string>
00048 #include <sstream>
00049 #include <vector>
00050 
00051 namespace utils
00052 {
00053 
00058 class Args
00059 {
00060 private:
00061         struct optionInfo {
00063                 std::vector<std::string> enumValues;
00064                 std::string longOption;         // We need a copy here to get the const char* correct
00066                 std::string value;
00067                 std::string description;
00068                 bool required;
00069         };
00070 
00071         struct additionalOptionInfo {
00072                 std::string name;
00073                 std::string description;
00074                 bool required;
00075         };
00076 
00080         struct valueConvert {
00081                 void operator()(char& c)
00082                 {
00083                         c = toupper(static_cast<unsigned char>(c));
00084                         switch (c) {
00085                         case '-':
00086                                 c = '_';
00087                                 break;
00088                         }
00089                 }
00090         };
00091 
00093         const std::string m_description;
00095         const bool m_addHelp;
00096 
00098         std::vector<struct option> m_options;
00103         std::vector<optionInfo> m_optionInfo;
00104 
00105         std::vector<additionalOptionInfo> m_additionalOptionInfo;
00106 
00108         std::map<char, size_t> m_short2option;
00109 
00111         std::map<std::string, std::string> m_arguments;
00112 
00117         std::map<std::string, std::string> m_additionalArguments;
00118 
00120         std::string m_customHelpMessage;
00121 
00122 public:
00123         enum Argument
00124         {
00125                 Required = required_argument,
00126                 No = no_argument,
00127                 Optional = optional_argument
00128         };
00129 
00130         enum Result
00131         {
00132                 Success = 0,
00133                 Error,
00135                 Help
00136         };
00137 
00138 public:
00139         Args(const std::string &description = "", bool addHelp = true)
00140                 : m_description(description),
00141                   m_addHelp(addHelp)
00142         {
00143         }
00144 
00145         void addOption(const std::string &longOption,
00146                         char shortOption = 0,
00147                         const std::string &description = "",
00148                         Argument argument = Required,
00149                         bool required = true)
00150         {
00151                 addOptionInternal(longOption, shortOption, description, argument, required);
00152         }
00153 
00157         template<size_t N>
00158         void addEnumOption(const std::string &longOption,
00159                         const char* (&enumValues)[N],
00160                         char shortOption = 0,
00161                         const std::string &description = "",
00162                         bool required = true)
00163         {
00164                 std::vector<std::string> values(enumValues, end(enumValues));
00165 
00166                 std::string value = "{" + StringUtils::join(values, "|") + "}";
00167 
00168                 addOptionInternal(longOption, shortOption, description, Required, required,
00169                                 value, values);
00170 
00171 
00172         }
00173 
00174         void addAdditionalOption(const std::string &name,
00175                         const std::string &description = "",
00176                         bool required = true)
00177         {
00178                 if (!m_additionalOptionInfo.empty()) {
00179                         if (required && !m_additionalOptionInfo.back().required)
00180                                 // After one optional argument there can only be more optional arguments
00181                                 return;
00182                 }
00183 
00184                 struct additionalOptionInfo i = {name, description, required};
00185                 m_additionalOptionInfo.push_back(i);
00186         }
00187 
00191         void setCustomHelpMessage(const std::string &message)
00192         {
00193                 m_customHelpMessage = message;
00194         }
00195 
00199         Result parse(int argc, char* const* argv, bool printHelp = true)
00200         {
00201                 if (m_addHelp)
00202                         addOption("help", 'h', "Show this help message", No, false);
00203 
00204                 std::ostringstream shortOptions;
00205                 for (std::vector<struct option>::const_iterator i = m_options.begin();
00206                         i != m_options.end(); i++) {
00207                         if (i->val != 0) {
00208                                 shortOptions << static_cast<char>(i->val);
00209                                 switch (i->has_arg)
00210                                 {
00211                                 case required_argument:
00212                                         shortOptions << ':';
00213                                         break;
00214                                 case optional_argument:
00215                                         shortOptions << "::";
00216                                         break;
00217                                 }
00218                         }
00219                 }
00220 
00221                 // Add null option
00222                 struct option o = {0, 0, 0, 0};
00223                 m_options.push_back(o);
00224 
00225                 // Update const char* in m_options
00226                 for (size_t i = 0; i < m_optionInfo.size(); i++)
00227                         m_options[i].name = m_optionInfo[i].longOption.c_str();
00228 
00229                 while (true) {
00230                         int optionIndex = 0;
00231 
00232                         int c = getopt_long(argc, argv, shortOptions.str().c_str(),
00233                                 &m_options[0], &optionIndex);
00234 
00235                         if (c < 0)
00236                                 break;
00237 
00238                         switch (c) {
00239                         case '?':
00240                                 if (printHelp)
00241                                         helpMessage(argv[0], std::cerr);
00242                                 return Error;
00243                         case 0:
00244                                 // Nothing to do
00245                                 break;
00246                         default:
00247                                 optionIndex = m_short2option.at(c);
00248                         }
00249 
00250                         if (optarg == 0L)
00251                                 m_arguments[m_options[optionIndex].name] = "";
00252                         else
00253                                 m_arguments[m_options[optionIndex].name] = optarg;
00254 
00255                         if (!m_optionInfo[optionIndex].enumValues.empty()) {
00256                                 std::vector<std::string>::const_iterator i =
00257                                         std::find(m_optionInfo[optionIndex].enumValues.begin(),
00258                                                         m_optionInfo[optionIndex].enumValues.end(),
00259                                                         m_arguments[m_options[optionIndex].name]);
00260                                 if (i == m_optionInfo[optionIndex].enumValues.end()) {
00261                                         if (printHelp) {
00262                                                 std::cerr << argv[0] << ": option --" << m_options[optionIndex].name
00263                                                                 << " must be set to " << m_optionInfo[optionIndex].value << std::endl;
00264                                                 helpMessage(argv[0], std::cerr);
00265                                         }
00266                                         return Error;
00267                                 }
00268 
00269                                 m_arguments[m_options[optionIndex].name]
00270                                     = StringUtils::toString(i-m_optionInfo[optionIndex].enumValues.begin());
00271                         }
00272                 }
00273 
00274                 if (m_addHelp && isSet("help")) {
00275                         if (printHelp)
00276                                 helpMessage(argv[0]);
00277                         return Help;
00278                 }
00279 
00280                 for (std::vector<optionInfo>::const_iterator i = m_optionInfo.begin();
00281                         i != m_optionInfo.end(); i++) {
00282                         if (i->required && !isSet(i->longOption)) {
00283                                 if (printHelp) {
00284                                         std::cerr << argv[0] << ": option --" << i->longOption << " is required" << std::endl;
00285                                         helpMessage(argv[0], std::cerr);
00286                                 }
00287                                 return Error;
00288                         }
00289                 }
00290 
00291                 // Parse additional options and check if all required options are set
00292                 int i;
00293                 for (i = 0; i < argc-optind; i++) {
00294                         if (i >= static_cast<int>(m_additionalOptionInfo.size())) {
00295                                 if (printHelp)
00296                                         std::cerr << argv[0] << ": ignoring unknown parameter \"" << argv[i+optind] << "\"" << std::endl;
00297                         } else
00298                                 m_additionalArguments[m_additionalOptionInfo[i].name] = argv[i+optind];
00299                 }
00300                 if (static_cast<size_t>(i) < m_additionalOptionInfo.size()) {
00301                         if (m_additionalOptionInfo[i].required) {
00302                                 if (printHelp) {
00303                                         std::cerr << argv[0] << ": option <" << m_additionalOptionInfo[i].name << "> is required" << std::endl;
00304                                         helpMessage(argv[0], std::cerr);
00305                                 }
00306                                 return Error;
00307                         }
00308                 }
00309 
00310                 return Success;
00311         }
00312 
00313         bool isSet(const std::string &option) const
00314         {
00315                 return m_arguments.find(option) != m_arguments.end();
00316         }
00317 
00318         bool isSetAdditional(const std::string &option) const
00319         {
00320                 return m_additionalArguments.find(option) != m_additionalArguments.end();
00321         }
00322 
00323         template<typename T>
00324         T getArgument(const std::string &option)
00325         {
00326                 return StringUtils::parse<T>(m_arguments.at(option));
00327         }
00328 
00329         template<typename T>
00330         T getArgument(const std::string &option, T defaultArgument)
00331         {
00332                 if (!isSet(option))
00333                         return defaultArgument;
00334 
00335                 return getArgument<T>(option);
00336         }
00337 
00338         template<typename T>
00339         T getAdditionalArgument(const std::string &option)
00340         {
00341                 return StringUtils::parse<T>(m_additionalArguments.at(option));
00342         }
00343 
00344         template<typename T>
00345         T getAdditionalArgument(const std::string &option, T defaultArgument)
00346         {
00347                 if (!isSetAdditional(option))
00348                         return defaultArgument;
00349 
00350                 return getAdditionalArgument<T>(option);
00351         }
00352 
00353         void helpMessage(const char* prog, std::ostream &out = std::cout)
00354         {
00355                 // First line with all short options
00356                 out << "Usage: " << prog;
00357                 for (size_t i = 0; i < m_options.size()-1; i++) {
00358                         out << ' ';
00359 
00360                         if (!m_optionInfo[i].required)
00361                                 out << '[';
00362 
00363                         if (m_options[i].val != 0)
00364                                 out << '-' << static_cast<char>(m_options[i].val);
00365                         else
00366                                 out << "--" << m_options[i].name;
00367 
00368                         argumentInfo(i, out);
00369 
00370                         if (!m_optionInfo[i].required)
00371                                 out << ']';
00372                 }
00373                 for (size_t i = 0; i < m_additionalOptionInfo.size(); i++) {
00374                         out << ' ';
00375 
00376                         if (!m_additionalOptionInfo[i].required)
00377                                 out << '[';
00378 
00379                         out << '<' << m_additionalOptionInfo[i].name << '>';
00380 
00381                         if (!m_additionalOptionInfo[i].required)
00382                                 out << ']';
00383 
00384                 }
00385                 out << std::endl;
00386 
00387                 // General program description
00388                 if (!m_description.empty())
00389                         out << std::endl << m_description << std::endl;
00390 
00391                 // Arguments
00392                 if (!m_additionalOptionInfo.empty()) {
00393                         out << std::endl << "arguments:" << std::endl;
00394                         for (size_t i = 0; i < m_additionalOptionInfo.size(); i++) {
00395                                 out << "  <" << m_additionalOptionInfo[i].name << '>';
00396 
00397                                 // Number of characters used for the option
00398                                 size_t length = 4 + m_additionalOptionInfo[i].name.size();
00399 
00400                                 if (length >= 30) {
00401                                         out << std::endl;
00402                                         out << std::setw(30) << ' ';
00403                                 } else
00404                                         out << std::setw(30-length) << ' ';
00405 
00406                                 out << m_additionalOptionInfo[i].description << std::endl;
00407                         }
00408                 }
00409 
00410                 // Optional arguments
00411                 if (m_options.size() > 1) {
00412                         out << std::endl << "optional arguments:" << std::endl;
00413                         for (size_t i = 0; i < m_options.size()-1; i++) {
00414                                 out << "  ";
00415 
00416                                 // Number of characters used for the option
00417                                 size_t length = 2;
00418 
00419                                 if (m_options[i].val != 0) {
00420                                         out << '-' << static_cast<char>(m_options[i].val);
00421                                         out << ", ";
00422                                         length += 4;
00423                                 }
00424 
00425                                 out << "--" << m_options[i].name;
00426                                 length += m_optionInfo[i].longOption.size() + 2;
00427                                 length += argumentInfo(i, out);
00428 
00429                                 if (length >= 30) {
00430                                         out << std::endl;
00431                                         out << std::setw(30) << ' ';
00432                                 } else
00433                                         out << std::setw(30-length) << ' ';
00434 
00435                                 out << m_optionInfo[i].description << std::endl;
00436                         }
00437                 }
00438 
00439                 out << m_customHelpMessage;
00440         }
00441 
00442 private:
00443         void addOptionInternal(const std::string &longOption,
00444                         char shortOption,
00445                         const std::string &description,
00446                         Argument argument,
00447                         bool required,
00448                         const std::string &value = "",
00449                         const std::vector<std::string> enumValues = std::vector<std::string>())
00450         {
00451 
00452                 if (shortOption)
00453                         m_short2option[shortOption] = m_options.size();
00454 
00455                 std::string v;
00456                 if (!value.empty())
00457                         v = value;
00458                 else if (argument != No) {
00459                         v = longOption;
00460                         std::for_each(v.begin(), v.end(), valueConvert());
00461                 }
00462 
00463                 struct optionInfo i = {enumValues, longOption, v, description, required};
00464                 m_optionInfo.push_back(i);
00465 
00466                 struct option o = {m_optionInfo.back().longOption.c_str(), argument, 0, shortOption};
00467                 m_options.push_back(o);
00468         }
00469 
00476         size_t argumentInfo(size_t i, std::ostream &out)
00477         {
00478                 switch (m_options[i].has_arg) {
00479                 case required_argument:
00480                         out << ' ' << m_optionInfo[i].value;
00481                         return m_optionInfo[i].value.size() + 1;
00482                 case optional_argument:
00483                         out << " [" << m_optionInfo[i].value << ']';
00484                         return m_optionInfo[i].value.size() + 3;
00485                 }
00486 
00487                 return 0;
00488         }
00489 
00490 private:
00491         template<typename T, size_t N>
00492         static T* end(T (&a)[N])
00493         {
00494                 return a + N;
00495         }
00496 };
00497 
00498 template<> inline
00499 bool utils::Args::getArgument(const std::string &option, bool defaultArgument)
00500 {
00501         if (!isSet(option))
00502                 return defaultArgument;
00503 
00504         return !defaultArgument;
00505 }
00506 
00507 }
00508 
00509 #endif // UTILS_ARGS_H
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines