github.com/platonnetwork/platon-go@v0.7.6/cases/tool/win/bls_win/include/cybozu/option.hpp (about) 1 #pragma once 2 /** 3 @file 4 @brief command line parser 5 6 @author MITSUNARI Shigeo(@herumi) 7 */ 8 #include <string> 9 #include <vector> 10 #include <map> 11 #include <sstream> 12 #include <iostream> 13 #include <limits> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <assert.h> 17 #include <cybozu/exception.hpp> 18 #include <cybozu/atoi.hpp> 19 20 /* 21 Option parser 22 23 progName (opt1-name|opt2-name|...) param1 param2 ... 24 param1:param1-help 25 param2:param2-help 26 -op1-name:opt1-help 27 ... 28 29 How to setup 30 int num; 31 -n num ; (optional) option => appendOpt(&x, <defaultValue>, "num", "num-help"); 32 -n num ; must option => appendMust(&x, "num", "num-help"); 33 34 std::vector<int> v; 35 -v s1 s2 s3 ... => appendVec(&v, "v"); 36 37 Remark1: terminate parsing of v if argv begins with '-[^0-9]' 38 Remark2: the begining character of opt-name is not a number ('0'...'9') 39 because avoid conflict with minus number 40 41 std::string file1; 42 file1 is param => appendParam(&file1, "input-file"); 43 file2 is optional param => appendParamOpt(&file2, "output-file"); 44 45 How to use 46 opt.parse(argc, argv); 47 48 see sample/option_smpl.cpp 49 */ 50 51 namespace cybozu { 52 53 struct OptionError : public cybozu::Exception { 54 enum Type { 55 NoError = 0, 56 BAD_OPT = 1, 57 BAD_VALUE, 58 NO_VALUE, 59 OPT_IS_NECESSARY, 60 PARAM_IS_NECESSARY, 61 REDUNDANT_VAL, 62 BAD_ARGC 63 }; 64 Type type; 65 int argPos; 66 OptionError() 67 : cybozu::Exception("OptionError", false) 68 , type(NoError) 69 , argPos(0) 70 { 71 } 72 cybozu::Exception& set(Type _type, int _argPos = 0) 73 { 74 this->type = _type; 75 this->argPos = _argPos; 76 switch (_type) { 77 case BAD_OPT: 78 (*this) << "bad opt"; 79 break; 80 case BAD_VALUE: 81 (*this) << "bad value"; 82 break; 83 case NO_VALUE: 84 (*this) << "no value"; 85 break; 86 case OPT_IS_NECESSARY: 87 (*this) << "opt is necessary"; 88 break; 89 case PARAM_IS_NECESSARY: 90 (*this) << "param is necessary"; 91 break; 92 case REDUNDANT_VAL: 93 (*this) << "redundant argVal"; 94 break; 95 case BAD_ARGC: 96 (*this) << "bad argc"; 97 default: 98 break; 99 } 100 return *this; 101 } 102 }; 103 104 namespace option_local { 105 106 template<class T> 107 bool convert(T* x, const char *str) 108 { 109 std::istringstream is(str); 110 is >> *x; 111 return !!is; 112 } 113 114 template<> 115 inline bool convert(std::string* x, const char *str) 116 { 117 *x = str; 118 return true; 119 } 120 121 template<class T> 122 bool convertInt(T* x, const char *str) 123 { 124 if (str[0] == '0' && str[1] == 'x') { 125 bool b; 126 *x = cybozu::hextoi(&b, str + 2); 127 return b; 128 } 129 size_t len = strlen(str); 130 int factor = 1; 131 if (len > 1) { 132 switch (str[len - 1]) { 133 case 'k': factor = 1000; len--; break; 134 case 'm': factor = 1000 * 1000; len--; break; 135 case 'g': factor = 1000 * 1000 * 1000; len--; break; 136 case 'K': factor = 1024; len--; break; 137 case 'M': factor = 1024 * 1024; len--; break; 138 case 'G': factor = 1024 * 1024 * 1024; len--; break; 139 default: break; 140 } 141 } 142 bool b; 143 T y = cybozu::atoi(&b, str, len); 144 if (!b) return false; 145 if (factor > 1) { 146 if ((std::numeric_limits<T>::min)() / factor <= y 147 && y <= (std::numeric_limits<T>::max)() / factor) { 148 *x = y * factor; 149 } else { 150 return false; 151 } 152 } else { 153 *x = y; 154 } 155 return true; 156 } 157 158 #define CYBOZU_OPTION_DEFINE_CONVERT_INT(type) \ 159 template<>inline bool convert(type* x, const char *str) { return convertInt(x, str); } 160 161 CYBOZU_OPTION_DEFINE_CONVERT_INT(int) 162 CYBOZU_OPTION_DEFINE_CONVERT_INT(long) 163 CYBOZU_OPTION_DEFINE_CONVERT_INT(long long) 164 165 CYBOZU_OPTION_DEFINE_CONVERT_INT(unsigned int) 166 CYBOZU_OPTION_DEFINE_CONVERT_INT(unsigned long) 167 CYBOZU_OPTION_DEFINE_CONVERT_INT(unsigned long long) 168 169 #undef CYBOZU_OPTION_DEFINE_CONVERT_INT 170 171 struct HolderBase { 172 virtual ~HolderBase(){} 173 virtual bool set(const char*) = 0; 174 virtual HolderBase *clone() const = 0; 175 virtual std::string toStr() const = 0; 176 virtual const void *get() const = 0; 177 }; 178 179 template<class T> 180 struct Holder : public HolderBase { 181 T *p_; 182 Holder(T *p) : p_(p) {} 183 HolderBase *clone() const { return new Holder(p_); } 184 bool set(const char *str) { return option_local::convert(p_, str); } 185 std::string toStr() const 186 { 187 std::ostringstream os; 188 os << *p_; 189 return os.str(); 190 } 191 const void *get() const { return (void*)p_; } 192 }; 193 194 /* 195 for gcc 7 with -fnew-ttp-matching 196 this specialization is not necessary under -fno-new-ttp-matching 197 */ 198 template struct Holder<std::string>; 199 200 template<class T, class Alloc, template<class T_, class Alloc_>class Container> 201 struct Holder<Container<T, Alloc> > : public HolderBase { 202 typedef Container<T, Alloc> Vec; 203 Vec *p_; 204 Holder(Vec *p) : p_(p) {} 205 HolderBase *clone() const { return new Holder<Vec>(p_); } 206 bool set(const char *str) 207 { 208 T t; 209 bool b = option_local::convert(&t, str); 210 if (b) p_->push_back(t); 211 return b; 212 } 213 std::string toStr() const 214 { 215 std::ostringstream os; 216 bool isFirst = true; 217 for (typename Vec::const_iterator i = p_->begin(), ie = p_->end(); i != ie; ++i) { 218 if (isFirst) { 219 isFirst = false; 220 } else { 221 os << ' '; 222 } 223 os << *i; 224 } 225 return os.str(); 226 } 227 const void *get() const { return (void*)p_; } 228 }; 229 230 class Var { 231 HolderBase *p_; 232 bool isSet_; 233 public: 234 Var() : p_(0), isSet_(false) { } 235 Var(const Var& rhs) : p_(rhs.p_->clone()), isSet_(false) { } 236 template<class T> 237 explicit Var(T *x) : p_(new Holder<T>(x)), isSet_(false) { } 238 239 ~Var() { delete p_; } 240 241 void swap(Var& rhs) CYBOZU_NOEXCEPT 242 { 243 std::swap(p_, rhs.p_); 244 std::swap(isSet_, rhs.isSet_); 245 } 246 void operator=(const Var& rhs) 247 { 248 Var v(rhs); 249 swap(v); 250 } 251 bool set(const char *str) 252 { 253 isSet_ = true; 254 return p_->set(str); 255 } 256 std::string toStr() const { return p_ ? p_->toStr() : ""; } 257 bool isSet() const { return isSet_; } 258 const void *get() const { return p_ ? p_->get() : 0; } 259 }; 260 261 } // option_local 262 263 class Option { 264 enum Mode { // for opt 265 N_is0 = 0, // for bool by appendBoolOpt() 266 N_is1 = 1, 267 N_any = 2 268 }; 269 enum ParamMode { 270 P_exact = 0, // one 271 P_optional = 1, // zero or one 272 P_variable = 2 // zero or greater 273 }; 274 struct Info { 275 option_local::Var var; 276 Mode mode; // 0 or 1 or any ; for opt, not used for Param 277 bool isMust; // this option is must 278 std::string opt; // option param name without '-' 279 std::string help; // description of option 280 281 Info() : mode(N_is0), isMust(false) {} 282 template<class T> 283 Info(T* pvar, Mode mode, bool isMust, const char *opt, const std::string& help) 284 : var(pvar) 285 , mode(mode) 286 , isMust(isMust) 287 , opt(opt) 288 , help(help) 289 { 290 } 291 friend inline std::ostream& operator<<(std::ostream& os, const Info& self) 292 { 293 os << self.opt << '=' << self.var.toStr(); 294 if (self.var.isSet()) { 295 os << " (set)"; 296 } else { 297 os << " (default)"; 298 } 299 return os; 300 } 301 void put() const 302 { 303 std::cout << *this; 304 } 305 void usage() const 306 { 307 printf(" -%s %s%s\n", opt.c_str(), help.c_str(), isMust ? " (must)" : ""); 308 } 309 void shortUsage() const 310 { 311 printf(" -%s %s", opt.c_str(), mode == N_is0 ? "" : mode == N_is1 ? "para" : "para..."); 312 } 313 bool isSet() const { return var.isSet(); } 314 const void *get() const { return var.get(); } 315 }; 316 typedef std::vector<Info> InfoVec; 317 typedef std::vector<std::string> StrVec; 318 typedef std::map<std::string, size_t> OptMap; 319 InfoVec infoVec_; 320 InfoVec paramVec_; 321 Info remains_; 322 OptMap optMap_; 323 bool showOptUsage_; 324 ParamMode paramMode_; 325 std::string progName_; 326 std::string desc_; 327 std::string helpOpt_; 328 std::string help_; 329 std::string usage_; 330 StrVec delimiters_; 331 StrVec *remainsAfterDelimiter_; 332 int nextDelimiter_; 333 template<class T> 334 void appendSub(T *pvar, Mode mode, bool isMust, const char *opt, const std::string& help) 335 { 336 const char c = opt[0]; 337 if ('0' <= c && c <= '9') throw cybozu::Exception("Option::appendSub:opt must begin with not number") << opt; 338 if (optMap_.find(opt) != optMap_.end()) { 339 throw cybozu::Exception("Option::append:duplicate option") << opt; 340 } 341 optMap_[opt] = infoVec_.size(); 342 infoVec_.push_back(Info(pvar, mode, isMust, opt, help)); 343 } 344 345 template<class T, class U> 346 void append(T *pvar, const U& defaultVal, bool isMust, const char *opt, const std::string& help = "") 347 { 348 *pvar = defaultVal; 349 appendSub(pvar, N_is1, isMust, opt, help); 350 } 351 /* 352 don't deal with negative number as option 353 */ 354 bool isOpt(const char *str) const 355 { 356 if (str[0] != '-') return false; 357 const char c = str[1]; 358 if ('0' <= c && c <= '9') return false; 359 return true; 360 } 361 void verifyParamMode() 362 { 363 if (paramMode_ != P_exact) throw cybozu::Exception("Option:appendParamVec:appendParam is forbidden after appendParamOpt/appendParamVec"); 364 } 365 std::string getBaseName(const std::string& name) const 366 { 367 size_t pos = name.find_last_of("/\\"); 368 if (pos == std::string::npos) return name; 369 return name.substr(pos + 1); 370 } 371 bool inDelimiters(const std::string& str) const 372 { 373 return std::find(delimiters_.begin(), delimiters_.end(), str) != delimiters_.end(); 374 } 375 public: 376 Option() 377 : showOptUsage_(true) 378 , paramMode_(P_exact) 379 , remainsAfterDelimiter_(0) 380 , nextDelimiter_(-1) 381 { 382 } 383 virtual ~Option() {} 384 /* 385 append optional option with default value 386 @param pvar [in] pointer to option variable 387 @param defaultVal [in] default value 388 @param opt [in] option name 389 @param help [in] option help 390 @note you can use 123k, 56M if T is int/long/long long 391 k : *1000 392 m : *1000000 393 g : *1000000000 394 K : *1024 395 M : *1024*1024 396 G : *1024*1024*1024 397 */ 398 template<class T, class U> 399 void appendOpt(T *pvar, const U& defaultVal, const char *opt, const std::string& help = "") 400 { 401 append(pvar, defaultVal, false, opt, help); 402 } 403 /* 404 default value of *pvar is false 405 */ 406 void appendBoolOpt(bool *pvar, const char *opt, const std::string& help = "") 407 { 408 *pvar = false; 409 appendSub(pvar, N_is0, false, opt, help); 410 } 411 /* 412 append necessary option 413 @param pvar [in] pointer to option variable 414 @param opt [in] option name 415 @param help [in] option help 416 */ 417 template<class T> 418 void appendMust(T *pvar, const char *opt, const std::string& help = "") 419 { 420 append(pvar, T(), true, opt, help); 421 } 422 /* 423 append vector option 424 @param pvar [in] pointer to option variable 425 @param opt [in] option name 426 @param help [in] option help 427 */ 428 template<class T, class Alloc, template<class T_, class Alloc_>class Container> 429 void appendVec(Container<T, Alloc> *pvar, const char *opt, const std::string& help = "") 430 { 431 appendSub(pvar, N_any, false, opt, help); 432 } 433 /* 434 append parameter 435 @param pvar [in] pointer to parameter 436 @param opt [in] option name 437 @param help [in] option help 438 */ 439 template<class T> 440 void appendParam(T *pvar, const char *opt, const std::string& help = "") 441 { 442 verifyParamMode(); 443 paramVec_.push_back(Info(pvar, N_is1, true, opt, help)); 444 } 445 /* 446 append optional parameter 447 @param pvar [in] pointer to parameter 448 @param defaultVal [in] default value 449 @param opt [in] option name 450 @param help [in] option help 451 @note you can call appendParamOpt once after appendParam 452 */ 453 template<class T, class U> 454 void appendParamOpt(T *pvar, const U& defaultVal, const char *opt, const std::string& help = "") 455 { 456 verifyParamMode(); 457 *pvar = defaultVal; 458 paramMode_ = P_optional; 459 paramVec_.push_back(Info(pvar, N_is1, false, opt, help)); 460 } 461 /* 462 append remain parameter 463 @param pvar [in] pointer to vector of parameter 464 @param opt [in] option name 465 @param help [in] option help 466 @note you can call appendParamVec once after appendParam 467 */ 468 template<class T, class Alloc, template<class T_, class Alloc_>class Container> 469 void appendParamVec(Container<T, Alloc> *pvar, const char *name, const std::string& help = "") 470 { 471 verifyParamMode(); 472 paramMode_ = P_variable; 473 remains_.var = option_local::Var(pvar); 474 remains_.mode = N_any; 475 remains_.isMust = false; 476 remains_.opt = name; 477 remains_.help = help; 478 } 479 void appendHelp(const char *opt, const std::string& help = ": show this message") 480 { 481 helpOpt_ = opt; 482 help_ = help; 483 } 484 /* 485 stop parsing after delimiter is found 486 @param delimiter [in] string to stop 487 @param remain [out] set remaining strings if remain 488 */ 489 void setDelimiter(const std::string& delimiter, std::vector<std::string> *remain = 0) 490 { 491 delimiters_.push_back(delimiter); 492 remainsAfterDelimiter_ = remain; 493 } 494 /* 495 stop parsing after delimiter is found 496 @param delimiter [in] string to stop to append list of delimiters 497 */ 498 void appendDelimiter(const std::string& delimiter) 499 { 500 delimiters_.push_back(delimiter); 501 } 502 /* 503 clear list of delimiters 504 */ 505 void clearDelimiterList() { delimiters_.clear(); } 506 /* 507 return the next position of delimiter between [0, argc] 508 @note return argc if delimiter is not set nor found 509 */ 510 int getNextPositionOfDelimiter() const { return nextDelimiter_; } 511 /* 512 parse (argc, argv) 513 @param argc [in] argc of main 514 @param argv [in] argv of main 515 @param startPos [in] start position of argc 516 @param progName [in] used instead of argv[0] 517 */ 518 bool parse(int argc, const char *const argv[], int startPos = 1, const char *progName = 0) 519 { 520 if (argc < 1 || startPos > argc) return false; 521 progName_ = getBaseName(progName ? progName : argv[startPos - 1]); 522 nextDelimiter_ = argc; 523 OptionError err; 524 for (int pos = startPos; pos < argc; pos++) { 525 if (inDelimiters(argv[pos])) { 526 nextDelimiter_ = pos + 1; 527 if (remainsAfterDelimiter_) { 528 for (int i = nextDelimiter_; i < argc; i++) { 529 remainsAfterDelimiter_->push_back(argv[i]); 530 } 531 } 532 break; 533 } 534 if (isOpt(argv[pos])) { 535 const std::string str = argv[pos] + 1; 536 if (helpOpt_ == str) { 537 usage(); 538 exit(0); 539 } 540 OptMap::const_iterator i = optMap_.find(str); 541 if (i == optMap_.end()) { 542 err.set(OptionError::BAD_OPT, pos); 543 goto ERR; 544 } 545 546 Info& info = infoVec_[i->second]; 547 switch (info.mode) { 548 case N_is0: 549 if (!info.var.set("1")) { 550 err.set(OptionError::BAD_VALUE, pos); 551 goto ERR; 552 } 553 break; 554 case N_is1: 555 pos++; 556 if (pos == argc) { 557 err.set(OptionError::BAD_VALUE, pos) << (std::string("no value for -") + info.opt); 558 goto ERR; 559 } 560 if (!info.var.set(argv[pos])) { 561 err.set(OptionError::BAD_VALUE, pos) << (std::string(argv[pos]) + " for -" + info.opt); 562 goto ERR; 563 } 564 break; 565 case N_any: 566 default: 567 { 568 pos++; 569 int j = 0; 570 while (pos < argc && !isOpt(argv[pos])) { 571 if (!info.var.set(argv[pos])) { 572 err.set(OptionError::BAD_VALUE, pos) << (std::string(argv[pos]) + " for -" + info.opt) << j; 573 goto ERR; 574 } 575 pos++; 576 j++; 577 } 578 if (j > 0) { 579 pos--; 580 } else { 581 err.set(OptionError::NO_VALUE, pos) << (std::string("for -") + info.opt); 582 goto ERR; 583 } 584 } 585 break; 586 } 587 } else { 588 bool used = false; 589 for (size_t i = 0; i < paramVec_.size(); i++) { 590 Info& param = paramVec_[i]; 591 if (!param.var.isSet()) { 592 if (!param.var.set(argv[pos])) { 593 err.set(OptionError::BAD_VALUE, pos) << (std::string(argv[pos]) + " for " + param.opt); 594 goto ERR; 595 } 596 used = true; 597 break; 598 } 599 } 600 if (!used) { 601 if (paramMode_ == P_variable) { 602 remains_.var.set(argv[pos]); 603 } else { 604 err.set(OptionError::REDUNDANT_VAL, pos) << argv[pos]; 605 goto ERR; 606 } 607 } 608 } 609 } 610 // check whether must-opt is set 611 for (size_t i = 0; i < infoVec_.size(); i++) { 612 const Info& info = infoVec_[i]; 613 if (info.isMust && !info.var.isSet()) { 614 err.set(OptionError::OPT_IS_NECESSARY) << info.opt; 615 goto ERR; 616 } 617 } 618 // check whether param is set 619 for (size_t i = 0; i < paramVec_.size(); i++) { 620 const Info& param = paramVec_[i]; 621 if (param.isMust && !param.var.isSet()) { 622 err.set(OptionError::PARAM_IS_NECESSARY) << param.opt; 623 goto ERR; 624 } 625 } 626 // check whether remains is set 627 if (paramMode_ == P_variable && remains_.isMust && !remains_.var.isSet()) { 628 err.set(OptionError::PARAM_IS_NECESSARY) << remains_.opt; 629 goto ERR; 630 } 631 return true; 632 ERR: 633 assert(err.type); 634 printf("%s\n", err.what()); 635 return false; 636 } 637 /* 638 show desc at first in usage() 639 */ 640 void setDescription(const std::string& desc) 641 { 642 desc_ = desc; 643 } 644 /* 645 show command line after desc 646 don't put option message if not showOptUsage 647 */ 648 void setUsage(const std::string& usage, bool showOptUsage = false) 649 { 650 usage_ = usage; 651 showOptUsage_ = showOptUsage; 652 } 653 void usage() const 654 { 655 if (!desc_.empty()) printf("%s\n", desc_.c_str()); 656 if (usage_.empty()) { 657 printf("usage:%s", progName_.c_str()); 658 if (!infoVec_.empty()) printf(" [opt]"); 659 for (size_t i = 0; i < infoVec_.size(); i++) { 660 if (infoVec_[i].isMust) infoVec_[i].shortUsage(); 661 } 662 for (size_t i = 0; i < paramVec_.size(); i++) { 663 printf(" %s", paramVec_[i].opt.c_str()); 664 } 665 if (paramMode_ == P_variable) { 666 printf(" %s", remains_.opt.c_str()); 667 } 668 printf("\n"); 669 } else { 670 printf("%s\n", usage_.c_str()); 671 if (!showOptUsage_) return; 672 } 673 for (size_t i = 0; i < paramVec_.size(); i++) { 674 const Info& param = paramVec_[i]; 675 if (!param.help.empty()) printf(" %s %s\n", paramVec_[i].opt.c_str(), paramVec_[i].help.c_str()); 676 } 677 if (!remains_.help.empty()) printf(" %s %s\n", remains_.opt.c_str(), remains_.help.c_str()); 678 if (!helpOpt_.empty()) { 679 printf(" -%s %s\n", helpOpt_.c_str(), help_.c_str()); 680 } 681 for (size_t i = 0; i < infoVec_.size(); i++) { 682 infoVec_[i].usage(); 683 } 684 } 685 friend inline std::ostream& operator<<(std::ostream& os, const Option& self) 686 { 687 for (size_t i = 0; i < self.paramVec_.size(); i++) { 688 const Info& param = self.paramVec_[i]; 689 os << param.opt << '=' << param.var.toStr() << std::endl; 690 } 691 if (self.paramMode_ == P_variable) { 692 os << "remains=" << self.remains_.var.toStr() << std::endl; 693 } 694 for (size_t i = 0; i < self.infoVec_.size(); i++) { 695 os << self.infoVec_[i] << std::endl; 696 } 697 return os; 698 } 699 void put() const 700 { 701 std::cout << *this; 702 } 703 /* 704 whether pvar is set or not 705 */ 706 template<class T> 707 bool isSet(const T* pvar) const 708 { 709 const void *p = static_cast<const void*>(pvar); 710 for (size_t i = 0; i < paramVec_.size(); i++) { 711 const Info& v = paramVec_[i]; 712 if (v.get() == p) return v.isSet(); 713 } 714 if (remains_.get() == p) return remains_.isSet(); 715 for (size_t i = 0; i < infoVec_.size(); i++) { 716 const Info& v = infoVec_[i]; 717 if (v.get() == p) return v.isSet(); 718 } 719 throw cybozu::Exception("Option:isSet:no assigned var") << pvar; 720 } 721 }; 722 723 } // cybozu