C++ Utils
0.1
|
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