Commit fdf49ba719b15c5dc830d540bcbf525bfd5ad3a0
Merge branch 'default_implicit'
Showing
2 changed files
with
178 additions
and
41 deletions
src/cxxopts.hpp
| @@ -214,15 +214,36 @@ namespace cxxopts | @@ -214,15 +214,36 @@ namespace cxxopts | ||
| 214 | 214 | ||
| 215 | namespace cxxopts | 215 | namespace cxxopts |
| 216 | { | 216 | { |
| 217 | - class Value | 217 | + class Value : public std::enable_shared_from_this<Value> |
| 218 | { | 218 | { |
| 219 | public: | 219 | public: |
| 220 | 220 | ||
| 221 | virtual void | 221 | virtual void |
| 222 | parse(const std::string& text) const = 0; | 222 | parse(const std::string& text) const = 0; |
| 223 | 223 | ||
| 224 | + virtual void | ||
| 225 | + parse() const = 0; | ||
| 226 | + | ||
| 224 | virtual bool | 227 | virtual bool |
| 225 | has_arg() const = 0; | 228 | has_arg() const = 0; |
| 229 | + | ||
| 230 | + virtual bool | ||
| 231 | + has_default() const = 0; | ||
| 232 | + | ||
| 233 | + virtual bool | ||
| 234 | + has_implicit() const = 0; | ||
| 235 | + | ||
| 236 | + virtual std::string | ||
| 237 | + get_default_value() const = 0; | ||
| 238 | + | ||
| 239 | + virtual std::string | ||
| 240 | + get_implicit_value() const = 0; | ||
| 241 | + | ||
| 242 | + virtual std::shared_ptr<Value> | ||
| 243 | + default_value(const std::string& value) = 0; | ||
| 244 | + | ||
| 245 | + virtual std::shared_ptr<Value> | ||
| 246 | + implicit_value(const std::string& value) = 0; | ||
| 226 | }; | 247 | }; |
| 227 | 248 | ||
| 228 | class OptionException : public std::exception | 249 | class OptionException : public std::exception |
| @@ -375,7 +396,7 @@ namespace cxxopts | @@ -375,7 +396,7 @@ namespace cxxopts | ||
| 375 | 396 | ||
| 376 | inline | 397 | inline |
| 377 | void | 398 | void |
| 378 | - parse_value(const std::string& text, bool& value) | 399 | + parse_value(const std::string& /*text*/, bool& value) |
| 379 | { | 400 | { |
| 380 | //TODO recognise on, off, yes, no, enable, disable | 401 | //TODO recognise on, off, yes, no, enable, disable |
| 381 | //so that we can write --long=yes explicitly | 402 | //so that we can write --long=yes explicitly |
| @@ -395,16 +416,16 @@ namespace cxxopts | @@ -395,16 +416,16 @@ namespace cxxopts | ||
| 395 | }; | 416 | }; |
| 396 | 417 | ||
| 397 | template <typename T> | 418 | template <typename T> |
| 398 | - class default_value : public Value | 419 | + class standard_value : public Value |
| 399 | { | 420 | { |
| 400 | public: | 421 | public: |
| 401 | - default_value() | 422 | + standard_value() |
| 402 | : m_result(std::make_shared<T>()) | 423 | : m_result(std::make_shared<T>()) |
| 403 | , m_store(m_result.get()) | 424 | , m_store(m_result.get()) |
| 404 | { | 425 | { |
| 405 | } | 426 | } |
| 406 | 427 | ||
| 407 | - default_value(T* t) | 428 | + standard_value(T* t) |
| 408 | : m_store(t) | 429 | : m_store(t) |
| 409 | { | 430 | { |
| 410 | } | 431 | } |
| @@ -412,7 +433,20 @@ namespace cxxopts | @@ -412,7 +433,20 @@ namespace cxxopts | ||
| 412 | void | 433 | void |
| 413 | parse(const std::string& text) const | 434 | parse(const std::string& text) const |
| 414 | { | 435 | { |
| 415 | - parse_value(text, *m_store); | 436 | + if (m_implicit && text.empty()) |
| 437 | + { | ||
| 438 | + parse_value(m_implicit_value, *m_store); | ||
| 439 | + } | ||
| 440 | + else | ||
| 441 | + { | ||
| 442 | + parse_value(text, *m_store); | ||
| 443 | + } | ||
| 444 | + } | ||
| 445 | + | ||
| 446 | + void | ||
| 447 | + parse() const | ||
| 448 | + { | ||
| 449 | + parse_value(m_default_value, *m_store); | ||
| 416 | } | 450 | } |
| 417 | 451 | ||
| 418 | bool | 452 | bool |
| @@ -421,6 +455,44 @@ namespace cxxopts | @@ -421,6 +455,44 @@ namespace cxxopts | ||
| 421 | return value_has_arg<T>::value; | 455 | return value_has_arg<T>::value; |
| 422 | } | 456 | } |
| 423 | 457 | ||
| 458 | + bool | ||
| 459 | + has_default() const | ||
| 460 | + { | ||
| 461 | + return m_default; | ||
| 462 | + } | ||
| 463 | + | ||
| 464 | + bool | ||
| 465 | + has_implicit() const | ||
| 466 | + { | ||
| 467 | + return m_implicit; | ||
| 468 | + } | ||
| 469 | + | ||
| 470 | + virtual std::shared_ptr<Value> | ||
| 471 | + default_value(const std::string& value){ | ||
| 472 | + m_default = true; | ||
| 473 | + m_default_value = value; | ||
| 474 | + return shared_from_this(); | ||
| 475 | + } | ||
| 476 | + | ||
| 477 | + virtual std::shared_ptr<Value> | ||
| 478 | + implicit_value(const std::string& value){ | ||
| 479 | + m_implicit = true; | ||
| 480 | + m_implicit_value = value; | ||
| 481 | + return shared_from_this(); | ||
| 482 | + } | ||
| 483 | + | ||
| 484 | + std::string | ||
| 485 | + get_default_value() const | ||
| 486 | + { | ||
| 487 | + return m_default_value; | ||
| 488 | + } | ||
| 489 | + | ||
| 490 | + std::string | ||
| 491 | + get_implicit_value() const | ||
| 492 | + { | ||
| 493 | + return m_implicit_value; | ||
| 494 | + } | ||
| 495 | + | ||
| 424 | const T& | 496 | const T& |
| 425 | get() const | 497 | get() const |
| 426 | { | 498 | { |
| @@ -434,25 +506,28 @@ namespace cxxopts | @@ -434,25 +506,28 @@ namespace cxxopts | ||
| 434 | } | 506 | } |
| 435 | } | 507 | } |
| 436 | 508 | ||
| 437 | - private: | 509 | + protected: |
| 438 | std::shared_ptr<T> m_result; | 510 | std::shared_ptr<T> m_result; |
| 439 | T* m_store; | 511 | T* m_store; |
| 512 | + bool m_default = false; | ||
| 513 | + std::string m_default_value; | ||
| 514 | + bool m_implicit = false; | ||
| 515 | + std::string m_implicit_value; | ||
| 440 | }; | 516 | }; |
| 441 | - | ||
| 442 | } | 517 | } |
| 443 | 518 | ||
| 444 | template <typename T> | 519 | template <typename T> |
| 445 | std::shared_ptr<Value> | 520 | std::shared_ptr<Value> |
| 446 | value() | 521 | value() |
| 447 | { | 522 | { |
| 448 | - return std::make_shared<values::default_value<T>>(); | 523 | + return std::make_shared<values::standard_value<T>>(); |
| 449 | } | 524 | } |
| 450 | 525 | ||
| 451 | template <typename T> | 526 | template <typename T> |
| 452 | std::shared_ptr<Value> | 527 | std::shared_ptr<Value> |
| 453 | value(T& t) | 528 | value(T& t) |
| 454 | { | 529 | { |
| 455 | - return std::make_shared<values::default_value<T>>(&t); | 530 | + return std::make_shared<values::standard_value<T>>(&t); |
| 456 | } | 531 | } |
| 457 | 532 | ||
| 458 | class OptionAdder; | 533 | class OptionAdder; |
| @@ -490,17 +565,28 @@ namespace cxxopts | @@ -490,17 +565,28 @@ namespace cxxopts | ||
| 490 | ++m_count; | 565 | ++m_count; |
| 491 | } | 566 | } |
| 492 | 567 | ||
| 568 | + void | ||
| 569 | + parse_default() | ||
| 570 | + { | ||
| 571 | + m_value->parse(); | ||
| 572 | + ++m_count; | ||
| 573 | + } | ||
| 574 | + | ||
| 493 | int | 575 | int |
| 494 | count() const | 576 | count() const |
| 495 | { | 577 | { |
| 496 | return m_count; | 578 | return m_count; |
| 497 | } | 579 | } |
| 498 | 580 | ||
| 581 | + const Value& value() const { | ||
| 582 | + return *m_value; | ||
| 583 | + } | ||
| 584 | + | ||
| 499 | template <typename T> | 585 | template <typename T> |
| 500 | const T& | 586 | const T& |
| 501 | as() const | 587 | as() const |
| 502 | { | 588 | { |
| 503 | - return dynamic_cast<const values::default_value<T>&>(*m_value).get(); | 589 | + return dynamic_cast<const values::standard_value<T>&>(*m_value).get(); |
| 504 | } | 590 | } |
| 505 | 591 | ||
| 506 | private: | 592 | private: |
| @@ -515,6 +601,10 @@ namespace cxxopts | @@ -515,6 +601,10 @@ namespace cxxopts | ||
| 515 | std::string l; | 601 | std::string l; |
| 516 | String desc; | 602 | String desc; |
| 517 | bool has_arg; | 603 | bool has_arg; |
| 604 | + bool has_default; | ||
| 605 | + std::string default_value; | ||
| 606 | + bool has_implicit; | ||
| 607 | + std::string implicit_value; | ||
| 518 | std::string arg_help; | 608 | std::string arg_help; |
| 519 | }; | 609 | }; |
| 520 | 610 | ||
| @@ -622,7 +712,7 @@ namespace cxxopts | @@ -622,7 +712,7 @@ namespace cxxopts | ||
| 622 | ( | 712 | ( |
| 623 | int argc, | 713 | int argc, |
| 624 | char* argv[], | 714 | char* argv[], |
| 625 | - int argPos, | 715 | + int& current, |
| 626 | std::shared_ptr<OptionDetails> value, | 716 | std::shared_ptr<OptionDetails> value, |
| 627 | const std::string& name | 717 | const std::string& name |
| 628 | ); | 718 | ); |
| @@ -686,12 +776,12 @@ namespace cxxopts | @@ -686,12 +776,12 @@ namespace cxxopts | ||
| 686 | String | 776 | String |
| 687 | format_option | 777 | format_option |
| 688 | ( | 778 | ( |
| 689 | - const std::string& s, | ||
| 690 | - const std::string& l, | ||
| 691 | - bool has_arg, | ||
| 692 | - const std::string& arg_help | 779 | + const HelpOptionDetails& o |
| 693 | ) | 780 | ) |
| 694 | { | 781 | { |
| 782 | + auto& s = o.s; | ||
| 783 | + auto& l = o.l; | ||
| 784 | + | ||
| 695 | String result = " "; | 785 | String result = " "; |
| 696 | 786 | ||
| 697 | if (s.size() > 0) | 787 | if (s.size() > 0) |
| @@ -708,15 +798,17 @@ namespace cxxopts | @@ -708,15 +798,17 @@ namespace cxxopts | ||
| 708 | result += " --" + toLocalString(l); | 798 | result += " --" + toLocalString(l); |
| 709 | } | 799 | } |
| 710 | 800 | ||
| 711 | - if (has_arg) | 801 | + if (o.has_arg) |
| 712 | { | 802 | { |
| 713 | - if (arg_help.size() != 0) | 803 | + auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; |
| 804 | + | ||
| 805 | + if (o.has_implicit) | ||
| 714 | { | 806 | { |
| 715 | - result += " " + toLocalString(arg_help); | 807 | + result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; |
| 716 | } | 808 | } |
| 717 | else | 809 | else |
| 718 | { | 810 | { |
| 719 | - result += " arg"; | 811 | + result += " " + arg; |
| 720 | } | 812 | } |
| 721 | } | 813 | } |
| 722 | 814 | ||
| @@ -726,20 +818,27 @@ namespace cxxopts | @@ -726,20 +818,27 @@ namespace cxxopts | ||
| 726 | String | 818 | String |
| 727 | format_description | 819 | format_description |
| 728 | ( | 820 | ( |
| 729 | - const String& text, | 821 | + const HelpOptionDetails& o, |
| 730 | int start, | 822 | int start, |
| 731 | int width | 823 | int width |
| 732 | ) | 824 | ) |
| 733 | { | 825 | { |
| 826 | + auto desc = o.desc; | ||
| 827 | + | ||
| 828 | + if (o.has_default) | ||
| 829 | + { | ||
| 830 | + desc += toLocalString(" (default:" + o.default_value + ")"); | ||
| 831 | + } | ||
| 832 | + | ||
| 734 | String result; | 833 | String result; |
| 735 | 834 | ||
| 736 | - auto current = std::begin(text); | 835 | + auto current = std::begin(desc); |
| 737 | auto startLine = current; | 836 | auto startLine = current; |
| 738 | auto lastSpace = current; | 837 | auto lastSpace = current; |
| 739 | 838 | ||
| 740 | int size = 0; | 839 | int size = 0; |
| 741 | 840 | ||
| 742 | - while (current != std::end(text)) | 841 | + while (current != std::end(desc)) |
| 743 | { | 842 | { |
| 744 | if (*current == ' ') | 843 | if (*current == ' ') |
| 745 | { | 844 | { |
| @@ -816,7 +915,7 @@ void | @@ -816,7 +915,7 @@ void | ||
| 816 | Options::parse_option | 915 | Options::parse_option |
| 817 | ( | 916 | ( |
| 818 | std::shared_ptr<OptionDetails> value, | 917 | std::shared_ptr<OptionDetails> value, |
| 819 | - const std::string& name, | 918 | + const std::string& /*name*/, |
| 820 | const std::string& arg | 919 | const std::string& arg |
| 821 | ) | 920 | ) |
| 822 | { | 921 | { |
| @@ -828,17 +927,34 @@ Options::checked_parse_arg | @@ -828,17 +927,34 @@ Options::checked_parse_arg | ||
| 828 | ( | 927 | ( |
| 829 | int argc, | 928 | int argc, |
| 830 | char* argv[], | 929 | char* argv[], |
| 831 | - int argPos, | 930 | + int& current, |
| 832 | std::shared_ptr<OptionDetails> value, | 931 | std::shared_ptr<OptionDetails> value, |
| 833 | const std::string& name | 932 | const std::string& name |
| 834 | ) | 933 | ) |
| 835 | { | 934 | { |
| 836 | - if (argPos >= argc) | 935 | + if (current + 1 >= argc) |
| 837 | { | 936 | { |
| 838 | - throw missing_argument_exception(name); | 937 | + if (value->value().has_implicit()) |
| 938 | + { | ||
| 939 | + parse_option(value, name, ""); | ||
| 940 | + } | ||
| 941 | + else | ||
| 942 | + { | ||
| 943 | + throw missing_argument_exception(name); | ||
| 944 | + } | ||
| 945 | + } | ||
| 946 | + else | ||
| 947 | + { | ||
| 948 | + if (argv[current + 1][0] == '-' && value->value().has_implicit()) | ||
| 949 | + { | ||
| 950 | + parse_option(value, name, ""); | ||
| 951 | + } | ||
| 952 | + else | ||
| 953 | + { | ||
| 954 | + parse_option(value, name, argv[current + 1]); | ||
| 955 | + ++current; | ||
| 956 | + } | ||
| 839 | } | 957 | } |
| 840 | - | ||
| 841 | - parse_option(value, name, argv[argPos]); | ||
| 842 | } | 958 | } |
| 843 | 959 | ||
| 844 | void | 960 | void |
| @@ -909,7 +1025,7 @@ Options::parse(int& argc, char**& argv) | @@ -909,7 +1025,7 @@ Options::parse(int& argc, char**& argv) | ||
| 909 | { | 1025 | { |
| 910 | const std::string& s = result[4]; | 1026 | const std::string& s = result[4]; |
| 911 | 1027 | ||
| 912 | - for (int i = 0; i != s.size(); ++i) | 1028 | + for (std::size_t i = 0; i != s.size(); ++i) |
| 913 | { | 1029 | { |
| 914 | std::string name(1, s[i]); | 1030 | std::string name(1, s[i]); |
| 915 | auto iter = m_options.find(name); | 1031 | auto iter = m_options.find(name); |
| @@ -931,8 +1047,11 @@ Options::parse(int& argc, char**& argv) | @@ -931,8 +1047,11 @@ Options::parse(int& argc, char**& argv) | ||
| 931 | //it must be the last argument | 1047 | //it must be the last argument |
| 932 | if (i + 1 == s.size()) | 1048 | if (i + 1 == s.size()) |
| 933 | { | 1049 | { |
| 934 | - checked_parse_arg(argc, argv, current+1, value, name); | ||
| 935 | - ++current; | 1050 | + checked_parse_arg(argc, argv, current, value, name); |
| 1051 | + } | ||
| 1052 | + else if (value->value().has_implicit()) | ||
| 1053 | + { | ||
| 1054 | + parse_option(value, name, ""); | ||
| 936 | } | 1055 | } |
| 937 | else | 1056 | else |
| 938 | { | 1057 | { |
| @@ -973,9 +1092,7 @@ Options::parse(int& argc, char**& argv) | @@ -973,9 +1092,7 @@ Options::parse(int& argc, char**& argv) | ||
| 973 | if (opt->has_arg()) | 1092 | if (opt->has_arg()) |
| 974 | { | 1093 | { |
| 975 | //parse the next argument | 1094 | //parse the next argument |
| 976 | - checked_parse_arg(argc, argv, current + 1, opt, name); | ||
| 977 | - | ||
| 978 | - ++current; | 1095 | + checked_parse_arg(argc, argv, current, opt, name); |
| 979 | } | 1096 | } |
| 980 | else | 1097 | else |
| 981 | { | 1098 | { |
| @@ -990,6 +1107,16 @@ Options::parse(int& argc, char**& argv) | @@ -990,6 +1107,16 @@ Options::parse(int& argc, char**& argv) | ||
| 990 | ++current; | 1107 | ++current; |
| 991 | } | 1108 | } |
| 992 | 1109 | ||
| 1110 | + for (auto& opt : m_options) | ||
| 1111 | + { | ||
| 1112 | + auto& detail = opt.second; | ||
| 1113 | + auto& value = detail->value(); | ||
| 1114 | + | ||
| 1115 | + if(!detail->count() && value.has_default()){ | ||
| 1116 | + detail->parse_default(); | ||
| 1117 | + } | ||
| 1118 | + } | ||
| 1119 | + | ||
| 993 | argc = nextKeep; | 1120 | argc = nextKeep; |
| 994 | } | 1121 | } |
| 995 | 1122 | ||
| @@ -1019,10 +1146,12 @@ Options::add_option | @@ -1019,10 +1146,12 @@ Options::add_option | ||
| 1019 | 1146 | ||
| 1020 | //add the help details | 1147 | //add the help details |
| 1021 | auto& options = m_help[group]; | 1148 | auto& options = m_help[group]; |
| 1022 | - options.options. | ||
| 1023 | - emplace_back(HelpOptionDetails{s, l, stringDesc, value->has_arg(), | ||
| 1024 | - std::move(arg_help)} | ||
| 1025 | - ); | 1149 | + |
| 1150 | + options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, | ||
| 1151 | + value->has_arg(), | ||
| 1152 | + value->has_default(), value->get_default_value(), | ||
| 1153 | + value->has_implicit(), value->get_implicit_value(), | ||
| 1154 | + std::move(arg_help)}); | ||
| 1026 | } | 1155 | } |
| 1027 | 1156 | ||
| 1028 | void | 1157 | void |
| @@ -1064,7 +1193,7 @@ Options::help_one_group(const std::string& g) const | @@ -1064,7 +1193,7 @@ Options::help_one_group(const std::string& g) const | ||
| 1064 | 1193 | ||
| 1065 | for (const auto& o : group->second.options) | 1194 | for (const auto& o : group->second.options) |
| 1066 | { | 1195 | { |
| 1067 | - auto s = format_option(o.s, o.l, o.has_arg, o.arg_help); | 1196 | + auto s = format_option(o); |
| 1068 | longest = std::max(longest, stringLength(s)); | 1197 | longest = std::max(longest, stringLength(s)); |
| 1069 | format.push_back(std::make_pair(s, String())); | 1198 | format.push_back(std::make_pair(s, String())); |
| 1070 | } | 1199 | } |
| @@ -1077,7 +1206,7 @@ Options::help_one_group(const std::string& g) const | @@ -1077,7 +1206,7 @@ Options::help_one_group(const std::string& g) const | ||
| 1077 | auto fiter = format.begin(); | 1206 | auto fiter = format.begin(); |
| 1078 | for (const auto& o : group->second.options) | 1207 | for (const auto& o : group->second.options) |
| 1079 | { | 1208 | { |
| 1080 | - auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed); | 1209 | + auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); |
| 1081 | 1210 | ||
| 1082 | result += fiter->first; | 1211 | result += fiter->first; |
| 1083 | if (stringLength(fiter->first) > longest) | 1212 | if (stringLength(fiter->first) > longest) |
src/example.cpp
| @@ -38,6 +38,8 @@ int main(int argc, char* argv[]) | @@ -38,6 +38,8 @@ int main(int argc, char* argv[]) | ||
| 38 | ("a,apple", "an apple", cxxopts::value<bool>(apple)) | 38 | ("a,apple", "an apple", cxxopts::value<bool>(apple)) |
| 39 | ("b,bob", "Bob") | 39 | ("b,bob", "Bob") |
| 40 | ("f,file", "File", cxxopts::value<std::vector<std::string>>(), "FILE") | 40 | ("f,file", "File", cxxopts::value<std::vector<std::string>>(), "FILE") |
| 41 | + ("o,output", "Output file", cxxopts::value<std::string>() | ||
| 42 | + ->default_value("a.out")->implicit_value("b.def"), "BIN") | ||
| 41 | ("positional", | 43 | ("positional", |
| 42 | "Positional arguments: these are the arguments that are entered " | 44 | "Positional arguments: these are the arguments that are entered " |
| 43 | "without an option", cxxopts::value<std::string>()) | 45 | "without an option", cxxopts::value<std::string>()) |
| @@ -92,6 +94,12 @@ int main(int argc, char* argv[]) | @@ -92,6 +94,12 @@ int main(int argc, char* argv[]) | ||
| 92 | << std::endl; | 94 | << std::endl; |
| 93 | } | 95 | } |
| 94 | 96 | ||
| 97 | + if (options.count("output")) | ||
| 98 | + { | ||
| 99 | + std::cout << "Output = " << options["output"].as<std::string>() | ||
| 100 | + << std::endl; | ||
| 101 | + } | ||
| 102 | + | ||
| 95 | if (options.count("int")) | 103 | if (options.count("int")) |
| 96 | { | 104 | { |
| 97 | std::cout << "int = " << options["int"].as<int>() << std::endl; | 105 | std::cout << "int = " << options["int"].as<int>() << std::endl; |