Commit 8893afe13cc47dd0be4f25b5ae491e652c146098

Authored by Jarryd Beck
2 parents d7b93084 70b92306

Merge branch '2_0'

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 `&quot;value&quot;`,
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&amp; option, const std::string&amp; 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&lt;std::string&gt; 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&amp; argc, char**&amp; 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&amp; argc, char**&amp; 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&amp; argc, char**&amp; 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&amp; argc, char**&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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&lt;std::string&gt;&amp; 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&amp; 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
... ... @@ -29,4 +29,7 @@ if (CXXOPTS_BUILD_TESTS)
29 29 "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
30 30 "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
31 31 )
  32 +
  33 + add_executable(link_test link_a.cpp link_b.cpp)
  34 + target_link_libraries(link_test cxxopts)
32 35 endif()
... ...
test/link_a.cpp 0 → 100644
  1 +#include "cxxopts.hpp"
  2 +
  3 +int main(int, char**)
  4 +{
  5 + return 0;
  6 +}
... ...
test/link_b.cpp 0 → 100644
  1 +#include <cxxopts.hpp>
... ...
test/options.cpp
... ... @@ -71,17 +71,27 @@ TEST_CASE(&quot;Basic options&quot;, &quot;[options]&quot;)
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(&quot;Short options&quot;, &quot;[options]&quot;)
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(&quot;No positional&quot;, &quot;[positional]&quot;)
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(&quot;All positional&quot;, &quot;[positional]&quot;)
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(&quot;Some positional explicit&quot;, &quot;[positional]&quot;)
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(&quot;Empty with implicit value&quot;, &quot;[implicit]&quot;)
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(&quot;Integers&quot;, &quot;[options]&quot;)
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(&quot;Integer bounds&quot;, &quot;[integer]&quot;)
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(&quot;Floats&quot;, &quot;[options]&quot;)
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(&quot;Invalid integers&quot;, &quot;[integer]&quot;) {
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 +}
... ...