Commit 8893afe13cc47dd0be4f25b5ae491e652c146098
Merge branch '2_0'
Showing
8 changed files
with
621 additions
and
295 deletions
CHANGELOG.md
0 → 100644
| 1 | +# Changelog | |
| 2 | + | |
| 3 | +This is the changelog for `cxxopts`, a C++11 library for parsing command line | |
| 4 | +options. The project adheres to semantic versioning. | |
| 5 | + | |
| 6 | +## 2.0 | |
| 7 | + | |
| 8 | +### Changed | |
| 9 | + | |
| 10 | +* `Options::parse` returns a ParseResult rather than storing the parse | |
| 11 | + result internally. | |
| 12 | +* Options with default values now get counted as appearing once if they | |
| 13 | + were not specified by the user. | |
| 14 | + | |
| 15 | +### Added | |
| 16 | + | |
| 17 | +* A new `ParseResult` object that is the immutable result of parsing. It | |
| 18 | + responds to the same `count` and `operator[]` as `Options` of 1.x did. | |
| 19 | +* The function `ParseResult::arguments` returns a vector of the parsed | |
| 20 | + arguments to iterate through in the order they were provided. | |
| 21 | +* The symbol `cxxopts::version` for the version of the library. | |
| 22 | +* Booleans can be specified with various strings and explicitly set false. | |
| 23 | + | |
| 24 | +## 1.x | |
| 25 | + | |
| 26 | +The 1.x series was the first major version of the library, with release numbers | |
| 27 | +starting to follow semantic versioning, after 0.x being unstable. It never had | |
| 28 | +a changelog maintained for it. Releases mostly contained bug fixes, with the | |
| 29 | +occasional feature added. | ... | ... |
README.md
| ... | ... | @@ -39,12 +39,12 @@ Any type can be given as long as it can be parsed, with operator>>. |
| 39 | 39 | |
| 40 | 40 | To parse the command line do: |
| 41 | 41 | |
| 42 | - options.parse(argc, argv); | |
| 42 | + auto result = options.parse(argc, argv); | |
| 43 | 43 | |
| 44 | -To retrieve an option use `options.count("option")` to get the number of times | |
| 44 | +To retrieve an option use `result.count("option")` to get the number of times | |
| 45 | 45 | it appeared, and |
| 46 | 46 | |
| 47 | - options["opt"].as<type>() | |
| 47 | + result["opt"].as<type>() | |
| 48 | 48 | |
| 49 | 49 | to get its value. If "opt" doesn't exist, or isn't of the right type, then an |
| 50 | 50 | exception will be thrown. |
| ... | ... | @@ -84,6 +84,17 @@ If an option had both, then not specifying it would give the value `"value"`, |
| 84 | 84 | writing it on the command line as `--option` would give the value `"implicit"`, |
| 85 | 85 | and writing `--option=another` would give it the value `"another"`. |
| 86 | 86 | |
| 87 | +Note that the default and implicit value is always stored as a string, | |
| 88 | +regardless of the type that you want to store it in. It will be parsed as | |
| 89 | +though it was given on the command line. | |
| 90 | + | |
| 91 | +## Boolean values | |
| 92 | + | |
| 93 | +Boolean options have a default implicit value of `"true"`, which can be | |
| 94 | +overridden. The effect is that writing `-o` by itself will set option `o` to | |
| 95 | +`true`. However, they can also be written with various strings using either | |
| 96 | +`=value` or the next argument. | |
| 97 | + | |
| 87 | 98 | # Linking |
| 88 | 99 | |
| 89 | 100 | This is a header only library. |
| ... | ... | @@ -93,9 +104,7 @@ This is a header only library. |
| 93 | 104 | The only build requirement is a C++ compiler that supports C++11 regular |
| 94 | 105 | expressions. For example GCC >= 4.9 or clang with libc++. |
| 95 | 106 | |
| 96 | - | |
| 97 | 107 | # TODO list |
| 98 | 108 | |
| 99 | 109 | * Allow unrecognised options. |
| 100 | 110 | * Various help strings. |
| 101 | -* Unicode aware for help strings. | ... | ... |
include/cxxopts.hpp
| ... | ... | @@ -22,13 +22,8 @@ THE SOFTWARE. |
| 22 | 22 | |
| 23 | 23 | */ |
| 24 | 24 | |
| 25 | -#ifndef CXX_OPTS_HPP | |
| 26 | -#define CXX_OPTS_HPP | |
| 27 | - | |
| 28 | -#if defined(__GNUC__) | |
| 29 | -#pragma GCC diagnostic push | |
| 30 | -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" | |
| 31 | -#endif | |
| 25 | +#ifndef CXXOPTS_HPP_INCLUDED | |
| 26 | +#define CXXOPTS_HPP_INCLUDED | |
| 32 | 27 | |
| 33 | 28 | #include <cstring> |
| 34 | 29 | #include <cctype> |
| ... | ... | @@ -39,9 +34,17 @@ THE SOFTWARE. |
| 39 | 34 | #include <regex> |
| 40 | 35 | #include <sstream> |
| 41 | 36 | #include <string> |
| 37 | +#include <unordered_map> | |
| 42 | 38 | #include <unordered_set> |
| 43 | 39 | #include <vector> |
| 44 | 40 | |
| 41 | +namespace cxxopts | |
| 42 | +{ | |
| 43 | + static constexpr struct { | |
| 44 | + uint8_t major, minor, patch; | |
| 45 | + } version = {2, 0, 0}; | |
| 46 | +} | |
| 47 | + | |
| 45 | 48 | //when we ask cxxopts to use Unicode, help strings are processed using ICU, |
| 46 | 49 | //which results in the correct lengths being computed for strings when they |
| 47 | 50 | //are formatted for the help output |
| ... | ... | @@ -168,12 +171,14 @@ namespace cxxopts |
| 168 | 171 | |
| 169 | 172 | namespace std |
| 170 | 173 | { |
| 174 | + inline | |
| 171 | 175 | cxxopts::UnicodeStringIterator |
| 172 | 176 | begin(const icu::UnicodeString& s) |
| 173 | 177 | { |
| 174 | 178 | return cxxopts::UnicodeStringIterator(&s, 0); |
| 175 | 179 | } |
| 176 | 180 | |
| 181 | + inline | |
| 177 | 182 | cxxopts::UnicodeStringIterator |
| 178 | 183 | end(const icu::UnicodeString& s) |
| 179 | 184 | { |
| ... | ... | @@ -258,6 +263,12 @@ namespace cxxopts |
| 258 | 263 | { |
| 259 | 264 | public: |
| 260 | 265 | |
| 266 | + virtual ~Value() = default; | |
| 267 | + | |
| 268 | + virtual | |
| 269 | + std::shared_ptr<Value> | |
| 270 | + clone() const = 0; | |
| 271 | + | |
| 261 | 272 | virtual void |
| 262 | 273 | parse(const std::string& text) const = 0; |
| 263 | 274 | |
| ... | ... | @@ -265,9 +276,6 @@ namespace cxxopts |
| 265 | 276 | parse() const = 0; |
| 266 | 277 | |
| 267 | 278 | virtual bool |
| 268 | - has_arg() const = 0; | |
| 269 | - | |
| 270 | - virtual bool | |
| 271 | 279 | has_default() const = 0; |
| 272 | 280 | |
| 273 | 281 | virtual bool |
| ... | ... | @@ -287,6 +295,9 @@ namespace cxxopts |
| 287 | 295 | |
| 288 | 296 | virtual std::shared_ptr<Value> |
| 289 | 297 | implicit_value(const std::string& value) = 0; |
| 298 | + | |
| 299 | + virtual bool | |
| 300 | + is_boolean() const = 0; | |
| 290 | 301 | }; |
| 291 | 302 | |
| 292 | 303 | class OptionException : public std::exception |
| ... | ... | @@ -385,7 +396,7 @@ namespace cxxopts |
| 385 | 396 | ) |
| 386 | 397 | : OptionParseException( |
| 387 | 398 | u8"Option " + LQUOTE + option + RQUOTE + |
| 388 | - u8" does not take an argument, but argument" + | |
| 399 | + u8" does not take an argument, but argument " + | |
| 389 | 400 | LQUOTE + arg + RQUOTE + " given" |
| 390 | 401 | ) |
| 391 | 402 | { |
| ... | ... | @@ -432,6 +443,10 @@ namespace cxxopts |
| 432 | 443 | { |
| 433 | 444 | std::basic_regex<char> integer_pattern |
| 434 | 445 | ("(-)?(0x)?([1-9a-zA-Z][0-9a-zA-Z]*)|((0x)?0)"); |
| 446 | + std::basic_regex<char> truthy_pattern | |
| 447 | + ("t|true|T|True"); | |
| 448 | + std::basic_regex<char> falsy_pattern | |
| 449 | + ("(f|false|F|False)?"); | |
| 435 | 450 | } |
| 436 | 451 | |
| 437 | 452 | namespace detail |
| ... | ... | @@ -561,11 +576,6 @@ namespace cxxopts |
| 561 | 576 | value = checked_negate<T>(result, |
| 562 | 577 | text, |
| 563 | 578 | std::integral_constant<bool, is_signed>()); |
| 564 | - //if (!is_signed) | |
| 565 | - //{ | |
| 566 | - // throw argument_incorrect_type(text); | |
| 567 | - //} | |
| 568 | - //value = -result; | |
| 569 | 579 | } |
| 570 | 580 | else |
| 571 | 581 | { |
| ... | ... | @@ -641,11 +651,25 @@ namespace cxxopts |
| 641 | 651 | |
| 642 | 652 | inline |
| 643 | 653 | void |
| 644 | - parse_value(const std::string& /*text*/, bool& value) | |
| 654 | + parse_value(const std::string& text, bool& value) | |
| 645 | 655 | { |
| 646 | - //TODO recognise on, off, yes, no, enable, disable | |
| 647 | - //so that we can write --long=yes explicitly | |
| 648 | - value = true; | |
| 656 | + std::smatch result; | |
| 657 | + std::regex_match(text, result, truthy_pattern); | |
| 658 | + | |
| 659 | + if (!result.empty()) | |
| 660 | + { | |
| 661 | + value = true; | |
| 662 | + return; | |
| 663 | + } | |
| 664 | + | |
| 665 | + std::regex_match(text, result, falsy_pattern); | |
| 666 | + if (!result.empty()) | |
| 667 | + { | |
| 668 | + value = false; | |
| 669 | + return; | |
| 670 | + } | |
| 671 | + | |
| 672 | + throw argument_incorrect_type(text); | |
| 649 | 673 | } |
| 650 | 674 | |
| 651 | 675 | inline |
| ... | ... | @@ -674,18 +698,6 @@ namespace cxxopts |
| 674 | 698 | } |
| 675 | 699 | |
| 676 | 700 | template <typename T> |
| 677 | - struct value_has_arg | |
| 678 | - { | |
| 679 | - static constexpr bool value = true; | |
| 680 | - }; | |
| 681 | - | |
| 682 | - template <> | |
| 683 | - struct value_has_arg<bool> | |
| 684 | - { | |
| 685 | - static constexpr bool value = false; | |
| 686 | - }; | |
| 687 | - | |
| 688 | - template <typename T> | |
| 689 | 701 | struct type_is_container |
| 690 | 702 | { |
| 691 | 703 | static constexpr bool value = false; |
| ... | ... | @@ -698,20 +710,42 @@ namespace cxxopts |
| 698 | 710 | }; |
| 699 | 711 | |
| 700 | 712 | template <typename T> |
| 701 | - class standard_value final : public Value | |
| 713 | + class abstract_value : public Value | |
| 702 | 714 | { |
| 715 | + using Self = abstract_value<T>; | |
| 716 | + | |
| 703 | 717 | public: |
| 704 | - standard_value() | |
| 718 | + abstract_value() | |
| 705 | 719 | : m_result(std::make_shared<T>()) |
| 706 | 720 | , m_store(m_result.get()) |
| 707 | 721 | { |
| 708 | 722 | } |
| 709 | 723 | |
| 710 | - standard_value(T* t) | |
| 724 | + abstract_value(T* t) | |
| 711 | 725 | : m_store(t) |
| 712 | 726 | { |
| 713 | 727 | } |
| 714 | 728 | |
| 729 | + virtual ~abstract_value() = default; | |
| 730 | + | |
| 731 | + abstract_value(const abstract_value& rhs) | |
| 732 | + { | |
| 733 | + if (rhs.m_result) | |
| 734 | + { | |
| 735 | + m_result = std::make_shared<T>(); | |
| 736 | + m_store = m_result.get(); | |
| 737 | + } | |
| 738 | + else | |
| 739 | + { | |
| 740 | + m_store = rhs.m_store; | |
| 741 | + } | |
| 742 | + | |
| 743 | + m_default = rhs.m_default; | |
| 744 | + m_implicit = rhs.m_implicit; | |
| 745 | + m_default_value = rhs.m_default_value; | |
| 746 | + m_implicit_value = rhs.m_implicit_value; | |
| 747 | + } | |
| 748 | + | |
| 715 | 749 | void |
| 716 | 750 | parse(const std::string& text) const |
| 717 | 751 | { |
| ... | ... | @@ -731,12 +765,6 @@ namespace cxxopts |
| 731 | 765 | } |
| 732 | 766 | |
| 733 | 767 | bool |
| 734 | - has_arg() const | |
| 735 | - { | |
| 736 | - return value_has_arg<T>::value; | |
| 737 | - } | |
| 738 | - | |
| 739 | - bool | |
| 740 | 768 | has_default() const |
| 741 | 769 | { |
| 742 | 770 | return m_default; |
| ... | ... | @@ -748,15 +776,17 @@ namespace cxxopts |
| 748 | 776 | return m_implicit; |
| 749 | 777 | } |
| 750 | 778 | |
| 751 | - virtual std::shared_ptr<Value> | |
| 752 | - default_value(const std::string& value){ | |
| 779 | + std::shared_ptr<Value> | |
| 780 | + default_value(const std::string& value) | |
| 781 | + { | |
| 753 | 782 | m_default = true; |
| 754 | 783 | m_default_value = value; |
| 755 | 784 | return shared_from_this(); |
| 756 | 785 | } |
| 757 | 786 | |
| 758 | - virtual std::shared_ptr<Value> | |
| 759 | - implicit_value(const std::string& value){ | |
| 787 | + std::shared_ptr<Value> | |
| 788 | + implicit_value(const std::string& value) | |
| 789 | + { | |
| 760 | 790 | m_implicit = true; |
| 761 | 791 | m_implicit_value = value; |
| 762 | 792 | return shared_from_this(); |
| ... | ... | @@ -774,6 +804,12 @@ namespace cxxopts |
| 774 | 804 | return m_implicit_value; |
| 775 | 805 | } |
| 776 | 806 | |
| 807 | + bool | |
| 808 | + is_boolean() const | |
| 809 | + { | |
| 810 | + return std::is_same<T, bool>::value; | |
| 811 | + } | |
| 812 | + | |
| 777 | 813 | const T& |
| 778 | 814 | get() const |
| 779 | 815 | { |
| ... | ... | @@ -790,11 +826,59 @@ namespace cxxopts |
| 790 | 826 | protected: |
| 791 | 827 | std::shared_ptr<T> m_result; |
| 792 | 828 | T* m_store; |
| 829 | + | |
| 793 | 830 | bool m_default = false; |
| 794 | - std::string m_default_value; | |
| 795 | 831 | bool m_implicit = false; |
| 832 | + | |
| 833 | + std::string m_default_value; | |
| 796 | 834 | std::string m_implicit_value; |
| 797 | 835 | }; |
| 836 | + | |
| 837 | + template <typename T> | |
| 838 | + class standard_value : public abstract_value<T> | |
| 839 | + { | |
| 840 | + public: | |
| 841 | + using abstract_value<T>::abstract_value; | |
| 842 | + | |
| 843 | + std::shared_ptr<Value> | |
| 844 | + clone() const | |
| 845 | + { | |
| 846 | + return std::make_shared<standard_value<T>>(*this); | |
| 847 | + } | |
| 848 | + }; | |
| 849 | + | |
| 850 | + template <> | |
| 851 | + class standard_value<bool> : public abstract_value<bool> | |
| 852 | + { | |
| 853 | + public: | |
| 854 | + ~standard_value() = default; | |
| 855 | + | |
| 856 | + standard_value() | |
| 857 | + { | |
| 858 | + set_implicit(); | |
| 859 | + } | |
| 860 | + | |
| 861 | + standard_value(bool* b) | |
| 862 | + : abstract_value(b) | |
| 863 | + { | |
| 864 | + set_implicit(); | |
| 865 | + } | |
| 866 | + | |
| 867 | + std::shared_ptr<Value> | |
| 868 | + clone() const | |
| 869 | + { | |
| 870 | + return std::make_shared<standard_value<bool>>(*this); | |
| 871 | + } | |
| 872 | + | |
| 873 | + private: | |
| 874 | + | |
| 875 | + void | |
| 876 | + set_implicit() | |
| 877 | + { | |
| 878 | + m_implicit = true; | |
| 879 | + m_implicit_value = "true"; | |
| 880 | + } | |
| 881 | + }; | |
| 798 | 882 | } |
| 799 | 883 | |
| 800 | 884 | template <typename T> |
| ... | ... | @@ -818,62 +902,59 @@ namespace cxxopts |
| 818 | 902 | public: |
| 819 | 903 | OptionDetails |
| 820 | 904 | ( |
| 905 | + const std::string& short_name, | |
| 906 | + const std::string& long_name, | |
| 821 | 907 | const String& desc, |
| 822 | 908 | std::shared_ptr<const Value> val |
| 823 | 909 | ) |
| 824 | - : m_desc(desc) | |
| 910 | + : m_short(short_name) | |
| 911 | + , m_long(long_name) | |
| 912 | + , m_desc(desc) | |
| 825 | 913 | , m_value(val) |
| 826 | 914 | , m_count(0) |
| 827 | 915 | { |
| 828 | 916 | } |
| 829 | 917 | |
| 830 | - const String& | |
| 831 | - description() const | |
| 918 | + OptionDetails(const OptionDetails& rhs) | |
| 919 | + : m_desc(rhs.m_desc) | |
| 920 | + , m_count(rhs.m_count) | |
| 832 | 921 | { |
| 833 | - return m_desc; | |
| 922 | + m_value = rhs.m_value->clone(); | |
| 834 | 923 | } |
| 835 | 924 | |
| 836 | - bool | |
| 837 | - has_arg() const | |
| 838 | - { | |
| 839 | - return m_value->has_arg(); | |
| 840 | - } | |
| 925 | + OptionDetails(OptionDetails&& rhs) = default; | |
| 841 | 926 | |
| 842 | - void | |
| 843 | - parse(const std::string& text) | |
| 927 | + const String& | |
| 928 | + description() const | |
| 844 | 929 | { |
| 845 | - m_value->parse(text); | |
| 846 | - ++m_count; | |
| 930 | + return m_desc; | |
| 847 | 931 | } |
| 848 | 932 | |
| 849 | - void | |
| 850 | - parse_default() | |
| 851 | - { | |
| 852 | - m_value->parse(); | |
| 933 | + const Value& value() const { | |
| 934 | + return *m_value; | |
| 853 | 935 | } |
| 854 | 936 | |
| 855 | - int | |
| 856 | - count() const | |
| 937 | + std::shared_ptr<Value> | |
| 938 | + make_storage() const | |
| 857 | 939 | { |
| 858 | - return m_count; | |
| 940 | + return m_value->clone(); | |
| 859 | 941 | } |
| 860 | 942 | |
| 861 | - const Value& value() const { | |
| 862 | - return *m_value; | |
| 943 | + const std::string& | |
| 944 | + short_name() const | |
| 945 | + { | |
| 946 | + return m_short; | |
| 863 | 947 | } |
| 864 | 948 | |
| 865 | - template <typename T> | |
| 866 | - const T& | |
| 867 | - as() const | |
| 949 | + const std::string& | |
| 950 | + long_name() const | |
| 868 | 951 | { |
| 869 | -#ifdef CXXOPTS_NO_RTTI | |
| 870 | - return static_cast<const values::standard_value<T>&>(*m_value).get(); | |
| 871 | -#else | |
| 872 | - return dynamic_cast<const values::standard_value<T>&>(*m_value).get(); | |
| 873 | -#endif | |
| 952 | + return m_long; | |
| 874 | 953 | } |
| 875 | 954 | |
| 876 | 955 | private: |
| 956 | + std::string m_short; | |
| 957 | + std::string m_long; | |
| 877 | 958 | String m_desc; |
| 878 | 959 | std::shared_ptr<const Value> m_value; |
| 879 | 960 | int m_count; |
| ... | ... | @@ -884,13 +965,13 @@ namespace cxxopts |
| 884 | 965 | std::string s; |
| 885 | 966 | std::string l; |
| 886 | 967 | String desc; |
| 887 | - bool has_arg; | |
| 888 | 968 | bool has_default; |
| 889 | 969 | std::string default_value; |
| 890 | 970 | bool has_implicit; |
| 891 | 971 | std::string implicit_value; |
| 892 | 972 | std::string arg_help; |
| 893 | 973 | bool is_container; |
| 974 | + bool is_boolean; | |
| 894 | 975 | }; |
| 895 | 976 | |
| 896 | 977 | struct HelpGroupDetails |
| ... | ... | @@ -900,45 +981,104 @@ namespace cxxopts |
| 900 | 981 | std::vector<HelpOptionDetails> options; |
| 901 | 982 | }; |
| 902 | 983 | |
| 903 | - class Options | |
| 984 | + class OptionValue | |
| 904 | 985 | { |
| 905 | 986 | public: |
| 987 | + void | |
| 988 | + parse | |
| 989 | + ( | |
| 990 | + std::shared_ptr<const OptionDetails> details, | |
| 991 | + const std::string& text | |
| 992 | + ) | |
| 993 | + { | |
| 994 | + ensure_value(details); | |
| 995 | + ++m_count; | |
| 996 | + m_value->parse(text); | |
| 997 | + } | |
| 906 | 998 | |
| 907 | - Options(std::string program, std::string help_string = "") | |
| 908 | - : m_program(std::move(program)) | |
| 909 | - , m_help_string(toLocalString(std::move(help_string))) | |
| 910 | - , m_positional_help("positional parameters") | |
| 911 | - , m_next_positional(m_positional.end()) | |
| 999 | + void | |
| 1000 | + parse_default(std::shared_ptr<const OptionDetails> details) | |
| 912 | 1001 | { |
| 1002 | + ensure_value(details); | |
| 1003 | + m_value->parse(); | |
| 1004 | + m_count++; | |
| 913 | 1005 | } |
| 914 | 1006 | |
| 915 | - inline | |
| 916 | - Options& | |
| 917 | - positional_help(std::string help_text) | |
| 1007 | + size_t | |
| 1008 | + count() const | |
| 918 | 1009 | { |
| 919 | - m_positional_help = std::move(help_text); | |
| 920 | - return *this; | |
| 1010 | + return m_count; | |
| 921 | 1011 | } |
| 922 | 1012 | |
| 923 | - inline | |
| 1013 | + template <typename T> | |
| 1014 | + const T& | |
| 1015 | + as() const | |
| 1016 | + { | |
| 1017 | +#ifdef CXXOPTS_NO_RTTI | |
| 1018 | + return static_cast<const values::standard_value<T>&>(*m_value).get(); | |
| 1019 | +#else | |
| 1020 | + return dynamic_cast<const values::standard_value<T>&>(*m_value).get(); | |
| 1021 | +#endif | |
| 1022 | + } | |
| 1023 | + | |
| 1024 | + private: | |
| 924 | 1025 | void |
| 925 | - parse(int& argc, char**& argv); | |
| 1026 | + ensure_value(std::shared_ptr<const OptionDetails> details) | |
| 1027 | + { | |
| 1028 | + if (m_value == nullptr) | |
| 1029 | + { | |
| 1030 | + m_value = details->make_storage(); | |
| 1031 | + } | |
| 1032 | + } | |
| 926 | 1033 | |
| 927 | - inline | |
| 928 | - OptionAdder | |
| 929 | - add_options(std::string group = ""); | |
| 1034 | + std::shared_ptr<Value> m_value; | |
| 1035 | + size_t m_count = 0; | |
| 1036 | + }; | |
| 930 | 1037 | |
| 931 | - inline | |
| 932 | - void | |
| 933 | - add_option | |
| 934 | - ( | |
| 935 | - const std::string& group, | |
| 936 | - const std::string& s, | |
| 937 | - const std::string& l, | |
| 938 | - std::string desc, | |
| 939 | - std::shared_ptr<const Value> value, | |
| 940 | - std::string arg_help | |
| 941 | - ); | |
| 1038 | + class KeyValue | |
| 1039 | + { | |
| 1040 | + public: | |
| 1041 | + KeyValue(std::string key, std::string value) | |
| 1042 | + : m_key(std::move(key)) | |
| 1043 | + , m_value(std::move(value)) | |
| 1044 | + { | |
| 1045 | + } | |
| 1046 | + | |
| 1047 | + const | |
| 1048 | + std::string& | |
| 1049 | + key() const | |
| 1050 | + { | |
| 1051 | + return m_key; | |
| 1052 | + } | |
| 1053 | + | |
| 1054 | + const std::string | |
| 1055 | + value() const | |
| 1056 | + { | |
| 1057 | + return m_value; | |
| 1058 | + } | |
| 1059 | + | |
| 1060 | + template <typename T> | |
| 1061 | + T | |
| 1062 | + as() const | |
| 1063 | + { | |
| 1064 | + T result; | |
| 1065 | + values::parse_value(m_value, result); | |
| 1066 | + return result; | |
| 1067 | + } | |
| 1068 | + | |
| 1069 | + private: | |
| 1070 | + std::string m_key; | |
| 1071 | + std::string m_value; | |
| 1072 | + }; | |
| 1073 | + | |
| 1074 | + class ParseResult | |
| 1075 | + { | |
| 1076 | + public: | |
| 1077 | + | |
| 1078 | + ParseResult( | |
| 1079 | + const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>&, | |
| 1080 | + std::vector<std::string>, | |
| 1081 | + int&, char**&); | |
| 942 | 1082 | |
| 943 | 1083 | int |
| 944 | 1084 | count(const std::string& o) const |
| ... | ... | @@ -949,10 +1089,12 @@ namespace cxxopts |
| 949 | 1089 | return 0; |
| 950 | 1090 | } |
| 951 | 1091 | |
| 952 | - return iter->second->count(); | |
| 1092 | + auto riter = m_results.find(iter->second); | |
| 1093 | + | |
| 1094 | + return riter->second.count(); | |
| 953 | 1095 | } |
| 954 | 1096 | |
| 955 | - const OptionDetails& | |
| 1097 | + const OptionValue& | |
| 956 | 1098 | operator[](const std::string& option) const |
| 957 | 1099 | { |
| 958 | 1100 | auto iter = m_options.find(option); |
| ... | ... | @@ -962,49 +1104,31 @@ namespace cxxopts |
| 962 | 1104 | throw option_not_present_exception(option); |
| 963 | 1105 | } |
| 964 | 1106 | |
| 965 | - return *iter->second; | |
| 966 | - } | |
| 967 | - | |
| 968 | - //parse positional arguments into the given option | |
| 969 | - inline | |
| 970 | - void | |
| 971 | - parse_positional(std::string option); | |
| 1107 | + auto riter = m_results.find(iter->second); | |
| 972 | 1108 | |
| 973 | - inline | |
| 974 | - void | |
| 975 | - parse_positional(std::vector<std::string> options); | |
| 1109 | + return riter->second; | |
| 1110 | + } | |
| 976 | 1111 | |
| 977 | - inline | |
| 978 | - std::string | |
| 979 | - help(const std::vector<std::string>& groups = {""}) const; | |
| 1112 | + const std::vector<KeyValue>& | |
| 1113 | + arguments() const | |
| 1114 | + { | |
| 1115 | + return m_sequential; | |
| 1116 | + } | |
| 980 | 1117 | |
| 981 | - inline | |
| 982 | - const std::vector<std::string> | |
| 983 | - groups() const; | |
| 1118 | + private: | |
| 984 | 1119 | |
| 985 | - inline | |
| 986 | - const HelpGroupDetails& | |
| 987 | - group_help(const std::string& group) const; | |
| 1120 | + OptionValue& | |
| 1121 | + get_option(std::shared_ptr<OptionDetails>); | |
| 988 | 1122 | |
| 989 | - private: | |
| 1123 | + void | |
| 1124 | + parse(int& argc, char**& argv); | |
| 990 | 1125 | |
| 991 | - inline | |
| 992 | 1126 | void |
| 993 | - add_one_option | |
| 994 | - ( | |
| 995 | - const std::string& option, | |
| 996 | - std::shared_ptr<OptionDetails> details | |
| 997 | - ); | |
| 1127 | + add_to_option(const std::string& option, const std::string& arg); | |
| 998 | 1128 | |
| 999 | - inline | |
| 1000 | 1129 | bool |
| 1001 | 1130 | consume_positional(std::string a); |
| 1002 | 1131 | |
| 1003 | - inline | |
| 1004 | - void | |
| 1005 | - add_to_option(const std::string& option, const std::string& arg); | |
| 1006 | - | |
| 1007 | - inline | |
| 1008 | 1132 | void |
| 1009 | 1133 | parse_option |
| 1010 | 1134 | ( |
| ... | ... | @@ -1013,7 +1137,9 @@ namespace cxxopts |
| 1013 | 1137 | const std::string& arg = "" |
| 1014 | 1138 | ); |
| 1015 | 1139 | |
| 1016 | - inline | |
| 1140 | + void | |
| 1141 | + parse_default(std::shared_ptr<OptionDetails> details); | |
| 1142 | + | |
| 1017 | 1143 | void |
| 1018 | 1144 | checked_parse_arg |
| 1019 | 1145 | ( |
| ... | ... | @@ -1024,11 +1150,88 @@ namespace cxxopts |
| 1024 | 1150 | const std::string& name |
| 1025 | 1151 | ); |
| 1026 | 1152 | |
| 1027 | - inline | |
| 1153 | + const std::unordered_map<std::string, std::shared_ptr<OptionDetails>> | |
| 1154 | + &m_options; | |
| 1155 | + std::vector<std::string> m_positional; | |
| 1156 | + std::vector<std::string>::iterator m_next_positional; | |
| 1157 | + std::unordered_set<std::string> m_positional_set; | |
| 1158 | + std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results; | |
| 1159 | + | |
| 1160 | + std::vector<KeyValue> m_sequential; | |
| 1161 | + }; | |
| 1162 | + | |
| 1163 | + class Options | |
| 1164 | + { | |
| 1165 | + public: | |
| 1166 | + | |
| 1167 | + Options(std::string program, std::string help_string = "") | |
| 1168 | + : m_program(std::move(program)) | |
| 1169 | + , m_help_string(toLocalString(std::move(help_string))) | |
| 1170 | + , m_positional_help("positional parameters") | |
| 1171 | + , m_show_positional(false) | |
| 1172 | + , m_next_positional(m_positional.end()) | |
| 1173 | + { | |
| 1174 | + } | |
| 1175 | + | |
| 1176 | + Options& | |
| 1177 | + positional_help(std::string help_text) | |
| 1178 | + { | |
| 1179 | + m_positional_help = std::move(help_text); | |
| 1180 | + return *this; | |
| 1181 | + } | |
| 1182 | + | |
| 1183 | + Options& | |
| 1184 | + show_positional_help() | |
| 1185 | + { | |
| 1186 | + m_show_positional = true; | |
| 1187 | + return *this; | |
| 1188 | + } | |
| 1189 | + | |
| 1190 | + ParseResult | |
| 1191 | + parse(int& argc, char**& argv); | |
| 1192 | + | |
| 1193 | + OptionAdder | |
| 1194 | + add_options(std::string group = ""); | |
| 1195 | + | |
| 1196 | + void | |
| 1197 | + add_option | |
| 1198 | + ( | |
| 1199 | + const std::string& group, | |
| 1200 | + const std::string& s, | |
| 1201 | + const std::string& l, | |
| 1202 | + std::string desc, | |
| 1203 | + std::shared_ptr<const Value> value, | |
| 1204 | + std::string arg_help | |
| 1205 | + ); | |
| 1206 | + | |
| 1207 | + //parse positional arguments into the given option | |
| 1208 | + void | |
| 1209 | + parse_positional(std::string option); | |
| 1210 | + | |
| 1211 | + void | |
| 1212 | + parse_positional(std::vector<std::string> options); | |
| 1213 | + | |
| 1214 | + std::string | |
| 1215 | + help(const std::vector<std::string>& groups = {""}) const; | |
| 1216 | + | |
| 1217 | + const std::vector<std::string> | |
| 1218 | + groups() const; | |
| 1219 | + | |
| 1220 | + const HelpGroupDetails& | |
| 1221 | + group_help(const std::string& group) const; | |
| 1222 | + | |
| 1223 | + private: | |
| 1224 | + | |
| 1225 | + void | |
| 1226 | + add_one_option | |
| 1227 | + ( | |
| 1228 | + const std::string& option, | |
| 1229 | + std::shared_ptr<OptionDetails> details | |
| 1230 | + ); | |
| 1231 | + | |
| 1028 | 1232 | String |
| 1029 | 1233 | help_one_group(const std::string& group) const; |
| 1030 | 1234 | |
| 1031 | - inline | |
| 1032 | 1235 | void |
| 1033 | 1236 | generate_group_help |
| 1034 | 1237 | ( |
| ... | ... | @@ -1036,15 +1239,15 @@ namespace cxxopts |
| 1036 | 1239 | const std::vector<std::string>& groups |
| 1037 | 1240 | ) const; |
| 1038 | 1241 | |
| 1039 | - inline | |
| 1040 | 1242 | void |
| 1041 | 1243 | generate_all_groups_help(String& result) const; |
| 1042 | 1244 | |
| 1043 | 1245 | std::string m_program; |
| 1044 | 1246 | String m_help_string; |
| 1045 | 1247 | std::string m_positional_help; |
| 1248 | + bool m_show_positional; | |
| 1046 | 1249 | |
| 1047 | - std::map<std::string, std::shared_ptr<OptionDetails>> m_options; | |
| 1250 | + std::unordered_map<std::string, std::shared_ptr<OptionDetails>> m_options; | |
| 1048 | 1251 | std::vector<std::string> m_positional; |
| 1049 | 1252 | std::vector<std::string>::iterator m_next_positional; |
| 1050 | 1253 | std::unordered_set<std::string> m_positional_set; |
| ... | ... | @@ -1062,7 +1265,6 @@ namespace cxxopts |
| 1062 | 1265 | { |
| 1063 | 1266 | } |
| 1064 | 1267 | |
| 1065 | - inline | |
| 1066 | 1268 | OptionAdder& |
| 1067 | 1269 | operator() |
| 1068 | 1270 | ( |
| ... | ... | @@ -1078,24 +1280,6 @@ namespace cxxopts |
| 1078 | 1280 | std::string m_group; |
| 1079 | 1281 | }; |
| 1080 | 1282 | |
| 1081 | - // A helper function for setting required arguments | |
| 1082 | - inline | |
| 1083 | - void | |
| 1084 | - check_required | |
| 1085 | - ( | |
| 1086 | - const Options& options, | |
| 1087 | - const std::vector<std::string>& required | |
| 1088 | - ) | |
| 1089 | - { | |
| 1090 | - for (auto& r : required) | |
| 1091 | - { | |
| 1092 | - if (options.count(r) == 0) | |
| 1093 | - { | |
| 1094 | - throw option_required_exception(r); | |
| 1095 | - } | |
| 1096 | - } | |
| 1097 | - } | |
| 1098 | - | |
| 1099 | 1283 | namespace |
| 1100 | 1284 | { |
| 1101 | 1285 | constexpr int OPTION_LONGEST = 30; |
| ... | ... | @@ -1132,10 +1316,10 @@ namespace cxxopts |
| 1132 | 1316 | result += " --" + toLocalString(l); |
| 1133 | 1317 | } |
| 1134 | 1318 | |
| 1135 | - if (o.has_arg) | |
| 1136 | - { | |
| 1137 | - auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; | |
| 1319 | + auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; | |
| 1138 | 1320 | |
| 1321 | + if (!o.is_boolean) | |
| 1322 | + { | |
| 1139 | 1323 | if (o.has_implicit) |
| 1140 | 1324 | { |
| 1141 | 1325 | result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; |
| ... | ... | @@ -1213,12 +1397,28 @@ namespace cxxopts |
| 1213 | 1397 | } |
| 1214 | 1398 | } |
| 1215 | 1399 | |
| 1400 | +inline | |
| 1401 | +ParseResult::ParseResult | |
| 1402 | +( | |
| 1403 | + const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>& options, | |
| 1404 | + std::vector<std::string> positional, | |
| 1405 | + int& argc, char**& argv | |
| 1406 | +) | |
| 1407 | +: m_options(options) | |
| 1408 | +, m_positional(std::move(positional)) | |
| 1409 | +, m_next_positional(m_positional.begin()) | |
| 1410 | +{ | |
| 1411 | + parse(argc, argv); | |
| 1412 | +} | |
| 1413 | + | |
| 1414 | +inline | |
| 1216 | 1415 | OptionAdder |
| 1217 | 1416 | Options::add_options(std::string group) |
| 1218 | 1417 | { |
| 1219 | 1418 | return OptionAdder(*this, std::move(group)); |
| 1220 | 1419 | } |
| 1221 | 1420 | |
| 1421 | +inline | |
| 1222 | 1422 | OptionAdder& |
| 1223 | 1423 | OptionAdder::operator() |
| 1224 | 1424 | ( |
| ... | ... | @@ -1276,19 +1476,31 @@ OptionAdder::operator() |
| 1276 | 1476 | return *this; |
| 1277 | 1477 | } |
| 1278 | 1478 | |
| 1479 | +inline | |
| 1480 | +void | |
| 1481 | +ParseResult::parse_default(std::shared_ptr<OptionDetails> details) | |
| 1482 | +{ | |
| 1483 | + m_results[details].parse_default(details); | |
| 1484 | +} | |
| 1485 | + | |
| 1486 | +inline | |
| 1279 | 1487 | void |
| 1280 | -Options::parse_option | |
| 1488 | +ParseResult::parse_option | |
| 1281 | 1489 | ( |
| 1282 | 1490 | std::shared_ptr<OptionDetails> value, |
| 1283 | 1491 | const std::string& /*name*/, |
| 1284 | 1492 | const std::string& arg |
| 1285 | 1493 | ) |
| 1286 | 1494 | { |
| 1287 | - value->parse(arg); | |
| 1495 | + auto& result = m_results[value]; | |
| 1496 | + result.parse(value, arg); | |
| 1497 | + | |
| 1498 | + m_sequential.emplace_back(value->long_name(), arg); | |
| 1288 | 1499 | } |
| 1289 | 1500 | |
| 1501 | +inline | |
| 1290 | 1502 | void |
| 1291 | -Options::checked_parse_arg | |
| 1503 | +ParseResult::checked_parse_arg | |
| 1292 | 1504 | ( |
| 1293 | 1505 | int argc, |
| 1294 | 1506 | char* argv[], |
| ... | ... | @@ -1322,8 +1534,9 @@ Options::checked_parse_arg |
| 1322 | 1534 | } |
| 1323 | 1535 | } |
| 1324 | 1536 | |
| 1537 | +inline | |
| 1325 | 1538 | void |
| 1326 | -Options::add_to_option(const std::string& option, const std::string& arg) | |
| 1539 | +ParseResult::add_to_option(const std::string& option, const std::string& arg) | |
| 1327 | 1540 | { |
| 1328 | 1541 | auto iter = m_options.find(option); |
| 1329 | 1542 | |
| ... | ... | @@ -1335,17 +1548,19 @@ Options::add_to_option(const std::string& option, const std::string& arg) |
| 1335 | 1548 | parse_option(iter->second, option, arg); |
| 1336 | 1549 | } |
| 1337 | 1550 | |
| 1551 | +inline | |
| 1338 | 1552 | bool |
| 1339 | -Options::consume_positional(std::string a) | |
| 1553 | +ParseResult::consume_positional(std::string a) | |
| 1340 | 1554 | { |
| 1341 | 1555 | while (m_next_positional != m_positional.end()) |
| 1342 | 1556 | { |
| 1343 | 1557 | auto iter = m_options.find(*m_next_positional); |
| 1344 | 1558 | if (iter != m_options.end()) |
| 1345 | 1559 | { |
| 1560 | + auto& result = m_results[iter->second]; | |
| 1346 | 1561 | if (!iter->second->value().is_container()) |
| 1347 | 1562 | { |
| 1348 | - if (iter->second->count() == 0) | |
| 1563 | + if (result.count() == 0) | |
| 1349 | 1564 | { |
| 1350 | 1565 | add_to_option(*m_next_positional, a); |
| 1351 | 1566 | ++m_next_positional; |
| ... | ... | @@ -1369,12 +1584,14 @@ Options::consume_positional(std::string a) |
| 1369 | 1584 | return false; |
| 1370 | 1585 | } |
| 1371 | 1586 | |
| 1587 | +inline | |
| 1372 | 1588 | void |
| 1373 | 1589 | Options::parse_positional(std::string option) |
| 1374 | 1590 | { |
| 1375 | 1591 | parse_positional(std::vector<std::string>{option}); |
| 1376 | 1592 | } |
| 1377 | 1593 | |
| 1594 | +inline | |
| 1378 | 1595 | void |
| 1379 | 1596 | Options::parse_positional(std::vector<std::string> options) |
| 1380 | 1597 | { |
| ... | ... | @@ -1384,9 +1601,18 @@ Options::parse_positional(std::vector<std::string> options) |
| 1384 | 1601 | m_positional_set.insert(m_positional.begin(), m_positional.end()); |
| 1385 | 1602 | } |
| 1386 | 1603 | |
| 1387 | -void | |
| 1604 | +inline | |
| 1605 | +ParseResult | |
| 1388 | 1606 | Options::parse(int& argc, char**& argv) |
| 1389 | 1607 | { |
| 1608 | + ParseResult result(m_options, m_positional, argc, argv); | |
| 1609 | + return result; | |
| 1610 | +} | |
| 1611 | + | |
| 1612 | +inline | |
| 1613 | +void | |
| 1614 | +ParseResult::parse(int& argc, char**& argv) | |
| 1615 | +{ | |
| 1390 | 1616 | int current = 1; |
| 1391 | 1617 | |
| 1392 | 1618 | int nextKeep = 1; |
| ... | ... | @@ -1440,27 +1666,19 @@ Options::parse(int& argc, char**& argv) |
| 1440 | 1666 | |
| 1441 | 1667 | auto value = iter->second; |
| 1442 | 1668 | |
| 1443 | - //if no argument then just add it | |
| 1444 | - if (!value->has_arg()) | |
| 1669 | + if (i + 1 == s.size()) | |
| 1670 | + { | |
| 1671 | + //it must be the last argument | |
| 1672 | + checked_parse_arg(argc, argv, current, value, name); | |
| 1673 | + } | |
| 1674 | + else if (value->value().has_implicit()) | |
| 1445 | 1675 | { |
| 1446 | - parse_option(value, name); | |
| 1676 | + parse_option(value, name, value->value().get_implicit_value()); | |
| 1447 | 1677 | } |
| 1448 | 1678 | else |
| 1449 | 1679 | { |
| 1450 | - //it must be the last argument | |
| 1451 | - if (i + 1 == s.size()) | |
| 1452 | - { | |
| 1453 | - checked_parse_arg(argc, argv, current, value, name); | |
| 1454 | - } | |
| 1455 | - else if (value->value().has_implicit()) | |
| 1456 | - { | |
| 1457 | - parse_option(value, name, value->value().get_implicit_value()); | |
| 1458 | - } | |
| 1459 | - else | |
| 1460 | - { | |
| 1461 | - //error | |
| 1462 | - throw option_requires_argument_exception(name); | |
| 1463 | - } | |
| 1680 | + //error | |
| 1681 | + throw option_requires_argument_exception(name); | |
| 1464 | 1682 | } |
| 1465 | 1683 | } |
| 1466 | 1684 | } |
| ... | ... | @@ -1482,26 +1700,12 @@ Options::parse(int& argc, char**& argv) |
| 1482 | 1700 | { |
| 1483 | 1701 | //parse the option given |
| 1484 | 1702 | |
| 1485 | - //but if it doesn't take an argument, this is an error | |
| 1486 | - if (!opt->has_arg()) | |
| 1487 | - { | |
| 1488 | - throw option_not_has_argument_exception(name, result[3]); | |
| 1489 | - } | |
| 1490 | - | |
| 1491 | 1703 | parse_option(opt, name, result[3]); |
| 1492 | 1704 | } |
| 1493 | 1705 | else |
| 1494 | 1706 | { |
| 1495 | - if (opt->has_arg()) | |
| 1496 | - { | |
| 1497 | - //parse the next argument | |
| 1498 | - checked_parse_arg(argc, argv, current, opt, name); | |
| 1499 | - } | |
| 1500 | - else | |
| 1501 | - { | |
| 1502 | - //parse with empty argument | |
| 1503 | - parse_option(opt, name); | |
| 1504 | - } | |
| 1707 | + //parse the next argument | |
| 1708 | + checked_parse_arg(argc, argv, current, opt, name); | |
| 1505 | 1709 | } |
| 1506 | 1710 | } |
| 1507 | 1711 | |
| ... | ... | @@ -1515,8 +1719,10 @@ Options::parse(int& argc, char**& argv) |
| 1515 | 1719 | auto& detail = opt.second; |
| 1516 | 1720 | auto& value = detail->value(); |
| 1517 | 1721 | |
| 1518 | - if(!detail->count() && value.has_default()){ | |
| 1519 | - detail->parse_default(); | |
| 1722 | + auto& store = m_results[detail]; | |
| 1723 | + | |
| 1724 | + if(!store.count() && value.has_default()){ | |
| 1725 | + parse_default(detail); | |
| 1520 | 1726 | } |
| 1521 | 1727 | } |
| 1522 | 1728 | |
| ... | ... | @@ -1542,6 +1748,7 @@ Options::parse(int& argc, char**& argv) |
| 1542 | 1748 | |
| 1543 | 1749 | } |
| 1544 | 1750 | |
| 1751 | +inline | |
| 1545 | 1752 | void |
| 1546 | 1753 | Options::add_option |
| 1547 | 1754 | ( |
| ... | ... | @@ -1554,7 +1761,7 @@ Options::add_option |
| 1554 | 1761 | ) |
| 1555 | 1762 | { |
| 1556 | 1763 | auto stringDesc = toLocalString(std::move(desc)); |
| 1557 | - auto option = std::make_shared<OptionDetails>(stringDesc, value); | |
| 1764 | + auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value); | |
| 1558 | 1765 | |
| 1559 | 1766 | if (s.size() > 0) |
| 1560 | 1767 | { |
| ... | ... | @@ -1570,13 +1777,14 @@ Options::add_option |
| 1570 | 1777 | auto& options = m_help[group]; |
| 1571 | 1778 | |
| 1572 | 1779 | options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, |
| 1573 | - value->has_arg(), | |
| 1574 | 1780 | value->has_default(), value->get_default_value(), |
| 1575 | 1781 | value->has_implicit(), value->get_implicit_value(), |
| 1576 | 1782 | std::move(arg_help), |
| 1577 | - value->is_container()}); | |
| 1783 | + value->is_container(), | |
| 1784 | + value->is_boolean()}); | |
| 1578 | 1785 | } |
| 1579 | 1786 | |
| 1787 | +inline | |
| 1580 | 1788 | void |
| 1581 | 1789 | Options::add_one_option |
| 1582 | 1790 | ( |
| ... | ... | @@ -1592,6 +1800,7 @@ Options::add_one_option |
| 1592 | 1800 | } |
| 1593 | 1801 | } |
| 1594 | 1802 | |
| 1803 | +inline | |
| 1595 | 1804 | String |
| 1596 | 1805 | Options::help_one_group(const std::string& g) const |
| 1597 | 1806 | { |
| ... | ... | @@ -1616,7 +1825,9 @@ Options::help_one_group(const std::string& g) const |
| 1616 | 1825 | |
| 1617 | 1826 | for (const auto& o : group->second.options) |
| 1618 | 1827 | { |
| 1619 | - if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) | |
| 1828 | + if (o.is_container && | |
| 1829 | + m_positional_set.find(o.l) != m_positional_set.end() && | |
| 1830 | + !m_show_positional) | |
| 1620 | 1831 | { |
| 1621 | 1832 | continue; |
| 1622 | 1833 | } |
| ... | ... | @@ -1634,7 +1845,9 @@ Options::help_one_group(const std::string& g) const |
| 1634 | 1845 | auto fiter = format.begin(); |
| 1635 | 1846 | for (const auto& o : group->second.options) |
| 1636 | 1847 | { |
| 1637 | - if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) | |
| 1848 | + if (o.is_container && | |
| 1849 | + m_positional_set.find(o.l) != m_positional_set.end() && | |
| 1850 | + !m_show_positional) | |
| 1638 | 1851 | { |
| 1639 | 1852 | continue; |
| 1640 | 1853 | } |
| ... | ... | @@ -1662,6 +1875,7 @@ Options::help_one_group(const std::string& g) const |
| 1662 | 1875 | return result; |
| 1663 | 1876 | } |
| 1664 | 1877 | |
| 1878 | +inline | |
| 1665 | 1879 | void |
| 1666 | 1880 | Options::generate_group_help |
| 1667 | 1881 | ( |
| ... | ... | @@ -1684,6 +1898,7 @@ Options::generate_group_help |
| 1684 | 1898 | } |
| 1685 | 1899 | } |
| 1686 | 1900 | |
| 1901 | +inline | |
| 1687 | 1902 | void |
| 1688 | 1903 | Options::generate_all_groups_help(String& result) const |
| 1689 | 1904 | { |
| ... | ... | @@ -1698,6 +1913,7 @@ Options::generate_all_groups_help(String& result) const |
| 1698 | 1913 | generate_group_help(result, all_groups); |
| 1699 | 1914 | } |
| 1700 | 1915 | |
| 1916 | +inline | |
| 1701 | 1917 | std::string |
| 1702 | 1918 | Options::help(const std::vector<std::string>& help_groups) const |
| 1703 | 1919 | { |
| ... | ... | @@ -1722,6 +1938,7 @@ Options::help(const std::vector<std::string>& help_groups) const |
| 1722 | 1938 | return toUTF8String(result); |
| 1723 | 1939 | } |
| 1724 | 1940 | |
| 1941 | +inline | |
| 1725 | 1942 | const std::vector<std::string> |
| 1726 | 1943 | Options::groups() const |
| 1727 | 1944 | { |
| ... | ... | @@ -1740,6 +1957,7 @@ Options::groups() const |
| 1740 | 1957 | return g; |
| 1741 | 1958 | } |
| 1742 | 1959 | |
| 1960 | +inline | |
| 1743 | 1961 | const HelpGroupDetails& |
| 1744 | 1962 | Options::group_help(const std::string& group) const |
| 1745 | 1963 | { |
| ... | ... | @@ -1748,8 +1966,4 @@ Options::group_help(const std::string& group) const |
| 1748 | 1966 | |
| 1749 | 1967 | } |
| 1750 | 1968 | |
| 1751 | -#if defined(__GNUC__) | |
| 1752 | -#pragma GCC diagnostic pop | |
| 1753 | -#endif | |
| 1754 | - | |
| 1755 | -#endif //CXX_OPTS_HPP | |
| 1969 | +#endif //CXXOPTS_HPP_INCLUDED | ... | ... |
src/example.cpp
| ... | ... | @@ -31,7 +31,9 @@ int main(int argc, char* argv[]) |
| 31 | 31 | try |
| 32 | 32 | { |
| 33 | 33 | cxxopts::Options options(argv[0], " - example command line options"); |
| 34 | - options.positional_help("[optional args]"); | |
| 34 | + options | |
| 35 | + .positional_help("[optional args]") | |
| 36 | + .show_positional_help(); | |
| 35 | 37 | |
| 36 | 38 | bool apple = false; |
| 37 | 39 | |
| ... | ... | @@ -63,9 +65,9 @@ int main(int argc, char* argv[]) |
| 63 | 65 | |
| 64 | 66 | options.parse_positional({"input", "output", "positional"}); |
| 65 | 67 | |
| 66 | - options.parse(argc, argv); | |
| 68 | + auto result = options.parse(argc, argv); | |
| 67 | 69 | |
| 68 | - if (options.count("help")) | |
| 70 | + if (result.count("help")) | |
| 69 | 71 | { |
| 70 | 72 | std::cout << options.help({"", "Group"}) << std::endl; |
| 71 | 73 | exit(0); |
| ... | ... | @@ -73,18 +75,18 @@ int main(int argc, char* argv[]) |
| 73 | 75 | |
| 74 | 76 | if (apple) |
| 75 | 77 | { |
| 76 | - std::cout << "Saw option ‘a’ " << options.count("a") << " times " << | |
| 78 | + std::cout << "Saw option ‘a’ " << result.count("a") << " times " << | |
| 77 | 79 | std::endl; |
| 78 | 80 | } |
| 79 | 81 | |
| 80 | - if (options.count("b")) | |
| 82 | + if (result.count("b")) | |
| 81 | 83 | { |
| 82 | 84 | std::cout << "Saw option ‘b’" << std::endl; |
| 83 | 85 | } |
| 84 | 86 | |
| 85 | - if (options.count("f")) | |
| 87 | + if (result.count("f")) | |
| 86 | 88 | { |
| 87 | - auto& ff = options["f"].as<std::vector<std::string>>(); | |
| 89 | + auto& ff = result["f"].as<std::vector<std::string>>(); | |
| 88 | 90 | std::cout << "Files" << std::endl; |
| 89 | 91 | for (const auto& f : ff) |
| 90 | 92 | { |
| ... | ... | @@ -92,36 +94,36 @@ int main(int argc, char* argv[]) |
| 92 | 94 | } |
| 93 | 95 | } |
| 94 | 96 | |
| 95 | - if (options.count("input")) | |
| 97 | + if (result.count("input")) | |
| 96 | 98 | { |
| 97 | - std::cout << "Input = " << options["input"].as<std::string>() | |
| 99 | + std::cout << "Input = " << result["input"].as<std::string>() | |
| 98 | 100 | << std::endl; |
| 99 | 101 | } |
| 100 | 102 | |
| 101 | - if (options.count("output")) | |
| 103 | + if (result.count("output")) | |
| 102 | 104 | { |
| 103 | - std::cout << "Output = " << options["output"].as<std::string>() | |
| 105 | + std::cout << "Output = " << result["output"].as<std::string>() | |
| 104 | 106 | << std::endl; |
| 105 | 107 | } |
| 106 | 108 | |
| 107 | - if (options.count("positional")) | |
| 109 | + if (result.count("positional")) | |
| 108 | 110 | { |
| 109 | 111 | std::cout << "Positional = {"; |
| 110 | - auto& v = options["positional"].as<std::vector<std::string>>(); | |
| 112 | + auto& v = result["positional"].as<std::vector<std::string>>(); | |
| 111 | 113 | for (const auto& s : v) { |
| 112 | 114 | std::cout << s << ", "; |
| 113 | 115 | } |
| 114 | 116 | std::cout << "}" << std::endl; |
| 115 | 117 | } |
| 116 | 118 | |
| 117 | - if (options.count("int")) | |
| 119 | + if (result.count("int")) | |
| 118 | 120 | { |
| 119 | - std::cout << "int = " << options["int"].as<int>() << std::endl; | |
| 121 | + std::cout << "int = " << result["int"].as<int>() << std::endl; | |
| 120 | 122 | } |
| 121 | 123 | |
| 122 | - if (options.count("float")) | |
| 124 | + if (result.count("float")) | |
| 123 | 125 | { |
| 124 | - std::cout << "float = " << options["float"].as<float>() << std::endl; | |
| 126 | + std::cout << "float = " << result["float"].as<float>() << std::endl; | |
| 125 | 127 | } |
| 126 | 128 | |
| 127 | 129 | std::cout << "Arguments remain = " << argc << std::endl; | ... | ... |
test/CMakeLists.txt
test/link_a.cpp
0 → 100644
test/link_b.cpp
0 → 100644
| 1 | +#include <cxxopts.hpp> | ... | ... |
test/options.cpp
| ... | ... | @@ -71,17 +71,27 @@ TEST_CASE("Basic options", "[options]") |
| 71 | 71 | char** actual_argv = argv.argv(); |
| 72 | 72 | auto argc = argv.argc(); |
| 73 | 73 | |
| 74 | - options.parse(argc, actual_argv); | |
| 75 | - | |
| 76 | - CHECK(options.count("long") == 1); | |
| 77 | - CHECK(options.count("s") == 1); | |
| 78 | - CHECK(options.count("value") == 1); | |
| 79 | - CHECK(options.count("a") == 1); | |
| 80 | - CHECK(options["value"].as<std::string>() == "value"); | |
| 81 | - CHECK(options["a"].as<std::string>() == "b"); | |
| 82 | - CHECK(options.count("6") == 1); | |
| 83 | - CHECK(options.count("p") == 2); | |
| 84 | - CHECK(options.count("space") == 2); | |
| 74 | + auto result = options.parse(argc, actual_argv); | |
| 75 | + | |
| 76 | + CHECK(result.count("long") == 1); | |
| 77 | + CHECK(result.count("s") == 1); | |
| 78 | + CHECK(result.count("value") == 1); | |
| 79 | + CHECK(result.count("a") == 1); | |
| 80 | + CHECK(result["value"].as<std::string>() == "value"); | |
| 81 | + CHECK(result["a"].as<std::string>() == "b"); | |
| 82 | + CHECK(result.count("6") == 1); | |
| 83 | + CHECK(result.count("p") == 2); | |
| 84 | + CHECK(result.count("space") == 2); | |
| 85 | + | |
| 86 | + auto& arguments = result.arguments(); | |
| 87 | + REQUIRE(arguments.size() == 7); | |
| 88 | + CHECK(arguments[0].key() == "long"); | |
| 89 | + CHECK(arguments[0].value() == "true"); | |
| 90 | + CHECK(arguments[0].as<bool>() == true); | |
| 91 | + | |
| 92 | + CHECK(arguments[1].key() == "short"); | |
| 93 | + CHECK(arguments[2].key() == "value"); | |
| 94 | + CHECK(arguments[3].key() == "av"); | |
| 85 | 95 | } |
| 86 | 96 | |
| 87 | 97 | TEST_CASE("Short options", "[options]") |
| ... | ... | @@ -96,36 +106,15 @@ TEST_CASE("Short options", "[options]") |
| 96 | 106 | auto actual_argv = argv.argv(); |
| 97 | 107 | auto argc = argv.argc(); |
| 98 | 108 | |
| 99 | - options.parse(argc, actual_argv); | |
| 109 | + auto result = options.parse(argc, actual_argv); | |
| 100 | 110 | |
| 101 | - CHECK(options.count("a") == 1); | |
| 102 | - CHECK(options["a"].as<std::string>() == "value"); | |
| 111 | + CHECK(result.count("a") == 1); | |
| 112 | + CHECK(result["a"].as<std::string>() == "value"); | |
| 103 | 113 | |
| 104 | 114 | REQUIRE_THROWS_AS(options.add_options()("", "nothing option"), |
| 105 | 115 | cxxopts::invalid_option_format_error); |
| 106 | 116 | } |
| 107 | 117 | |
| 108 | -TEST_CASE("Required arguments", "[options]") | |
| 109 | -{ | |
| 110 | - cxxopts::Options options("required", " - test required options"); | |
| 111 | - options.add_options() | |
| 112 | - ("one", "one option") | |
| 113 | - ("two", "second option") | |
| 114 | - ; | |
| 115 | - | |
| 116 | - Argv argv({ | |
| 117 | - "required", | |
| 118 | - "--one" | |
| 119 | - }); | |
| 120 | - | |
| 121 | - auto aargv = argv.argv(); | |
| 122 | - auto argc = argv.argc(); | |
| 123 | - | |
| 124 | - options.parse(argc, aargv); | |
| 125 | - REQUIRE_THROWS_AS(cxxopts::check_required(options, {"two"}), | |
| 126 | - cxxopts::option_required_exception); | |
| 127 | -} | |
| 128 | - | |
| 129 | 118 | TEST_CASE("No positional", "[positional]") |
| 130 | 119 | { |
| 131 | 120 | cxxopts::Options options("test_no_positional", |
| ... | ... | @@ -135,7 +124,7 @@ TEST_CASE("No positional", "[positional]") |
| 135 | 124 | |
| 136 | 125 | char** argv = av.argv(); |
| 137 | 126 | auto argc = av.argc(); |
| 138 | - options.parse(argc, argv); | |
| 127 | + auto result = options.parse(argc, argv); | |
| 139 | 128 | |
| 140 | 129 | REQUIRE(argc == 4); |
| 141 | 130 | CHECK(strcmp(argv[1], "a") == 0); |
| ... | ... | @@ -158,7 +147,7 @@ TEST_CASE("All positional", "[positional]") |
| 158 | 147 | |
| 159 | 148 | options.parse_positional("positional"); |
| 160 | 149 | |
| 161 | - options.parse(argc, argv); | |
| 150 | + auto result = options.parse(argc, argv); | |
| 162 | 151 | |
| 163 | 152 | REQUIRE(argc == 1); |
| 164 | 153 | REQUIRE(positional.size() == 3); |
| ... | ... | @@ -186,14 +175,14 @@ TEST_CASE("Some positional explicit", "[positional]") |
| 186 | 175 | char** argv = av.argv(); |
| 187 | 176 | auto argc = av.argc(); |
| 188 | 177 | |
| 189 | - options.parse(argc, argv); | |
| 178 | + auto result = options.parse(argc, argv); | |
| 190 | 179 | |
| 191 | 180 | CHECK(argc == 1); |
| 192 | - CHECK(options.count("output")); | |
| 193 | - CHECK(options["input"].as<std::string>() == "b"); | |
| 194 | - CHECK(options["output"].as<std::string>() == "a"); | |
| 181 | + CHECK(result.count("output")); | |
| 182 | + CHECK(result["input"].as<std::string>() == "b"); | |
| 183 | + CHECK(result["output"].as<std::string>() == "a"); | |
| 195 | 184 | |
| 196 | - auto& positional = options["positional"].as<std::vector<std::string>>(); | |
| 185 | + auto& positional = result["positional"].as<std::vector<std::string>>(); | |
| 197 | 186 | |
| 198 | 187 | REQUIRE(positional.size() == 2); |
| 199 | 188 | CHECK(positional[0] == "c"); |
| ... | ... | @@ -234,10 +223,58 @@ TEST_CASE("Empty with implicit value", "[implicit]") |
| 234 | 223 | char** argv = av.argv(); |
| 235 | 224 | auto argc = av.argc(); |
| 236 | 225 | |
| 237 | - options.parse(argc, argv); | |
| 226 | + auto result = options.parse(argc, argv); | |
| 238 | 227 | |
| 239 | - REQUIRE(options.count("implicit") == 1); | |
| 240 | - REQUIRE(options["implicit"].as<std::string>() == ""); | |
| 228 | + REQUIRE(result.count("implicit") == 1); | |
| 229 | + REQUIRE(result["implicit"].as<std::string>() == ""); | |
| 230 | +} | |
| 231 | + | |
| 232 | +TEST_CASE("Default values", "[default]") | |
| 233 | +{ | |
| 234 | + cxxopts::Options options("defaults", "has defaults"); | |
| 235 | + options.add_options() | |
| 236 | + ("default", "Has implicit", cxxopts::value<int>() | |
| 237 | + ->default_value("42")); | |
| 238 | + | |
| 239 | + SECTION("Sets defaults") { | |
| 240 | + Argv av({"implicit"}); | |
| 241 | + | |
| 242 | + char** argv = av.argv(); | |
| 243 | + auto argc = av.argc(); | |
| 244 | + | |
| 245 | + auto result = options.parse(argc, argv); | |
| 246 | + CHECK(result.count("default") == 1); | |
| 247 | + CHECK(result["default"].as<int>() == 42); | |
| 248 | + } | |
| 249 | + | |
| 250 | + SECTION("When values provided") { | |
| 251 | + Argv av({"implicit", "--default", "5"}); | |
| 252 | + | |
| 253 | + char** argv = av.argv(); | |
| 254 | + auto argc = av.argc(); | |
| 255 | + | |
| 256 | + auto result = options.parse(argc, argv); | |
| 257 | + CHECK(result.count("default") == 1); | |
| 258 | + CHECK(result["default"].as<int>() == 5); | |
| 259 | + } | |
| 260 | +} | |
| 261 | + | |
| 262 | +TEST_CASE("Parse into a reference", "[reference]") | |
| 263 | +{ | |
| 264 | + int value = 0; | |
| 265 | + | |
| 266 | + cxxopts::Options options("into_reference", "parses into a reference"); | |
| 267 | + options.add_options() | |
| 268 | + ("ref", "A reference", cxxopts::value(value)); | |
| 269 | + | |
| 270 | + Argv av({"into_reference", "--ref", "42"}); | |
| 271 | + | |
| 272 | + auto argv = av.argv(); | |
| 273 | + auto argc = av.argc(); | |
| 274 | + | |
| 275 | + auto result = options.parse(argc, argv); | |
| 276 | + CHECK(result.count("ref") == 1); | |
| 277 | + CHECK(value == 42); | |
| 241 | 278 | } |
| 242 | 279 | |
| 243 | 280 | TEST_CASE("Integers", "[options]") |
| ... | ... | @@ -252,11 +289,12 @@ TEST_CASE("Integers", "[options]") |
| 252 | 289 | auto argc = av.argc(); |
| 253 | 290 | |
| 254 | 291 | options.parse_positional("positional"); |
| 255 | - options.parse(argc, argv); | |
| 292 | + auto result = options.parse(argc, argv); | |
| 256 | 293 | |
| 257 | - REQUIRE(options.count("positional") == 7); | |
| 294 | + REQUIRE(result.count("positional") == 7); | |
| 258 | 295 | |
| 259 | - auto& positional = options["positional"].as<std::vector<int>>(); | |
| 296 | + auto& positional = result["positional"].as<std::vector<int>>(); | |
| 297 | + REQUIRE(positional.size() == 7); | |
| 260 | 298 | CHECK(positional[0] == 5); |
| 261 | 299 | CHECK(positional[1] == 6); |
| 262 | 300 | CHECK(positional[2] == -6); |
| ... | ... | @@ -295,11 +333,11 @@ TEST_CASE("Integer bounds", "[integer]") |
| 295 | 333 | auto argc = av.argc(); |
| 296 | 334 | |
| 297 | 335 | options.parse_positional("positional"); |
| 298 | - options.parse(argc, argv); | |
| 336 | + auto result = options.parse(argc, argv); | |
| 299 | 337 | |
| 300 | - REQUIRE(options.count("positional") == 5); | |
| 338 | + REQUIRE(result.count("positional") == 5); | |
| 301 | 339 | |
| 302 | - auto& positional = options["positional"].as<std::vector<int8_t>>(); | |
| 340 | + auto& positional = result["positional"].as<std::vector<int8_t>>(); | |
| 303 | 341 | CHECK(positional[0] == 127); |
| 304 | 342 | CHECK(positional[1] == -128); |
| 305 | 343 | CHECK(positional[2] == 0x7f); |
| ... | ... | @@ -351,14 +389,14 @@ TEST_CASE("Floats", "[options]") |
| 351 | 389 | auto argc = av.argc(); |
| 352 | 390 | |
| 353 | 391 | options.parse_positional("positional"); |
| 354 | - options.parse(argc, argv); | |
| 392 | + auto result = options.parse(argc, argv); | |
| 355 | 393 | |
| 356 | - REQUIRE(options.count("double") == 1); | |
| 357 | - REQUIRE(options.count("positional") == 4); | |
| 394 | + REQUIRE(result.count("double") == 1); | |
| 395 | + REQUIRE(result.count("positional") == 4); | |
| 358 | 396 | |
| 359 | - CHECK(options["double"].as<double>() == 0.5); | |
| 397 | + CHECK(result["double"].as<double>() == 0.5); | |
| 360 | 398 | |
| 361 | - auto& positional = options["positional"].as<std::vector<float>>(); | |
| 399 | + auto& positional = result["positional"].as<std::vector<float>>(); | |
| 362 | 400 | CHECK(positional[0] == 4); |
| 363 | 401 | CHECK(positional[1] == -4); |
| 364 | 402 | CHECK(positional[2] == 1.5e6); |
| ... | ... | @@ -378,3 +416,27 @@ TEST_CASE("Invalid integers", "[integer]") { |
| 378 | 416 | options.parse_positional("positional"); |
| 379 | 417 | CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type); |
| 380 | 418 | } |
| 419 | + | |
| 420 | +TEST_CASE("Booleans", "[boolean]") { | |
| 421 | + cxxopts::Options options("parses_floats", "parses floats correctly"); | |
| 422 | + options.add_options() | |
| 423 | + ("bool", "A Boolean", cxxopts::value<bool>()) | |
| 424 | + ("debug", "Debugging", cxxopts::value<bool>()) | |
| 425 | + ("timing", "Timing", cxxopts::value<bool>()) | |
| 426 | + ; | |
| 427 | + | |
| 428 | + Argv av({"booleans", "--bool=false", "--debug", "true", "--timing"}); | |
| 429 | + | |
| 430 | + char** argv = av.argv(); | |
| 431 | + auto argc = av.argc(); | |
| 432 | + | |
| 433 | + auto result = options.parse(argc, argv); | |
| 434 | + | |
| 435 | + REQUIRE(result.count("bool") == 1); | |
| 436 | + REQUIRE(result.count("debug") == 1); | |
| 437 | + REQUIRE(result.count("timing") == 1); | |
| 438 | + | |
| 439 | + CHECK(result["bool"].as<bool>() == false); | |
| 440 | + CHECK(result["debug"].as<bool>() == true); | |
| 441 | + CHECK(result["timing"].as<bool>() == true); | |
| 442 | +} | ... | ... |