3 Copyright (c) 2014, 2015, 2016 Jarryd Beck
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 #pragma GCC diagnostic push
30 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
41 #include <unordered_set>
44 //when we ask cxxopts to use Unicode, help strings are processed using ICU,
45 //which results in the correct lengths being computed for strings when they
46 //are formatted for the help output
47 //it is necessary to make sure that <unicode/unistr.h> can be found by the
48 //compiler, and that icu-uc is linked in to the binary.
50 #ifdef CXXOPTS_USE_UNICODE
51 #include <unicode/unistr.h>
55 typedef icu::UnicodeString String
;
59 toLocalString(std::string s
)
61 return icu::UnicodeString::fromUTF8(s
);
64 class UnicodeStringIterator
: public
65 std::iterator
<std::forward_iterator_tag
, int32_t>
69 UnicodeStringIterator(const icu::UnicodeString
* s
, int32_t pos
)
78 return s
->char32At(i
);
82 operator==(const UnicodeStringIterator
& rhs
) const
84 return s
== rhs
.s
&& i
== rhs
.i
;
88 operator!=(const UnicodeStringIterator
& rhs
) const
90 return !(*this == rhs
);
93 UnicodeStringIterator
&
100 UnicodeStringIterator
103 return UnicodeStringIterator(s
, i
+ v
);
107 const icu::UnicodeString
* s
;
113 stringAppend(String
&s
, String a
)
115 return s
.append(std::move(a
));
120 stringAppend(String
& s
, int n
, UChar32 c
)
122 for (int i
= 0; i
!= n
; ++i
)
130 template <typename Iterator
>
132 stringAppend(String
& s
, Iterator begin
, Iterator end
)
145 stringLength(const String
& s
)
152 toUTF8String(const String
& s
)
155 s
.toUTF8String(result
);
163 cxxopts::UnicodeStringIterator
164 begin(const icu::UnicodeString
& s
)
166 return cxxopts::UnicodeStringIterator(&s
, 0);
169 cxxopts::UnicodeStringIterator
170 end(const icu::UnicodeString
& s
)
172 return cxxopts::UnicodeStringIterator(&s
, s
.length());
176 //ifdef CXXOPTS_USE_UNICODE
181 typedef std::string String
;
183 template <typename T
>
192 stringLength(const String
& s
)
199 stringAppend(String
&s
, String a
)
201 return s
.append(std::move(a
));
206 stringAppend(String
& s
, size_t n
, char c
)
208 return s
.append(n
, c
);
211 template <typename Iterator
>
213 stringAppend(String
& s
, Iterator begin
, Iterator end
)
215 return s
.append(begin
, end
);
218 template <typename T
>
222 return std::forward
<T
>(t
);
227 //ifdef CXXOPTS_USE_UNICODE
232 class Value
: public std::enable_shared_from_this
<Value
>
237 parse(const std::string
& text
) const = 0;
246 has_default() const = 0;
249 is_container() const = 0;
252 has_implicit() const = 0;
255 get_default_value() const = 0;
258 get_implicit_value() const = 0;
260 virtual std::shared_ptr
<Value
>
261 default_value(const std::string
& value
) = 0;
263 virtual std::shared_ptr
<Value
>
264 implicit_value(const std::string
& value
) = 0;
267 class OptionException
: public std::exception
270 OptionException(const std::string
& message
)
276 what() const noexcept
278 return m_message
.c_str();
282 std::string m_message
;
285 class OptionSpecException
: public OptionException
289 OptionSpecException(const std::string
& message
)
290 : OptionException(message
)
295 class OptionParseException
: public OptionException
298 OptionParseException(const std::string
& message
)
299 : OptionException(message
)
304 class option_exists_error
: public OptionSpecException
307 option_exists_error(const std::string
& option
)
308 : OptionSpecException(u8
"Option ‘" + option
+ u8
"’ already exists")
313 class invalid_option_format_error
: public OptionSpecException
316 invalid_option_format_error(const std::string
& format
)
317 : OptionSpecException(u8
"Invalid option format ‘" + format
+ u8
"’")
322 class option_not_exists_exception
: public OptionParseException
325 option_not_exists_exception(const std::string
& option
)
326 : OptionParseException(u8
"Option ‘" + option
+ u8
"’ does not exist")
331 class missing_argument_exception
: public OptionParseException
334 missing_argument_exception(const std::string
& option
)
335 : OptionParseException(u8
"Option ‘" + option
+ u8
"’ is missing an argument")
340 class option_requires_argument_exception
: public OptionParseException
343 option_requires_argument_exception(const std::string
& option
)
344 : OptionParseException(u8
"Option ‘" + option
+ u8
"’ requires an argument")
349 class option_not_has_argument_exception
: public OptionParseException
352 option_not_has_argument_exception
354 const std::string
& option
,
355 const std::string
& arg
357 : OptionParseException(
358 u8
"Option ‘" + option
+ u8
"’ does not take an argument, but argument‘"
364 class option_not_present_exception
: public OptionParseException
367 option_not_present_exception(const std::string
& option
)
368 : OptionParseException(u8
"Option ‘" + option
+ u8
"’ not present")
373 class argument_incorrect_type
: public OptionParseException
376 argument_incorrect_type
378 const std::string
& arg
380 : OptionParseException(
381 u8
"Argument ‘" + arg
+ u8
"’ failed to parse"
389 template <typename T
>
391 parse_value(const std::string
& text
, T
& value
)
393 std::istringstream
is(text
);
396 throw argument_incorrect_type(text
);
399 if (is
.rdbuf()->in_avail() != 0)
401 throw argument_incorrect_type(text
);
407 parse_value(const std::string
& /*text*/, bool& value
)
409 //TODO recognise on, off, yes, no, enable, disable
410 //so that we can write --long=yes explicitly
416 parse_value(const std::string
& text
, std::string
& value
)
421 template <typename T
>
423 parse_value(const std::string
& text
, std::vector
<T
>& value
)
426 parse_value(text
, v
);
430 template <typename T
>
433 static constexpr bool value
= true;
437 struct value_has_arg
<bool>
439 static constexpr bool value
= false;
442 template <typename T
>
443 struct type_is_container
445 static constexpr bool value
= false;
448 template <typename T
>
449 struct type_is_container
<std::vector
<T
>>
451 static constexpr bool value
= true;
454 template <typename T
>
455 class standard_value
: public Value
459 : m_result(std::make_shared
<T
>())
460 , m_store(m_result
.get())
470 parse(const std::string
& text
) const
472 if (m_implicit
&& text
.empty())
474 parse_value(m_implicit_value
, *m_store
);
478 parse_value(text
, *m_store
);
485 return type_is_container
<T
>::value
;
491 parse_value(m_default_value
, *m_store
);
497 return value_has_arg
<T
>::value
;
512 virtual std::shared_ptr
<Value
>
513 default_value(const std::string
& value
){
515 m_default_value
= value
;
516 return shared_from_this();
519 virtual std::shared_ptr
<Value
>
520 implicit_value(const std::string
& value
){
522 m_implicit_value
= value
;
523 return shared_from_this();
527 get_default_value() const
529 return m_default_value
;
533 get_implicit_value() const
535 return m_implicit_value
;
541 if (m_store
== nullptr)
552 std::shared_ptr
<T
> m_result
;
554 bool m_default
= false;
555 std::string m_default_value
;
556 bool m_implicit
= false;
557 std::string m_implicit_value
;
561 template <typename T
>
562 std::shared_ptr
<Value
>
565 return std::make_shared
<values::standard_value
<T
>>();
568 template <typename T
>
569 std::shared_ptr
<Value
>
572 return std::make_shared
<values::standard_value
<T
>>(&t
);
582 const String
& description
,
583 std::shared_ptr
<const Value
> value
585 : m_desc(description
)
600 return m_value
->has_arg();
604 parse(const std::string
& text
)
606 m_value
->parse(text
);
622 const Value
& value() const {
626 template <typename T
>
630 #ifdef CXXOPTS_NO_RTTI
631 return static_cast<const values::standard_value
<T
>&>(*m_value
).get();
633 return dynamic_cast<const values::standard_value
<T
>&>(*m_value
).get();
639 std::shared_ptr
<const Value
> m_value
;
643 struct HelpOptionDetails
650 std::string default_value
;
652 std::string implicit_value
;
653 std::string arg_help
;
657 struct HelpGroupDetails
660 std::string description
;
661 std::vector
<HelpOptionDetails
> options
;
668 Options(std::string program
, std::string help_string
= "")
669 : m_program(std::move(program
))
670 , m_help_string(toLocalString(std::move(help_string
)))
671 , m_next_positional(m_positional
.end())
677 parse(int& argc
, char**& argv
);
681 add_options(std::string group
= "");
687 const std::string
& group
,
688 const std::string
& s
,
689 const std::string
& l
,
691 std::shared_ptr
<const Value
> value
,
696 count(const std::string
& o
) const
698 auto iter
= m_options
.find(o
);
699 if (iter
== m_options
.end())
704 return iter
->second
->count();
708 operator[](const std::string
& option
) const
710 auto iter
= m_options
.find(option
);
712 if (iter
== m_options
.end())
714 throw option_not_present_exception(option
);
717 return *iter
->second
;
720 //parse positional arguments into the given option
723 parse_positional(std::string option
);
727 parse_positional(std::vector
<std::string
> options
);
731 help(const std::vector
<std::string
>& groups
= {""}) const;
734 const std::vector
<std::string
>
738 const HelpGroupDetails
&
739 group_help(const std::string
& group
) const;
747 const std::string
& option
,
748 std::shared_ptr
<OptionDetails
> details
753 consume_positional(std::string a
);
757 add_to_option(const std::string
& option
, const std::string
& arg
);
763 std::shared_ptr
<OptionDetails
> value
,
764 const std::string
& name
,
765 const std::string
& arg
= ""
775 std::shared_ptr
<OptionDetails
> value
,
776 const std::string
& name
781 help_one_group(const std::string
& group
) const;
783 std::string m_program
;
784 String m_help_string
;
786 std::map
<std::string
, std::shared_ptr
<OptionDetails
>> m_options
;
787 std::vector
<std::string
> m_positional
;
788 std::vector
<std::string
>::iterator m_next_positional
;
789 std::unordered_set
<std::string
> m_positional_set
;
791 //mapping from groups to help options
792 std::map
<std::string
, HelpGroupDetails
> m_help
;
799 OptionAdder(Options
& options
, std::string group
)
800 : m_options(options
), m_group(std::move(group
))
808 const std::string
& opts
,
809 const std::string
& desc
,
810 std::shared_ptr
<const Value
> value
811 = ::cxxopts::value
<bool>(),
812 std::string arg_help
= ""
828 constexpr int OPTION_LONGEST
= 30;
829 constexpr int OPTION_DESC_GAP
= 2;
831 std::basic_regex
<char> option_matcher
832 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)");
834 std::basic_regex
<char> option_specifier
835 ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)");
840 const HelpOptionDetails
& o
850 result
+= "-" + toLocalString(s
) + ",";
859 result
+= " --" + toLocalString(l
);
864 auto arg
= o
.arg_help
.size() > 0 ? toLocalString(o
.arg_help
) : "arg";
868 result
+= " [=" + arg
+ "(=" + toLocalString(o
.implicit_value
) + ")]";
882 const HelpOptionDetails
& o
,
891 desc
+= toLocalString(" (default: " + o
.default_value
+ ")");
896 auto current
= std::begin(desc
);
897 auto startLine
= current
;
898 auto lastSpace
= current
;
900 auto size
= size_t{};
902 while (current
!= std::end(desc
))
911 if (lastSpace
== startLine
)
913 stringAppend(result
, startLine
, current
+ 1);
914 stringAppend(result
, "\n");
915 stringAppend(result
, start
, ' ');
916 startLine
= current
+ 1;
917 lastSpace
= startLine
;
921 stringAppend(result
, startLine
, lastSpace
);
922 stringAppend(result
, "\n");
923 stringAppend(result
, start
, ' ');
924 startLine
= lastSpace
+ 1;
936 //append whatever is left
937 stringAppend(result
, startLine
, current
);
944 Options::add_options(std::string group
)
946 return OptionAdder(*this, std::move(group
));
950 OptionAdder::operator()
952 const std::string
& opts
,
953 const std::string
& desc
,
954 std::shared_ptr
<const Value
> value
,
958 std::match_results
<const char*> result
;
959 std::regex_match(opts
.c_str(), result
, option_specifier
);
963 throw invalid_option_format_error(opts
);
966 const auto& s
= result
[2];
967 const auto& l
= result
[3];
969 m_options
.add_option(m_group
, s
.str(), l
.str(), desc
, value
,
970 std::move(arg_help
));
976 Options::parse_option
978 std::shared_ptr
<OptionDetails
> value
,
979 const std::string
& /*name*/,
980 const std::string
& arg
987 Options::checked_parse_arg
992 std::shared_ptr
<OptionDetails
> value
,
993 const std::string
& name
996 if (current
+ 1 >= argc
)
998 if (value
->value().has_implicit())
1000 parse_option(value
, name
, "");
1004 throw missing_argument_exception(name
);
1009 if (argv
[current
+ 1][0] == '-' && value
->value().has_implicit())
1011 parse_option(value
, name
, "");
1015 parse_option(value
, name
, argv
[current
+ 1]);
1022 Options::add_to_option(const std::string
& option
, const std::string
& arg
)
1024 auto iter
= m_options
.find(option
);
1026 if (iter
== m_options
.end())
1028 throw option_not_exists_exception(option
);
1031 parse_option(iter
->second
, option
, arg
);
1035 Options::consume_positional(std::string a
)
1037 while (m_next_positional
!= m_positional
.end())
1039 auto iter
= m_options
.find(*m_next_positional
);
1040 if (iter
!= m_options
.end())
1042 if (!iter
->second
->value().is_container())
1044 if (iter
->second
->count() == 0)
1046 add_to_option(*m_next_positional
, a
);
1047 ++m_next_positional
;
1052 ++m_next_positional
;
1058 add_to_option(*m_next_positional
, a
);
1062 ++m_next_positional
;
1069 Options::parse_positional(std::string option
)
1071 parse_positional(std::vector
<std::string
>{option
});
1075 Options::parse_positional(std::vector
<std::string
> options
)
1077 m_positional
= std::move(options
);
1078 m_next_positional
= m_positional
.begin();
1080 m_positional_set
.insert(m_positional
.begin(), m_positional
.end());
1084 Options::parse(int& argc
, char**& argv
)
1090 bool consume_remaining
= false;
1092 while (current
!= argc
)
1094 if (strcmp(argv
[current
], "--") == 0)
1096 consume_remaining
= true;
1101 std::match_results
<const char*> result
;
1102 std::regex_match(argv
[current
], result
, option_matcher
);
1108 //if true is returned here then it was consumed, otherwise it is
1110 if (consume_positional(argv
[current
]))
1115 argv
[nextKeep
] = argv
[current
];
1118 //if we return from here then it was parsed successfully, so continue
1122 //short or long option?
1123 if (result
[4].length() != 0)
1125 const std::string
& s
= result
[4];
1127 for (std::size_t i
= 0; i
!= s
.size(); ++i
)
1129 std::string
name(1, s
[i
]);
1130 auto iter
= m_options
.find(name
);
1132 if (iter
== m_options
.end())
1134 throw option_not_exists_exception(name
);
1137 auto value
= iter
->second
;
1139 //if no argument then just add it
1140 if (!value
->has_arg())
1142 parse_option(value
, name
);
1146 //it must be the last argument
1147 if (i
+ 1 == s
.size())
1149 checked_parse_arg(argc
, argv
, current
, value
, name
);
1151 else if (value
->value().has_implicit())
1153 parse_option(value
, name
, "");
1158 throw option_requires_argument_exception(name
);
1163 else if (result
[1].length() != 0)
1165 const std::string
& name
= result
[1];
1167 auto iter
= m_options
.find(name
);
1169 if (iter
== m_options
.end())
1171 throw option_not_exists_exception(name
);
1174 auto opt
= iter
->second
;
1176 //equals provided for long option?
1177 if (result
[3].length() != 0)
1179 //parse the option given
1181 //but if it doesn't take an argument, this is an error
1182 if (!opt
->has_arg())
1184 throw option_not_has_argument_exception(name
, result
[3]);
1187 parse_option(opt
, name
, result
[3]);
1193 //parse the next argument
1194 checked_parse_arg(argc
, argv
, current
, opt
, name
);
1198 //parse with empty argument
1199 parse_option(opt
, name
);
1209 for (auto& opt
: m_options
)
1211 auto& detail
= opt
.second
;
1212 auto& value
= detail
->value();
1214 if(!detail
->count() && value
.has_default()){
1215 detail
->parse_default();
1219 if (consume_remaining
)
1221 while (current
< argc
)
1223 consume_positional(argv
[current
]);
1235 const std::string
& group
,
1236 const std::string
& s
,
1237 const std::string
& l
,
1239 std::shared_ptr
<const Value
> value
,
1240 std::string arg_help
1243 auto stringDesc
= toLocalString(std::move(desc
));
1244 auto option
= std::make_shared
<OptionDetails
>(stringDesc
, value
);
1248 add_one_option(s
, option
);
1253 add_one_option(l
, option
);
1256 //add the help details
1257 auto& options
= m_help
[group
];
1259 options
.options
.emplace_back(HelpOptionDetails
{s
, l
, stringDesc
,
1261 value
->has_default(), value
->get_default_value(),
1262 value
->has_implicit(), value
->get_implicit_value(),
1263 std::move(arg_help
),
1264 value
->is_container()});
1268 Options::add_one_option
1270 const std::string
& option
,
1271 std::shared_ptr
<OptionDetails
> details
1274 auto in
= m_options
.emplace(option
, details
);
1278 throw option_exists_error(option
);
1283 Options::help_one_group(const std::string
& g
) const
1285 typedef std::vector
<std::pair
<String
, String
>> OptionHelp
;
1287 auto group
= m_help
.find(g
);
1288 if (group
== m_help
.end())
1301 result
+= toLocalString(" " + g
+ " options:\n");
1304 for (const auto& o
: group
->second
.options
)
1306 if (o
.is_container
&& m_positional_set
.find(o
.l
) != m_positional_set
.end())
1311 auto s
= format_option(o
);
1312 longest
= std::max(longest
, stringLength(s
));
1313 format
.push_back(std::make_pair(s
, String()));
1316 longest
= std::min(longest
, static_cast<size_t>(OPTION_LONGEST
));
1318 //widest allowed description
1319 auto allowed
= size_t{76} - longest
- OPTION_DESC_GAP
;
1321 auto fiter
= format
.begin();
1322 for (const auto& o
: group
->second
.options
)
1324 if (o
.is_container
&& m_positional_set
.find(o
.l
) != m_positional_set
.end())
1329 auto d
= format_description(o
, longest
+ OPTION_DESC_GAP
, allowed
);
1331 result
+= fiter
->first
;
1332 if (stringLength(fiter
->first
) > longest
)
1335 result
+= toLocalString(std::string(longest
+ OPTION_DESC_GAP
, ' '));
1339 result
+= toLocalString(std::string(longest
+ OPTION_DESC_GAP
-
1340 stringLength(fiter
->first
),
1353 Options::help(const std::vector
<std::string
>& groups
) const
1355 String result
= m_help_string
+ "\nUsage:\n " +
1356 toLocalString(m_program
) + " [OPTION...]";
1358 if (m_positional
.size() > 0) {
1359 result
+= " positional parameters";
1364 for (std::size_t i
= 0; i
< groups
.size(); ++i
)
1366 String
const& group_help
= help_one_group(groups
[i
]);
1367 if (group_help
.empty()) continue;
1368 result
+= group_help
;
1369 if (i
< groups
.size() - 1)
1375 return toUTF8String(result
);
1378 const std::vector
<std::string
>
1379 Options::groups() const
1381 std::vector
<std::string
> g
;
1386 std::back_inserter(g
),
1387 [] (const std::map
<std::string
, HelpGroupDetails
>::value_type
& pair
)
1396 const HelpGroupDetails
&
1397 Options::group_help(const std::string
& group
) const
1399 return m_help
.at(group
);
1404 #if defined(__GNU__)
1405 #pragma GCC diagnostic pop
1408 #endif //CXX_OPTS_HPP