1 /* 2 Copyright (c) 2009, Hideyuki Tanaka 3 All rights reserved. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are met: 7 * Redistributions of source code must retain the above copyright 8 notice, this list of conditions and the following disclaimer. 9 * Redistributions in binary form must reproduce the above copyright 10 notice, this list of conditions and the following disclaimer in the 11 documentation and/or other materials provided with the distribution. 12 * Neither the name of the <organization> nor the 13 names of its contributors may be used to endorse or promote products 14 derived from this software without specific prior written permission. 15 16 THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY 17 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY 20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #pragma once 29 30 #include <iostream> 31 #include <sstream> 32 #include <vector> 33 #include <map> 34 #include <string> 35 #include <stdexcept> 36 #include <typeinfo> 37 #include <cstring> 38 #include <algorithm> 39 #include <cxxabi.h> 40 #include <cstdlib> 41 42 namespace cmdline{ 43 44 namespace detail{ 45 46 template <typename Target, typename Source, bool Same> 47 class lexical_cast_t{ 48 public: cast(const Source & arg)49 static Target cast(const Source &arg){ 50 Target ret; 51 std::stringstream ss; 52 if (!(ss<<arg && ss>>ret && ss.eof())) 53 throw std::bad_cast(); 54 55 return ret; 56 } 57 }; 58 59 template <typename Target, typename Source> 60 class lexical_cast_t<Target, Source, true>{ 61 public: cast(const Source & arg)62 static Target cast(const Source &arg){ 63 return arg; 64 } 65 }; 66 67 template <typename Source> 68 class lexical_cast_t<std::string, Source, false>{ 69 public: cast(const Source & arg)70 static std::string cast(const Source &arg){ 71 std::ostringstream ss; 72 ss<<arg; 73 return ss.str(); 74 } 75 }; 76 77 template <typename Target> 78 class lexical_cast_t<Target, std::string, false>{ 79 public: cast(const std::string & arg)80 static Target cast(const std::string &arg){ 81 Target ret; 82 std::istringstream ss(arg); 83 if (!(ss>>ret && ss.eof())) 84 throw std::bad_cast(); 85 return ret; 86 } 87 }; 88 89 template <typename T1, typename T2> 90 struct is_same { 91 static const bool value = false; 92 }; 93 94 template <typename T> 95 struct is_same<T, T>{ 96 static const bool value = true; 97 }; 98 99 template<typename Target, typename Source> 100 Target lexical_cast(const Source &arg) 101 { 102 return lexical_cast_t<Target, Source, detail::is_same<Target, Source>::value>::cast(arg); 103 } 104 105 static inline std::string demangle(const std::string &name) 106 { 107 int status=0; 108 char *p=abi::__cxa_demangle(name.c_str(), 0, 0, &status); 109 std::string ret(p); 110 free(p); 111 return ret; 112 } 113 114 template <class T> 115 std::string readable_typename() 116 { 117 return demangle(typeid(T).name()); 118 } 119 120 template <class T> 121 std::string default_value(T def) 122 { 123 return detail::lexical_cast<std::string>(def); 124 } 125 126 template <> 127 inline std::string readable_typename<std::string>() 128 { 129 return "string"; 130 } 131 132 } // detail 133 134 //----- 135 136 class cmdline_error : public std::exception { 137 public: 138 cmdline_error(const std::string &msg): msg(msg){} 139 ~cmdline_error() throw() {} 140 const char *what() const throw() { return msg.c_str(); } 141 private: 142 std::string msg; 143 }; 144 145 template <class T> 146 struct default_reader{ 147 T operator()(const std::string &str){ 148 return detail::lexical_cast<T>(str); 149 } 150 }; 151 152 template <class T> 153 struct range_reader{ 154 range_reader(const T &low, const T &high): low(low), high(high) {} 155 T operator()(const std::string &s) const { 156 T ret=default_reader<T>()(s); 157 if (!(ret>=low && ret<=high)) throw cmdline::cmdline_error("range_error"); 158 return ret; 159 } 160 private: 161 T low, high; 162 }; 163 164 template <class T> 165 range_reader<T> range(const T &low, const T &high) 166 { 167 return range_reader<T>(low, high); 168 } 169 170 template <class T> 171 struct oneof_reader{ 172 T operator()(const std::string &s){ 173 T ret=default_reader<T>()(s); 174 if (std::find(alt.begin(), alt.end(), ret)==alt.end()) 175 throw cmdline_error(""); 176 return ret; 177 } 178 void add(const T &v){ alt.push_back(v); } 179 private: 180 std::vector<T> alt; 181 }; 182 183 template <class T> 184 oneof_reader<T> oneof(T a1) 185 { 186 oneof_reader<T> ret; 187 ret.add(a1); 188 return ret; 189 } 190 191 template <class T> 192 oneof_reader<T> oneof(T a1, T a2) 193 { 194 oneof_reader<T> ret; 195 ret.add(a1); 196 ret.add(a2); 197 return ret; 198 } 199 200 template <class T> 201 oneof_reader<T> oneof(T a1, T a2, T a3) 202 { 203 oneof_reader<T> ret; 204 ret.add(a1); 205 ret.add(a2); 206 ret.add(a3); 207 return ret; 208 } 209 210 template <class T> 211 oneof_reader<T> oneof(T a1, T a2, T a3, T a4) 212 { 213 oneof_reader<T> ret; 214 ret.add(a1); 215 ret.add(a2); 216 ret.add(a3); 217 ret.add(a4); 218 return ret; 219 } 220 221 template <class T> 222 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5) 223 { 224 oneof_reader<T> ret; 225 ret.add(a1); 226 ret.add(a2); 227 ret.add(a3); 228 ret.add(a4); 229 ret.add(a5); 230 return ret; 231 } 232 233 template <class T> 234 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6) 235 { 236 oneof_reader<T> ret; 237 ret.add(a1); 238 ret.add(a2); 239 ret.add(a3); 240 ret.add(a4); 241 ret.add(a5); 242 ret.add(a6); 243 return ret; 244 } 245 246 template <class T> 247 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) 248 { 249 oneof_reader<T> ret; 250 ret.add(a1); 251 ret.add(a2); 252 ret.add(a3); 253 ret.add(a4); 254 ret.add(a5); 255 ret.add(a6); 256 ret.add(a7); 257 return ret; 258 } 259 260 template <class T> 261 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) 262 { 263 oneof_reader<T> ret; 264 ret.add(a1); 265 ret.add(a2); 266 ret.add(a3); 267 ret.add(a4); 268 ret.add(a5); 269 ret.add(a6); 270 ret.add(a7); 271 ret.add(a8); 272 return ret; 273 } 274 275 template <class T> 276 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) 277 { 278 oneof_reader<T> ret; 279 ret.add(a1); 280 ret.add(a2); 281 ret.add(a3); 282 ret.add(a4); 283 ret.add(a5); 284 ret.add(a6); 285 ret.add(a7); 286 ret.add(a8); 287 ret.add(a9); 288 return ret; 289 } 290 291 template <class T> 292 oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10) 293 { 294 oneof_reader<T> ret; 295 ret.add(a1); 296 ret.add(a2); 297 ret.add(a3); 298 ret.add(a4); 299 ret.add(a5); 300 ret.add(a6); 301 ret.add(a7); 302 ret.add(a8); 303 ret.add(a9); 304 ret.add(a10); 305 return ret; 306 } 307 308 //----- 309 310 class parser{ 311 public: 312 parser(){ 313 } 314 ~parser(){ 315 for (std::map<std::string, option_base*>::iterator p=options.begin(); 316 p!=options.end(); p++) 317 delete p->second; 318 } 319 320 void add(const std::string &name, 321 char short_name=0, 322 const std::string &desc=""){ 323 if (options.count(name)) throw cmdline_error("multiple definition: "+name); 324 options[name]=new option_without_value(name, short_name, desc); 325 ordered.push_back(options[name]); 326 } 327 328 template <class T> 329 void add(const std::string &name, 330 char short_name=0, 331 const std::string &desc="", 332 bool need=true, 333 const T def=T()){ 334 add(name, short_name, desc, need, def, default_reader<T>()); 335 } 336 337 template <class T, class F> 338 void add(const std::string &name, 339 char short_name=0, 340 const std::string &desc="", 341 bool need=true, 342 const T def=T(), 343 F reader=F()){ 344 if (options.count(name)) throw cmdline_error("multiple definition: "+name); 345 options[name]=new option_with_value_with_reader<T, F>(name, short_name, need, def, desc, reader); 346 ordered.push_back(options[name]); 347 } 348 349 void footer(const std::string &f){ 350 ftr=f; 351 } 352 353 void set_program_name(const std::string &name){ 354 prog_name=name; 355 } 356 357 bool exist(const std::string &name) const { 358 if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); 359 return options.find(name)->second->has_set(); 360 } 361 362 template <class T> 363 const T &get(const std::string &name) const { 364 if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); 365 const option_with_value<T> *p=dynamic_cast<const option_with_value<T>*>(options.find(name)->second); 366 if (p==NULL) throw cmdline_error("type mismatch flag '"+name+"'"); 367 return p->get(); 368 } 369 370 const std::vector<std::string> &rest() const { 371 return others; 372 } 373 374 bool parse(const std::string &arg){ 375 std::vector<std::string> args; 376 377 std::string buf; 378 bool in_quote=false; 379 for (std::string::size_type i=0; i<arg.length(); i++){ 380 if (arg[i]=='\"'){ 381 in_quote=!in_quote; 382 continue; 383 } 384 385 if (arg[i]==' ' && !in_quote){ 386 args.push_back(buf); 387 buf=""; 388 continue; 389 } 390 391 if (arg[i]=='\\'){ 392 i++; 393 if (i>=arg.length()){ 394 errors.push_back("unexpected occurrence of '\\' at end of string"); 395 return false; 396 } 397 } 398 399 buf+=arg[i]; 400 } 401 402 if (in_quote){ 403 errors.push_back("quote is not closed"); 404 return false; 405 } 406 407 if (buf.length()>0) 408 args.push_back(buf); 409 410 for (size_t i=0; i<args.size(); i++) 411 std::cout<<"\""<<args[i]<<"\""<<std::endl; 412 413 return parse(args); 414 } 415 416 bool parse(const std::vector<std::string> &args){ 417 int argc=static_cast<int>(args.size()); 418 std::vector<const char*> argv(argc); 419 420 for (int i=0; i<argc; i++) 421 argv[i]=args[i].c_str(); 422 423 return parse(argc, &argv[0]); 424 } 425 426 bool parse(int argc, const char * const argv[]){ 427 errors.clear(); 428 others.clear(); 429 430 if (argc<1){ 431 errors.push_back("argument number must be longer than 0"); 432 return false; 433 } 434 if (prog_name=="") 435 prog_name=argv[0]; 436 437 std::map<char, std::string> lookup; 438 for (std::map<std::string, option_base*>::iterator p=options.begin(); 439 p!=options.end(); p++){ 440 if (p->first.length()==0) continue; 441 char initial=p->second->short_name(); 442 if (initial){ 443 if (lookup.count(initial)>0){ 444 lookup[initial]=""; 445 errors.push_back(std::string("short option '")+initial+"' is ambiguous"); 446 return false; 447 } 448 else lookup[initial]=p->first; 449 } 450 } 451 452 for (int i=1; i<argc; i++){ 453 if (strncmp(argv[i], "--", 2)==0){ 454 const char *p=strchr(argv[i]+2, '='); 455 if (p){ 456 std::string name(argv[i]+2, p); 457 std::string val(p+1); 458 set_option(name, val); 459 } 460 else{ 461 std::string name(argv[i]+2); 462 if (options.count(name)==0){ 463 errors.push_back("undefined option: --"+name); 464 continue; 465 } 466 if (options[name]->has_value()){ 467 if (i+1>=argc){ 468 errors.push_back("option needs value: --"+name); 469 continue; 470 } 471 else{ 472 i++; 473 set_option(name, argv[i]); 474 } 475 } 476 else{ 477 set_option(name); 478 } 479 } 480 } 481 else if (strncmp(argv[i], "-", 1)==0){ 482 if (!argv[i][1]) continue; 483 char last=argv[i][1]; 484 for (int j=2; argv[i][j]; j++){ 485 last=argv[i][j]; 486 if (lookup.count(argv[i][j-1])==0){ 487 errors.push_back(std::string("undefined short option: -")+argv[i][j-1]); 488 continue; 489 } 490 if (lookup[argv[i][j-1]]==""){ 491 errors.push_back(std::string("ambiguous short option: -")+argv[i][j-1]); 492 continue; 493 } 494 set_option(lookup[argv[i][j-1]]); 495 } 496 497 if (lookup.count(last)==0){ 498 errors.push_back(std::string("undefined short option: -")+last); 499 continue; 500 } 501 if (lookup[last]==""){ 502 errors.push_back(std::string("ambiguous short option: -")+last); 503 continue; 504 } 505 506 if (i+1<argc && options[lookup[last]]->has_value()){ 507 set_option(lookup[last], argv[i+1]); 508 i++; 509 } 510 else{ 511 set_option(lookup[last]); 512 } 513 } 514 else{ 515 others.push_back(argv[i]); 516 } 517 } 518 519 for (std::map<std::string, option_base*>::iterator p=options.begin(); 520 p!=options.end(); p++) 521 if (!p->second->valid()) 522 errors.push_back("need option: --"+std::string(p->first)); 523 524 return errors.size()==0; 525 } 526 527 void parse_check(const std::string &arg){ 528 if (!options.count("help")) 529 add("help", '?', "print this message"); 530 check(0, parse(arg)); 531 } 532 533 void parse_check(const std::vector<std::string> &args){ 534 if (!options.count("help")) 535 add("help", '?', "print this message"); 536 check(args.size(), parse(args)); 537 } 538 539 void parse_check(int argc, char *argv[]){ 540 if (!options.count("help")) 541 add("help", '?', "print this message"); 542 check(argc, parse(argc, argv)); 543 } 544 545 std::string error() const{ 546 return errors.size()>0?errors[0]:""; 547 } 548 549 std::string error_full() const{ 550 std::ostringstream oss; 551 for (size_t i=0; i<errors.size(); i++) 552 oss<<errors[i]<<std::endl; 553 return oss.str(); 554 } 555 556 std::string usage() const { 557 std::ostringstream oss; 558 oss<<"usage: "<<prog_name<<" "; 559 for (size_t i=0; i<ordered.size(); i++){ 560 if (ordered[i]->must()) 561 oss<<ordered[i]->short_description()<<" "; 562 } 563 564 oss<<"[options] ... "<<ftr<<std::endl; 565 oss<<"options:"<<std::endl; 566 567 size_t max_width=0; 568 for (size_t i=0; i<ordered.size(); i++){ 569 max_width=std::max(max_width, ordered[i]->name().length()); 570 } 571 for (size_t i=0; i<ordered.size(); i++){ 572 if (ordered[i]->short_name()){ 573 oss<<" -"<<ordered[i]->short_name()<<", "; 574 } 575 else{ 576 oss<<" "; 577 } 578 579 oss<<"--"<<ordered[i]->name(); 580 for (size_t j=ordered[i]->name().length(); j<max_width+4; j++) 581 oss<<' '; 582 oss<<ordered[i]->description()<<std::endl; 583 } 584 return oss.str(); 585 } 586 587 private: 588 589 void check(int argc, bool ok){ 590 if ((argc==1 && !ok) || exist("help")){ 591 std::cerr<<usage(); 592 exit(0); 593 } 594 595 if (!ok){ 596 std::cerr<<error()<<std::endl<<usage(); 597 exit(1); 598 } 599 } 600 601 void set_option(const std::string &name){ 602 if (options.count(name)==0){ 603 errors.push_back("undefined option: --"+name); 604 return; 605 } 606 if (!options[name]->set()){ 607 errors.push_back("option needs value: --"+name); 608 return; 609 } 610 } 611 612 void set_option(const std::string &name, const std::string &value){ 613 if (options.count(name)==0){ 614 errors.push_back("undefined option: --"+name); 615 return; 616 } 617 if (!options[name]->set(value)){ 618 errors.push_back("option value is invalid: --"+name+"="+value); 619 return; 620 } 621 } 622 623 class option_base{ 624 public: 625 virtual ~option_base(){} 626 627 virtual bool has_value() const=0; 628 virtual bool set()=0; 629 virtual bool set(const std::string &value)=0; 630 virtual bool has_set() const=0; 631 virtual bool valid() const=0; 632 virtual bool must() const=0; 633 634 virtual const std::string &name() const=0; 635 virtual char short_name() const=0; 636 virtual const std::string &description() const=0; 637 virtual std::string short_description() const=0; 638 }; 639 640 class option_without_value : public option_base { 641 public: 642 option_without_value(const std::string &name, 643 char short_name, 644 const std::string &desc) 645 :nam(name), snam(short_name), desc(desc), has(false){ 646 } 647 ~option_without_value(){} 648 649 bool has_value() const { return false; } 650 651 bool set(){ 652 has=true; 653 return true; 654 } 655 656 bool set(const std::string &){ 657 return false; 658 } 659 660 bool has_set() const { 661 return has; 662 } 663 664 bool valid() const{ 665 return true; 666 } 667 668 bool must() const{ 669 return false; 670 } 671 672 const std::string &name() const{ 673 return nam; 674 } 675 676 char short_name() const{ 677 return snam; 678 } 679 680 const std::string &description() const { 681 return desc; 682 } 683 684 std::string short_description() const{ 685 return "--"+nam; 686 } 687 688 private: 689 std::string nam; 690 char snam; 691 std::string desc; 692 bool has; 693 }; 694 695 template <class T> 696 class option_with_value : public option_base { 697 public: 698 option_with_value(const std::string &name, 699 char short_name, 700 bool need, 701 const T &def, 702 const std::string &desc) 703 : nam(name), snam(short_name), need(need), has(false) 704 , def(def), actual(def) { 705 this->desc=full_description(desc); 706 } 707 ~option_with_value(){} 708 709 const T &get() const { 710 return actual; 711 } 712 713 bool has_value() const { return true; } 714 715 bool set(){ 716 return false; 717 } 718 719 bool set(const std::string &value){ 720 try{ 721 actual=read(value); 722 has=true; 723 } 724 catch(const std::exception &e){ 725 return false; 726 } 727 return true; 728 } 729 730 bool has_set() const{ 731 return has; 732 } 733 734 bool valid() const{ 735 if (need && !has) return false; 736 return true; 737 } 738 739 bool must() const{ 740 return need; 741 } 742 743 const std::string &name() const{ 744 return nam; 745 } 746 747 char short_name() const{ 748 return snam; 749 } 750 751 const std::string &description() const { 752 return desc; 753 } 754 755 std::string short_description() const{ 756 return "--"+nam+"="+detail::readable_typename<T>(); 757 } 758 759 protected: 760 std::string full_description(const std::string &desc){ 761 return 762 desc+" ("+detail::readable_typename<T>()+ 763 (need?"":" [="+detail::default_value<T>(def)+"]") 764 +")"; 765 } 766 767 virtual T read(const std::string &s)=0; 768 769 std::string nam; 770 char snam; 771 bool need; 772 std::string desc; 773 774 bool has; 775 T def; 776 T actual; 777 }; 778 779 template <class T, class F> 780 class option_with_value_with_reader : public option_with_value<T> { 781 public: 782 option_with_value_with_reader(const std::string &name, 783 char short_name, 784 bool need, 785 const T def, 786 const std::string &desc, 787 F reader) 788 : option_with_value<T>(name, short_name, need, def, desc), reader(reader){ 789 } 790 791 private: 792 T read(const std::string &s){ 793 return reader(s); 794 } 795 796 F reader; 797 }; 798 799 std::map<std::string, option_base*> options; 800 std::vector<option_base*> ordered; 801 std::string ftr; 802 803 std::string prog_name; 804 std::vector<std::string> others; 805 806 std::vector<std::string> errors; 807 }; 808 809 } // cmdline 810