Rerun C++ SDK
Loading...
Searching...
No Matches
cxxopts.hpp
1#ifdef __GNUC__
2// Rerun uses fairly strict warning settings.
3// Suppress the warnings by treating this as a system header.
4#pragma GCC system_header
5
6// Due to GCC bug any use of regex may cause `maybe-uninitialized` warnings when using address sanitizers.
7// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562
8#pragma GCC diagnostic push
9#if defined __SANITIZE_ADDRESS__ && defined __OPTIMIZE__
10#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
11#endif
12
13#endif
14
15/*
16
17Copyright (c) 2014-2022 Jarryd Beck
18
19Permission is hereby granted, free of charge, to any person obtaining a copy
20of this software and associated documentation files (the "Software"), to deal
21in the Software without restriction, including without limitation the rights
22to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23copies of the Software, and to permit persons to whom the Software is
24furnished to do so, subject to the following conditions:
25
26The above copyright notice and this permission notice shall be included in
27all copies or substantial portions of the Software.
28
29THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35THE SOFTWARE.
36
37<https://github.com/jarro2783/cxxopts/blob/v3.1.1/include/cxxopts.hpp>
38
39*/
40
41// vim: ts=2:sw=2:expandtab
42
43#ifndef CXXOPTS_HPP_INCLUDED
44#define CXXOPTS_HPP_INCLUDED
45
46#include <algorithm>
47#include <cstring>
48#include <exception>
49#include <initializer_list>
50#include <limits>
51#include <locale>
52#include <map>
53#include <memory>
54#include <sstream>
55#include <string>
56#include <unordered_map>
57#include <unordered_set>
58#include <utility>
59#include <vector>
60
61#ifdef CXXOPTS_NO_EXCEPTIONS
62#include <iostream>
63#endif
64
65#if defined(__GNUC__) && !defined(__clang__)
66#if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
67#define CXXOPTS_NO_REGEX true
68#endif
69#endif
70#if defined(_MSC_VER) && !defined(__clang__)
71#define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern
72#define CXXOPTS_LINKONCE __declspec(selectany) extern
73#else
74#define CXXOPTS_LINKONCE_CONST
75#define CXXOPTS_LINKONCE
76#endif
77
78#ifndef CXXOPTS_NO_REGEX
79#include <regex>
80#endif // CXXOPTS_NO_REGEX
81
82// Nonstandard before C++17, which is coincidentally what we also need for <optional>
83#ifdef __has_include
84#if __has_include(<optional>)
85#include <optional>
86#ifdef __cpp_lib_optional
87#define CXXOPTS_HAS_OPTIONAL
88#endif
89#endif
90#endif
91
92#if __cplusplus >= 201603L
93#define CXXOPTS_NODISCARD [[nodiscard]]
94#else
95#define CXXOPTS_NODISCARD
96#endif
97
98#ifndef CXXOPTS_VECTOR_DELIMITER
99#define CXXOPTS_VECTOR_DELIMITER ','
100#endif
101
102#define CXXOPTS__VERSION_MAJOR 3
103#define CXXOPTS__VERSION_MINOR 1
104#define CXXOPTS__VERSION_PATCH 1
105
106#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
107#define CXXOPTS_NULL_DEREF_IGNORE
108#endif
109
110#if defined(__GNUC__)
111#define DO_PRAGMA(x) _Pragma(#x)
112#define CXXOPTS_DIAGNOSTIC_PUSH DO_PRAGMA(GCC diagnostic push)
113#define CXXOPTS_DIAGNOSTIC_POP DO_PRAGMA(GCC diagnostic pop)
114#define CXXOPTS_IGNORE_WARNING(x) DO_PRAGMA(GCC diagnostic ignored x)
115#else
116// define other compilers here if needed
117#define CXXOPTS_DIAGNOSTIC_PUSH
118#define CXXOPTS_DIAGNOSTIC_POP
119#define CXXOPTS_IGNORE_WARNING(x)
120#endif
121
122#ifdef CXXOPTS_NO_RTTI
123#define CXXOPTS_RTTI_CAST static_cast
124#else
125#define CXXOPTS_RTTI_CAST dynamic_cast
126#endif
127
128namespace cxxopts {
129 static constexpr struct {
130 uint8_t major, minor, patch;
131 } version = {CXXOPTS__VERSION_MAJOR, CXXOPTS__VERSION_MINOR, CXXOPTS__VERSION_PATCH};
132} // namespace cxxopts
133
134//when we ask cxxopts to use Unicode, help strings are processed using ICU,
135//which results in the correct lengths being computed for strings when they
136//are formatted for the help output
137//it is necessary to make sure that <unicode/unistr.h> can be found by the
138//compiler, and that icu-uc is linked in to the binary.
139
140#ifdef CXXOPTS_USE_UNICODE
141#include <unicode/unistr.h>
142
143namespace cxxopts {
144
145 using String = icu::UnicodeString;
146
147 inline String toLocalString(std::string s) {
148 return icu::UnicodeString::fromUTF8(std::move(s));
149 }
150
151 // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
152 // warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
153 CXXOPTS_DIAGNOSTIC_PUSH
154 CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
155
156 // This will be ignored under other compilers like LLVM clang.
157 class UnicodeStringIterator {
158 public:
159 using iterator_category = std::forward_iterator_tag;
160 using value_type = int32_t;
161 using difference_type = std::ptrdiff_t;
162 using pointer = value_type*;
163 using reference = value_type&;
164
165 UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) : s(string), i(pos) {}
166
167 value_type operator*() const {
168 return s->char32At(i);
169 }
170
171 bool operator==(const UnicodeStringIterator& rhs) const {
172 return s == rhs.s && i == rhs.i;
173 }
174
175 bool operator!=(const UnicodeStringIterator& rhs) const {
176 return !(*this == rhs);
177 }
178
179 UnicodeStringIterator& operator++() {
180 ++i;
181 return *this;
182 }
183
184 UnicodeStringIterator operator+(int32_t v) {
185 return UnicodeStringIterator(s, i + v);
186 }
187
188 private:
189 const icu::UnicodeString* s;
190 int32_t i;
191 };
192
193 CXXOPTS_DIAGNOSTIC_POP
194
195 inline String& stringAppend(String& s, String a) {
196 return s.append(std::move(a));
197 }
198
199 inline String& stringAppend(String& s, std::size_t n, UChar32 c) {
200 for (std::size_t i = 0; i != n; ++i) {
201 s.append(c);
202 }
203
204 return s;
205 }
206
207 template <typename Iterator>
208 String& stringAppend(String& s, Iterator begin, Iterator end) {
209 while (begin != end) {
210 s.append(*begin);
211 ++begin;
212 }
213
214 return s;
215 }
216
217 inline std::size_t stringLength(const String& s) {
218 return s.length();
219 }
220
221 inline std::string toUTF8String(const String& s) {
222 std::string result;
223 s.toUTF8String(result);
224
225 return result;
226 }
227
228 inline bool empty(const String& s) {
229 return s.isEmpty();
230 }
231
232} // namespace cxxopts
233
234namespace std {
235
236 inline cxxopts::UnicodeStringIterator begin(const icu::UnicodeString& s) {
237 return cxxopts::UnicodeStringIterator(&s, 0);
238 }
239
240 inline cxxopts::UnicodeStringIterator end(const icu::UnicodeString& s) {
241 return cxxopts::UnicodeStringIterator(&s, s.length());
242 }
243
244} // namespace std
245
246//ifdef CXXOPTS_USE_UNICODE
247#else
248
249namespace cxxopts {
250
251 using String = std::string;
252
253 template <typename T>
254 T toLocalString(T&& t) {
255 return std::forward<T>(t);
256 }
257
258 inline std::size_t stringLength(const String& s) {
259 return s.length();
260 }
261
262 inline String& stringAppend(String& s, const String& a) {
263 return s.append(a);
264 }
265
266 inline String& stringAppend(String& s, std::size_t n, char c) {
267 return s.append(n, c);
268 }
269
270 template <typename Iterator>
271 String& stringAppend(String& s, Iterator begin, Iterator end) {
272 return s.append(begin, end);
273 }
274
275 template <typename T>
276 std::string toUTF8String(T&& t) {
277 return std::forward<T>(t);
278 }
279
280 inline bool empty(const std::string& s) {
281 return s.empty();
282 }
283
284} // namespace cxxopts
285
286//ifdef CXXOPTS_USE_UNICODE
287#endif
288
289namespace cxxopts {
290
291 namespace {
292#ifdef _WIN32
293 CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
294 CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
295#else
296 CXXOPTS_LINKONCE_CONST std::string LQUOTE("‘");
297 CXXOPTS_LINKONCE_CONST std::string RQUOTE("’");
298#endif
299 } // namespace
300
301 // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we
302 // want to silence it: warning: base class 'class
303 // std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual
304 // destructor This will be ignored under other compilers like LLVM clang.
305 CXXOPTS_DIAGNOSTIC_PUSH
306 CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
307
308 // some older versions of GCC warn under this warning
309 CXXOPTS_IGNORE_WARNING("-Weffc++")
310
311 class Value : public std::enable_shared_from_this<Value> {
312 public:
313 virtual ~Value() = default;
314
315 virtual std::shared_ptr<Value> clone() const = 0;
316
317 virtual void parse(const std::string& text) const = 0;
318
319 virtual void parse() const = 0;
320
321 virtual bool has_default() const = 0;
322
323 virtual bool is_container() const = 0;
324
325 virtual bool has_implicit() const = 0;
326
327 virtual std::string get_default_value() const = 0;
328
329 virtual std::string get_implicit_value() const = 0;
330
331 virtual std::shared_ptr<Value> default_value(const std::string& value) = 0;
332
333 virtual std::shared_ptr<Value> implicit_value(const std::string& value) = 0;
334
335 virtual std::shared_ptr<Value> no_implicit_value() = 0;
336
337 virtual bool is_boolean() const = 0;
338 };
339
340 CXXOPTS_DIAGNOSTIC_POP
341
342 namespace exceptions {
343
344 class exception : public std::exception {
345 public:
346 explicit exception(std::string message) : m_message(std::move(message)) {}
347
348 CXXOPTS_NODISCARD
349 const char* what() const noexcept override {
350 return m_message.c_str();
351 }
352
353 private:
354 std::string m_message;
355 };
356
357 class specification : public exception {
358 public:
359 explicit specification(const std::string& message) : exception(message) {}
360 };
361
362 class parsing : public exception {
363 public:
364 explicit parsing(const std::string& message) : exception(message) {}
365 };
366
368 public:
369 explicit option_already_exists(const std::string& option)
370 : specification("Option " + LQUOTE + option + RQUOTE + " already exists") {}
371 };
372
374 public:
375 explicit invalid_option_format(const std::string& format)
376 : specification("Invalid option format " + LQUOTE + format + RQUOTE) {}
377 };
378
380 public:
381 explicit invalid_option_syntax(const std::string& text)
382 : parsing(
383 "Argument " + LQUOTE + text + RQUOTE +
384 " starts with a - but has incorrect syntax"
385 ) {}
386 };
387
388 class no_such_option : public parsing {
389 public:
390 explicit no_such_option(const std::string& option)
391 : parsing("Option " + LQUOTE + option + RQUOTE + " does not exist") {}
392 };
393
394 class missing_argument : public parsing {
395 public:
396 explicit missing_argument(const std::string& option)
397 : parsing("Option " + LQUOTE + option + RQUOTE + " is missing an argument") {}
398 };
399
401 public:
402 explicit option_requires_argument(const std::string& option)
403 : parsing("Option " + LQUOTE + option + RQUOTE + " requires an argument") {}
404 };
405
407 public:
408 gratuitous_argument_for_option(const std::string& option, const std::string& arg)
409 : parsing(
410 "Option " + LQUOTE + option + RQUOTE +
411 " does not take an argument, but argument " + LQUOTE + arg + RQUOTE + " given"
412 ) {}
413 };
414
416 public:
417 explicit requested_option_not_present(const std::string& option)
418 : parsing("Option " + LQUOTE + option + RQUOTE + " not present") {}
419 };
420
422 public:
423 explicit option_has_no_value(const std::string& option)
424 : exception(
425 !option.empty() ? ("Option " + LQUOTE + option + RQUOTE + " has no value")
426 : "Option has no value"
427 ) {}
428 };
429
431 public:
432 explicit incorrect_argument_type(const std::string& arg)
433 : parsing("Argument " + LQUOTE + arg + RQUOTE + " failed to parse") {}
434 };
435
436 } // namespace exceptions
437
438 template <typename T>
439 void throw_or_mimic(const std::string& text) {
440 static_assert(
441 std::is_base_of<std::exception, T>::value,
442 "throw_or_mimic only works on std::exception and "
443 "deriving classes"
444 );
445
446#ifndef CXXOPTS_NO_EXCEPTIONS
447 // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
448 throw T{text};
449#else
450 // Otherwise manually instantiate the exception, print what() to stderr,
451 // and exit
452 T exception{text};
453 std::cerr << exception.what() << std::endl;
454 std::exit(EXIT_FAILURE);
455#endif
456 }
457
458 using OptionNames = std::vector<std::string>;
459
460 namespace values {
461
462 namespace parser_tool {
463
464 struct IntegerDesc {
465 std::string negative = "";
466 std::string base = "";
467 std::string value = "";
468 };
469
470 struct ArguDesc {
471 std::string arg_name = "";
472 bool grouping = false;
473 bool set_value = false;
474 std::string value = "";
475 };
476
477#ifdef CXXOPTS_NO_REGEX
478 inline IntegerDesc SplitInteger(const std::string& text) {
479 if (text.empty()) {
480 throw_or_mimic<exceptions::incorrect_argument_type>(text);
481 }
482 IntegerDesc desc;
483 const char* pdata = text.c_str();
484 if (*pdata == '-') {
485 pdata += 1;
486 desc.negative = "-";
487 }
488 if (strncmp(pdata, "0x", 2) == 0) {
489 pdata += 2;
490 desc.base = "0x";
491 }
492 if (*pdata != '\0') {
493 desc.value = std::string(pdata);
494 } else {
495 throw_or_mimic<exceptions::incorrect_argument_type>(text);
496 }
497 return desc;
498 }
499
500 inline bool IsTrueText(const std::string& text) {
501 const char* pdata = text.c_str();
502 if (*pdata == 't' || *pdata == 'T') {
503 pdata += 1;
504 if (strncmp(pdata, "rue\0", 4) == 0) {
505 return true;
506 }
507 } else if (strncmp(pdata, "1\0", 2) == 0) {
508 return true;
509 }
510 return false;
511 }
512
513 inline bool IsFalseText(const std::string& text) {
514 const char* pdata = text.c_str();
515 if (*pdata == 'f' || *pdata == 'F') {
516 pdata += 1;
517 if (strncmp(pdata, "alse\0", 5) == 0) {
518 return true;
519 }
520 } else if (strncmp(pdata, "0\0", 2) == 0) {
521 return true;
522 }
523 return false;
524 }
525
526 inline OptionNames split_option_names(const std::string& text) {
527 OptionNames split_names;
528
529 std::string::size_type token_start_pos = 0;
530 auto length = text.length();
531
532 if (length == 0) {
533 throw_or_mimic<exceptions::invalid_option_format>(text);
534 }
535
536 while (token_start_pos < length) {
537 const auto& npos = std::string::npos;
538 auto next_non_space_pos = text.find_first_not_of(' ', token_start_pos);
539 if (next_non_space_pos == npos) {
540 throw_or_mimic<exceptions::invalid_option_format>(text);
541 }
542 token_start_pos = next_non_space_pos;
543 auto next_delimiter_pos = text.find(',', token_start_pos);
544 if (next_delimiter_pos == token_start_pos) {
545 throw_or_mimic<exceptions::invalid_option_format>(text);
546 }
547 if (next_delimiter_pos == npos) {
548 next_delimiter_pos = length;
549 }
550 auto token_length = next_delimiter_pos - token_start_pos;
551 // validate the token itself matches the regex /([:alnum:][-_[:alnum:]]*/
552 {
553 const char* option_name_valid_chars =
554 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
555 "abcdefghijklmnopqrstuvwxyz"
556 "0123456789"
557 "_-.?";
558
559 if (!std::isalnum(text[token_start_pos], std::locale::classic()) ||
560 text.find_first_not_of(option_name_valid_chars, token_start_pos) <
561 next_delimiter_pos) {
562 throw_or_mimic<exceptions::invalid_option_format>(text);
563 }
564 }
565 split_names.emplace_back(text.substr(token_start_pos, token_length));
566 token_start_pos = next_delimiter_pos + 1;
567 }
568 return split_names;
569 }
570
571 inline ArguDesc ParseArgument(const char* arg, bool& matched) {
572 ArguDesc argu_desc;
573 const char* pdata = arg;
574 matched = false;
575 if (strncmp(pdata, "--", 2) == 0) {
576 pdata += 2;
577 if (isalnum(*pdata, std::locale::classic())) {
578 argu_desc.arg_name.push_back(*pdata);
579 pdata += 1;
580 while (isalnum(*pdata, std::locale::classic()) || *pdata == '-' ||
581 *pdata == '_') {
582 argu_desc.arg_name.push_back(*pdata);
583 pdata += 1;
584 }
585 if (argu_desc.arg_name.length() > 1) {
586 if (*pdata == '=') {
587 argu_desc.set_value = true;
588 pdata += 1;
589 if (*pdata != '\0') {
590 argu_desc.value = std::string(pdata);
591 }
592 matched = true;
593 } else if (*pdata == '\0') {
594 matched = true;
595 }
596 }
597 }
598 } else if (strncmp(pdata, "-", 1) == 0) {
599 pdata += 1;
600 argu_desc.grouping = true;
601 while (isalnum(*pdata, std::locale::classic())) {
602 argu_desc.arg_name.push_back(*pdata);
603 pdata += 1;
604 }
605 matched = !argu_desc.arg_name.empty() && *pdata == '\0';
606 }
607 return argu_desc;
608 }
609
610#else // CXXOPTS_NO_REGEX
611
612 namespace {
613 CXXOPTS_LINKONCE
614 std::basic_regex<char> integer_pattern("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
615 CXXOPTS_LINKONCE
616 std::basic_regex<char> truthy_pattern("(t|T)(rue)?|1");
617 CXXOPTS_LINKONCE
618 std::basic_regex<char> falsy_pattern("(f|F)(alse)?|0");
619 CXXOPTS_LINKONCE
620 std::basic_regex<char> option_matcher(
621 "--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)"
622 );
623 CXXOPTS_LINKONCE
624 std::basic_regex<char> option_specifier(
625 "([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*"
626 );
627 CXXOPTS_LINKONCE
628 std::basic_regex<char> option_specifier_separator(", *");
629
630 } // namespace
631
632 inline IntegerDesc SplitInteger(const std::string& text) {
633 std::smatch match;
634 std::regex_match(text, match, integer_pattern);
635
636 if (match.length() == 0) {
637 throw_or_mimic<exceptions::incorrect_argument_type>(text);
638 }
639
640 IntegerDesc desc;
641 desc.negative = match[1];
642 desc.base = match[2];
643 desc.value = match[3];
644
645 if (match.length(4) > 0) {
646 desc.base = match[5];
647 desc.value = "0";
648 return desc;
649 }
650
651 return desc;
652 }
653
654 inline bool IsTrueText(const std::string& text) {
655 std::smatch result;
656 std::regex_match(text, result, truthy_pattern);
657 return !result.empty();
658 }
659
660 inline bool IsFalseText(const std::string& text) {
661 std::smatch result;
662 std::regex_match(text, result, falsy_pattern);
663 return !result.empty();
664 }
665
666 // Gets the option names specified via a single, comma-separated string,
667 // and returns the separate, space-discarded, non-empty names
668 // (without considering which or how many are single-character)
669 inline OptionNames split_option_names(const std::string& text) {
670 if (!std::regex_match(text.c_str(), option_specifier)) {
671 throw_or_mimic<exceptions::invalid_option_format>(text);
672 }
673
674 OptionNames split_names;
675
676 constexpr int use_non_matches{-1};
677 auto token_iterator = std::sregex_token_iterator(
678 text.begin(),
679 text.end(),
680 option_specifier_separator,
681 use_non_matches
682 );
683 std::copy(
684 token_iterator,
685 std::sregex_token_iterator(),
686 std::back_inserter(split_names)
687 );
688 return split_names;
689 }
690
691 inline ArguDesc ParseArgument(const char* arg, bool& matched) {
692 std::match_results<const char*> result;
693 std::regex_match(arg, result, option_matcher);
694 matched = !result.empty();
695
696 ArguDesc argu_desc;
697 if (matched) {
698 argu_desc.arg_name = result[1].str();
699 argu_desc.set_value = result[2].length() > 0;
700 argu_desc.value = result[3].str();
701 if (result[4].length() > 0) {
702 argu_desc.grouping = true;
703 argu_desc.arg_name = result[4].str();
704 }
705 }
706
707 return argu_desc;
708 }
709
710#endif // CXXOPTS_NO_REGEX
711#undef CXXOPTS_NO_REGEX
712 } // namespace parser_tool
713
714 namespace detail {
715
716 template <typename T, bool B>
718
719 template <typename T>
720 struct SignedCheck<T, true> {
721 template <typename U>
722 void operator()(bool negative, U u, const std::string& text) {
723 if (negative) {
724 if (u > static_cast<U>((std::numeric_limits<T>::min)())) {
725 throw_or_mimic<exceptions::incorrect_argument_type>(text);
726 }
727 } else {
728 if (u > static_cast<U>((std::numeric_limits<T>::max)())) {
729 throw_or_mimic<exceptions::incorrect_argument_type>(text);
730 }
731 }
732 }
733 };
734
735 template <typename T>
736 struct SignedCheck<T, false> {
737 template <typename U>
738 void operator()(bool, U, const std::string&) const {}
739 };
740
741 template <typename T, typename U>
742 void check_signed_range(bool negative, U value, const std::string& text) {
743 SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
744 }
745
746 } // namespace detail
747
748 template <typename R, typename T>
749 void checked_negate(R& r, T&& t, const std::string&, std::true_type) {
750 // if we got to here, then `t` is a positive number that fits into
751 // `R`. So to avoid MSVC C4146, we first cast it to `R`.
752 // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
753 r = static_cast<R>(-static_cast<R>(t - 1) - 1);
754 }
755
756 template <typename R, typename T>
757 void checked_negate(R&, T&&, const std::string& text, std::false_type) {
758 throw_or_mimic<exceptions::incorrect_argument_type>(text);
759 }
760
761 template <typename T>
762 void integer_parser(const std::string& text, T& value) {
763 parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
764
765 using US = typename std::make_unsigned<T>::type;
766 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
767
768 const bool negative = int_desc.negative.length() > 0;
769 const uint8_t base = int_desc.base.length() > 0 ? 16 : 10;
770 const std::string& value_match = int_desc.value;
771
772 US result = 0;
773
774 for (char ch : value_match) {
775 US digit = 0;
776
777 if (ch >= '0' && ch <= '9') {
778 digit = static_cast<US>(ch - '0');
779 } else if (base == 16 && ch >= 'a' && ch <= 'f') {
780 digit = static_cast<US>(ch - 'a' + 10);
781 } else if (base == 16 && ch >= 'A' && ch <= 'F') {
782 digit = static_cast<US>(ch - 'A' + 10);
783 } else {
784 throw_or_mimic<exceptions::incorrect_argument_type>(text);
785 }
786
787 const US next = static_cast<US>(result * base + digit);
788 if (result > next) {
789 throw_or_mimic<exceptions::incorrect_argument_type>(text);
790 }
791
792 result = next;
793 }
794
795 detail::check_signed_range<T>(negative, result, text);
796
797 if (negative) {
798 checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
799 } else {
800 value = static_cast<T>(result);
801 }
802 }
803
804 template <typename T>
805 void stringstream_parser(const std::string& text, T& value) {
806 std::stringstream in(text);
807 in >> value;
808 if (!in) {
809 throw_or_mimic<exceptions::incorrect_argument_type>(text);
810 }
811 }
812
813 template <typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
814 void parse_value(const std::string& text, T& value) {
815 integer_parser(text, value);
816 }
817
818 inline void parse_value(const std::string& text, bool& value) {
819 if (parser_tool::IsTrueText(text)) {
820 value = true;
821 return;
822 }
823
824 if (parser_tool::IsFalseText(text)) {
825 value = false;
826 return;
827 }
828
829 throw_or_mimic<exceptions::incorrect_argument_type>(text);
830 }
831
832 inline void parse_value(const std::string& text, std::string& value) {
833 value = text;
834 }
835
836 // The fallback parser. It uses the stringstream parser to parse all types
837 // that have not been overloaded explicitly. It has to be placed in the
838 // source code before all other more specialized templates.
839 template <typename T, typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr>
840 void parse_value(const std::string& text, T& value) {
841 stringstream_parser(text, value);
842 }
843
844 template <typename T>
845 void parse_value(const std::string& text, std::vector<T>& value) {
846 if (text.empty()) {
847 T v;
848 parse_value(text, v);
849 value.emplace_back(std::move(v));
850 return;
851 }
852 std::stringstream in(text);
853 std::string token;
854 while (!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
855 T v;
856 parse_value(token, v);
857 value.emplace_back(std::move(v));
858 }
859 }
860
861#ifdef CXXOPTS_HAS_OPTIONAL
862 template <typename T>
863 void parse_value(const std::string& text, std::optional<T>& value) {
864 T result;
865 parse_value(text, result);
866 value = std::move(result);
867 }
868#endif
869
870 inline void parse_value(const std::string& text, char& c) {
871 if (text.length() != 1) {
872 throw_or_mimic<exceptions::incorrect_argument_type>(text);
873 }
874
875 c = text[0];
876 }
877
878 template <typename T>
880 static constexpr bool value = false;
881 };
882
883 template <typename T>
884 struct type_is_container<std::vector<T>> {
885 static constexpr bool value = true;
886 };
887
888 template <typename T>
889 class abstract_value : public Value {
890 using Self = abstract_value<T>;
891
892 public:
893 abstract_value() : m_result(std::make_shared<T>()), m_store(m_result.get()) {}
894
895 explicit abstract_value(T* t) : m_store(t) {}
896
897 ~abstract_value() override = default;
898
899 abstract_value& operator=(const abstract_value&) = default;
900
901 abstract_value(const abstract_value& rhs) {
902 if (rhs.m_result) {
903 m_result = std::make_shared<T>();
904 m_store = m_result.get();
905 } else {
906 m_store = rhs.m_store;
907 }
908
909 m_default = rhs.m_default;
910 m_implicit = rhs.m_implicit;
911 m_default_value = rhs.m_default_value;
912 m_implicit_value = rhs.m_implicit_value;
913 }
914
915 void parse(const std::string& text) const override {
916 parse_value(text, *m_store);
917 }
918
919 bool is_container() const override {
921 }
922
923 void parse() const override {
924 parse_value(m_default_value, *m_store);
925 }
926
927 bool has_default() const override {
928 return m_default;
929 }
930
931 bool has_implicit() const override {
932 return m_implicit;
933 }
934
935 std::shared_ptr<Value> default_value(const std::string& value) override {
936 m_default = true;
937 m_default_value = value;
938 return shared_from_this();
939 }
940
941 std::shared_ptr<Value> implicit_value(const std::string& value) override {
942 m_implicit = true;
943 m_implicit_value = value;
944 return shared_from_this();
945 }
946
947 std::shared_ptr<Value> no_implicit_value() override {
948 m_implicit = false;
949 return shared_from_this();
950 }
951
952 std::string get_default_value() const override {
953 return m_default_value;
954 }
955
956 std::string get_implicit_value() const override {
957 return m_implicit_value;
958 }
959
960 bool is_boolean() const override {
961 return std::is_same<T, bool>::value;
962 }
963
964 const T& get() const {
965 if (m_store == nullptr) {
966 return *m_result;
967 }
968 return *m_store;
969 }
970
971 protected:
972 std::shared_ptr<T> m_result{};
973 T* m_store{};
974
975 bool m_default = false;
976 bool m_implicit = false;
977
978 std::string m_default_value{};
979 std::string m_implicit_value{};
980 };
981
982 template <typename T>
983 class standard_value : public abstract_value<T> {
984 public:
985 using abstract_value<T>::abstract_value;
986
987 CXXOPTS_NODISCARD
988 std::shared_ptr<Value> clone() const override {
989 return std::make_shared<standard_value<T>>(*this);
990 }
991 };
992
993 template <>
994 class standard_value<bool> : public abstract_value<bool> {
995 public:
996 ~standard_value() override = default;
997
999 set_default_and_implicit();
1000 }
1001
1002 explicit standard_value(bool* b) : abstract_value(b) {
1003 m_implicit = true;
1004 m_implicit_value = "true";
1005 }
1006
1007 std::shared_ptr<Value> clone() const override {
1008 return std::make_shared<standard_value<bool>>(*this);
1009 }
1010
1011 private:
1012 void set_default_and_implicit() {
1013 m_default = true;
1014 m_default_value = "false";
1015 m_implicit = true;
1016 m_implicit_value = "true";
1017 }
1018 };
1019
1020 } // namespace values
1021
1022 template <typename T>
1023 std::shared_ptr<Value> value() {
1024 return std::make_shared<values::standard_value<T>>();
1025 }
1026
1027 template <typename T>
1028 std::shared_ptr<Value> value(T& t) {
1029 return std::make_shared<values::standard_value<T>>(&t);
1030 }
1031
1032 class OptionAdder;
1033
1034 CXXOPTS_NODISCARD
1035 inline const std::string& first_or_empty(const OptionNames& long_names) {
1036 static const std::string empty{""};
1037 return long_names.empty() ? empty : long_names.front();
1038 }
1039
1041 public:
1043 std::string short_, OptionNames long_, String desc, std::shared_ptr<const Value> val
1044 )
1045 : m_short(std::move(short_)),
1046 m_long(std::move(long_)),
1047 m_desc(std::move(desc)),
1048 m_value(std::move(val)),
1049 m_count(0) {
1050 m_hash = std::hash<std::string>{}(first_long_name() + m_short);
1051 }
1052
1053 OptionDetails(const OptionDetails& rhs)
1054 : m_desc(rhs.m_desc), m_value(rhs.m_value->clone()), m_count(rhs.m_count) {}
1055
1056 OptionDetails(OptionDetails&& rhs) = default;
1057
1058 CXXOPTS_NODISCARD
1059 const String& description() const {
1060 return m_desc;
1061 }
1062
1063 CXXOPTS_NODISCARD
1064 const Value& value() const {
1065 return *m_value;
1066 }
1067
1068 CXXOPTS_NODISCARD
1069 std::shared_ptr<Value> make_storage() const {
1070 return m_value->clone();
1071 }
1072
1073 CXXOPTS_NODISCARD
1074 const std::string& short_name() const {
1075 return m_short;
1076 }
1077
1078 CXXOPTS_NODISCARD
1079 const std::string& first_long_name() const {
1080 return first_or_empty(m_long);
1081 }
1082
1083 CXXOPTS_NODISCARD
1084 const std::string& essential_name() const {
1085 return m_long.empty() ? m_short : m_long.front();
1086 }
1087
1088 CXXOPTS_NODISCARD
1089 const OptionNames& long_names() const {
1090 return m_long;
1091 }
1092
1093 std::size_t hash() const {
1094 return m_hash;
1095 }
1096
1097 private:
1098 std::string m_short{};
1099 OptionNames m_long{};
1100 String m_desc{};
1101 std::shared_ptr<const Value> m_value{};
1102 int m_count;
1103
1104 std::size_t m_hash{};
1105 };
1106
1108 std::string s;
1109 OptionNames l;
1110 String desc;
1111 bool has_default;
1112 std::string default_value;
1113 bool has_implicit;
1114 std::string implicit_value;
1115 std::string arg_help;
1116 bool is_container;
1117 bool is_boolean;
1118 };
1119
1121 std::string name{};
1122 std::string description{};
1123 std::vector<HelpOptionDetails> options{};
1124 };
1125
1127 public:
1128 void parse(const std::shared_ptr<const OptionDetails>& details, const std::string& text) {
1129 ensure_value(details);
1130 ++m_count;
1131 m_value->parse(text);
1132 m_long_names = &details->long_names();
1133 }
1134
1135 void parse_default(const std::shared_ptr<const OptionDetails>& details) {
1136 ensure_value(details);
1137 m_default = true;
1138 m_long_names = &details->long_names();
1139 m_value->parse();
1140 }
1141
1142 void parse_no_value(const std::shared_ptr<const OptionDetails>& details) {
1143 m_long_names = &details->long_names();
1144 }
1145
1146#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1147 CXXOPTS_DIAGNOSTIC_PUSH
1148 CXXOPTS_IGNORE_WARNING("-Wnull-dereference")
1149#endif
1150
1151 CXXOPTS_NODISCARD
1152 std::size_t count() const noexcept {
1153 return m_count;
1154 }
1155
1156#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1157 CXXOPTS_DIAGNOSTIC_POP
1158#endif
1159
1160 // TODO: maybe default options should count towards the number of arguments
1161 CXXOPTS_NODISCARD
1162 bool has_default() const noexcept {
1163 return m_default;
1164 }
1165
1166 template <typename T>
1167 const T& as() const {
1168 if (m_value == nullptr) {
1169 throw_or_mimic<exceptions::option_has_no_value>(
1170 m_long_names == nullptr ? "" : first_or_empty(*m_long_names)
1171 );
1172 }
1173
1174 return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
1175 }
1176
1177 private:
1178 void ensure_value(const std::shared_ptr<const OptionDetails>& details) {
1179 if (m_value == nullptr) {
1180 m_value = details->make_storage();
1181 }
1182 }
1183
1184 const OptionNames* m_long_names = nullptr;
1185 // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
1186 // where the key has the string we point to.
1187 std::shared_ptr<Value> m_value{};
1188 std::size_t m_count = 0;
1189 bool m_default = false;
1190 };
1191
1192 class KeyValue {
1193 public:
1194 KeyValue(std::string key_, std::string value_)
1195 : m_key(std::move(key_)), m_value(std::move(value_)) {}
1196
1197 CXXOPTS_NODISCARD
1198 const std::string& key() const {
1199 return m_key;
1200 }
1201
1202 CXXOPTS_NODISCARD
1203 const std::string& value() const {
1204 return m_value;
1205 }
1206
1207 template <typename T>
1208 T as() const {
1209 T result;
1210 values::parse_value(m_value, result);
1211 return result;
1212 }
1213
1214 private:
1215 std::string m_key;
1216 std::string m_value;
1217 };
1218
1219 using ParsedHashMap = std::unordered_map<std::size_t, OptionValue>;
1220 using NameHashMap = std::unordered_map<std::string, std::size_t>;
1221
1223 public:
1224 class Iterator {
1225 public:
1226 using iterator_category = std::forward_iterator_tag;
1227 using value_type = KeyValue;
1228 using difference_type = void;
1229 using pointer = const KeyValue*;
1230 using reference = const KeyValue&;
1231
1232 Iterator() = default;
1233 Iterator(const Iterator&) = default;
1234
1235 // GCC complains about m_iter not being initialised in the member
1236 // initializer list
1237 CXXOPTS_DIAGNOSTIC_PUSH
1238 CXXOPTS_IGNORE_WARNING("-Weffc++")
1239
1240 Iterator(const ParseResult* pr, bool end = false) : m_pr(pr) {
1241 if (end) {
1242 m_sequential = false;
1243 m_iter = m_pr->m_defaults.end();
1244 } else {
1245 m_sequential = true;
1246 m_iter = m_pr->m_sequential.begin();
1247
1248 if (m_iter == m_pr->m_sequential.end()) {
1249 m_sequential = false;
1250 m_iter = m_pr->m_defaults.begin();
1251 }
1252 }
1253 }
1254
1255 CXXOPTS_DIAGNOSTIC_POP
1256
1257 Iterator& operator++() {
1258 ++m_iter;
1259 if (m_sequential && m_iter == m_pr->m_sequential.end()) {
1260 m_sequential = false;
1261 m_iter = m_pr->m_defaults.begin();
1262 return *this;
1263 }
1264 return *this;
1265 }
1266
1267 Iterator operator++(int) {
1268 Iterator retval = *this;
1269 ++(*this);
1270 return retval;
1271 }
1272
1273 bool operator==(const Iterator& other) const {
1274 return (m_sequential == other.m_sequential) && (m_iter == other.m_iter);
1275 }
1276
1277 bool operator!=(const Iterator& other) const {
1278 return !(*this == other);
1279 }
1280
1281 const KeyValue& operator*() {
1282 return *m_iter;
1283 }
1284
1285 const KeyValue* operator->() {
1286 return m_iter.operator->();
1287 }
1288
1289 private:
1290 const ParseResult* m_pr;
1291 std::vector<KeyValue>::const_iterator m_iter;
1292 bool m_sequential = true;
1293 };
1294
1295 ParseResult() = default;
1296 ParseResult(const ParseResult&) = default;
1297
1299 NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential,
1300 std::vector<KeyValue> default_opts, std::vector<std::string>&& unmatched_args
1301 )
1302 : m_keys(std::move(keys)),
1303 m_values(std::move(values)),
1304 m_sequential(std::move(sequential)),
1305 m_defaults(std::move(default_opts)),
1306 m_unmatched(std::move(unmatched_args)) {}
1307
1308 ParseResult& operator=(ParseResult&&) = default;
1309 ParseResult& operator=(const ParseResult&) = default;
1310
1311 Iterator begin() const {
1312 return Iterator(this);
1313 }
1314
1315 Iterator end() const {
1316 return Iterator(this, true);
1317 }
1318
1319 std::size_t count(const std::string& o) const {
1320 auto iter = m_keys.find(o);
1321 if (iter == m_keys.end()) {
1322 return 0;
1323 }
1324
1325 auto viter = m_values.find(iter->second);
1326
1327 if (viter == m_values.end()) {
1328 return 0;
1329 }
1330
1331 return viter->second.count();
1332 }
1333
1334 const OptionValue& operator[](const std::string& option) const {
1335 auto iter = m_keys.find(option);
1336
1337 if (iter == m_keys.end()) {
1338 throw_or_mimic<exceptions::requested_option_not_present>(option);
1339 }
1340
1341 auto viter = m_values.find(iter->second);
1342
1343 if (viter == m_values.end()) {
1344 throw_or_mimic<exceptions::requested_option_not_present>(option);
1345 }
1346
1347 return viter->second;
1348 }
1349
1350 const std::vector<KeyValue>& arguments() const {
1351 return m_sequential;
1352 }
1353
1354 const std::vector<std::string>& unmatched() const {
1355 return m_unmatched;
1356 }
1357
1358 const std::vector<KeyValue>& defaults() const {
1359 return m_defaults;
1360 }
1361
1362 const std::string arguments_string() const {
1363 std::string result;
1364 for (const auto& kv : m_sequential) {
1365 result += kv.key() + " = " + kv.value() + "\n";
1366 }
1367 for (const auto& kv : m_defaults) {
1368 result += kv.key() + " = " + kv.value() + " " + "(default)" + "\n";
1369 }
1370 return result;
1371 }
1372
1373 private:
1374 NameHashMap m_keys{};
1375 ParsedHashMap m_values{};
1376 std::vector<KeyValue> m_sequential{};
1377 std::vector<KeyValue> m_defaults{};
1378 std::vector<std::string> m_unmatched{};
1379 };
1380
1381 struct Option {
1382 Option(
1383 std::string opts, std::string desc,
1384 std::shared_ptr<const Value> value = ::cxxopts::value<bool>(), std::string arg_help = ""
1385 )
1386 : opts_(std::move(opts)),
1387 desc_(std::move(desc)),
1388 value_(std::move(value)),
1389 arg_help_(std::move(arg_help)) {}
1390
1391 std::string opts_;
1392 std::string desc_;
1393 std::shared_ptr<const Value> value_;
1394 std::string arg_help_;
1395 };
1396
1397 using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1398 using PositionalList = std::vector<std::string>;
1399 using PositionalListIterator = PositionalList::const_iterator;
1400
1402 public:
1404 const OptionMap& options, const PositionalList& positional, bool allow_unrecognised
1405 )
1406 : m_options(options),
1407 m_positional(positional),
1408 m_allow_unrecognised(allow_unrecognised) {}
1409
1410 ParseResult parse(int argc, const char* const* argv);
1411
1412 bool consume_positional(const std::string& a, PositionalListIterator& next);
1413
1414 void checked_parse_arg(
1415 int argc, const char* const* argv, int& current,
1416 const std::shared_ptr<OptionDetails>& value, const std::string& name
1417 );
1418
1419 void add_to_option(
1420 OptionMap::const_iterator iter, const std::string& option, const std::string& arg
1421 );
1422
1423 void parse_option(
1424 const std::shared_ptr<OptionDetails>& value, const std::string& name,
1425 const std::string& arg = ""
1426 );
1427
1428 void parse_default(const std::shared_ptr<OptionDetails>& details);
1429
1430 void parse_no_value(const std::shared_ptr<OptionDetails>& details);
1431
1432 private:
1433 void finalise_aliases();
1434
1435 const OptionMap& m_options;
1436 const PositionalList& m_positional;
1437
1438 std::vector<KeyValue> m_sequential{};
1439 std::vector<KeyValue> m_defaults{};
1440 bool m_allow_unrecognised;
1441
1442 ParsedHashMap m_parsed{};
1443 NameHashMap m_keys{};
1444 };
1445
1446 class Options {
1447 public:
1448 explicit Options(std::string program_name, std::string help_string = "")
1449 : m_program(std::move(program_name)),
1450 m_help_string(toLocalString(std::move(help_string))),
1451 m_custom_help("[OPTION...]"),
1452 m_positional_help("positional parameters"),
1453 m_show_positional(false),
1454 m_allow_unrecognised(false),
1455 m_width(76),
1456 m_tab_expansion(false),
1457 m_options(std::make_shared<OptionMap>()) {}
1458
1459 Options& positional_help(std::string help_text) {
1460 m_positional_help = std::move(help_text);
1461 return *this;
1462 }
1463
1464 Options& custom_help(std::string help_text) {
1465 m_custom_help = std::move(help_text);
1466 return *this;
1467 }
1468
1469 Options& show_positional_help() {
1470 m_show_positional = true;
1471 return *this;
1472 }
1473
1474 Options& allow_unrecognised_options() {
1475 m_allow_unrecognised = true;
1476 return *this;
1477 }
1478
1479 Options& set_width(std::size_t width) {
1480 m_width = width;
1481 return *this;
1482 }
1483
1484 Options& set_tab_expansion(bool expansion = true) {
1485 m_tab_expansion = expansion;
1486 return *this;
1487 }
1488
1489 ParseResult parse(int argc, const char* const* argv);
1490
1491 OptionAdder add_options(std::string group = "");
1492
1493 void add_options(const std::string& group, std::initializer_list<Option> options);
1494
1495 void add_option(const std::string& group, const Option& option);
1496
1497 void add_option(
1498 const std::string& group, const std::string& s, const OptionNames& l, std::string desc,
1499 const std::shared_ptr<const Value>& value, std::string arg_help
1500 );
1501
1502 void add_option(
1503 const std::string& group, const std::string& short_name,
1504 const std::string& single_long_name, std::string desc,
1505 const std::shared_ptr<const Value>& value, std::string arg_help
1506 ) {
1507 OptionNames long_names;
1508 long_names.emplace_back(single_long_name);
1509 add_option(group, short_name, long_names, desc, value, arg_help);
1510 }
1511
1512 //parse positional arguments into the given option
1513 void parse_positional(std::string option);
1514
1515 void parse_positional(std::vector<std::string> options);
1516
1517 void parse_positional(std::initializer_list<std::string> options);
1518
1519 template <typename Iterator>
1520 void parse_positional(Iterator begin, Iterator end) {
1521 parse_positional(std::vector<std::string>{begin, end});
1522 }
1523
1524 std::string help(const std::vector<std::string>& groups = {}, bool print_usage = true)
1525 const;
1526
1527 std::vector<std::string> groups() const;
1528
1529 const HelpGroupDetails& group_help(const std::string& group) const;
1530
1531 const std::string& program() const {
1532 return m_program;
1533 }
1534
1535 private:
1536 void add_one_option(
1537 const std::string& option, const std::shared_ptr<OptionDetails>& details
1538 );
1539
1540 String help_one_group(const std::string& group) const;
1541
1542 void generate_group_help(String& result, const std::vector<std::string>& groups) const;
1543
1544 void generate_all_groups_help(String& result) const;
1545
1546 std::string m_program{};
1547 String m_help_string{};
1548 std::string m_custom_help{};
1549 std::string m_positional_help{};
1550 bool m_show_positional;
1551 bool m_allow_unrecognised;
1552 std::size_t m_width;
1553 bool m_tab_expansion;
1554
1555 std::shared_ptr<OptionMap> m_options;
1556 std::vector<std::string> m_positional{};
1557 std::unordered_set<std::string> m_positional_set{};
1558
1559 //mapping from groups to help options
1560 std::map<std::string, HelpGroupDetails> m_help{};
1561 };
1562
1564 public:
1565 OptionAdder(Options& options, std::string group)
1566 : m_options(options), m_group(std::move(group)) {}
1567
1568 OptionAdder& operator()(
1569 const std::string& opts, const std::string& desc,
1570 const std::shared_ptr<const Value>& value = ::cxxopts::value<bool>(),
1571 std::string arg_help = ""
1572 );
1573
1574 private:
1575 Options& m_options;
1576 std::string m_group;
1577 };
1578
1579 namespace {
1580 constexpr std::size_t OPTION_LONGEST = 30;
1581 constexpr std::size_t OPTION_DESC_GAP = 2;
1582
1583 String format_option(const HelpOptionDetails& o) {
1584 const auto& s = o.s;
1585 const auto& l = first_or_empty(o.l);
1586
1587 String result = " ";
1588
1589 if (!s.empty()) {
1590 result += "-" + toLocalString(s);
1591 if (!l.empty()) {
1592 result += ",";
1593 }
1594 } else {
1595 result += " ";
1596 }
1597
1598 if (!l.empty()) {
1599 result += " --" + toLocalString(l);
1600 }
1601
1602 auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
1603
1604 if (!o.is_boolean) {
1605 if (o.has_implicit) {
1606 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
1607 } else {
1608 result += " " + arg;
1609 }
1610 }
1611
1612 return result;
1613 }
1614
1615 String format_description(
1616 const HelpOptionDetails& o, std::size_t start, std::size_t allowed, bool tab_expansion
1617 ) {
1618 auto desc = o.desc;
1619
1620 if (o.has_default && (!o.is_boolean || o.default_value != "false")) {
1621 if (!o.default_value.empty()) {
1622 desc += toLocalString(" (default: " + o.default_value + ")");
1623 } else {
1624 desc += toLocalString(" (default: \"\")");
1625 }
1626 }
1627
1628 String result;
1629
1630 if (tab_expansion) {
1631 String desc2;
1632 auto size = std::size_t{0};
1633 for (auto c = std::begin(desc); c != std::end(desc); ++c) {
1634 if (*c == '\n') {
1635 desc2 += *c;
1636 size = 0;
1637 } else if (*c == '\t') {
1638 auto skip = 8 - size % 8;
1639 stringAppend(desc2, skip, ' ');
1640 size += skip;
1641 } else {
1642 desc2 += *c;
1643 ++size;
1644 }
1645 }
1646 desc = desc2;
1647 }
1648
1649 desc += " ";
1650
1651 auto current = std::begin(desc);
1652 auto previous = current;
1653 auto startLine = current;
1654 auto lastSpace = current;
1655
1656 auto size = std::size_t{};
1657
1658 bool appendNewLine;
1659 bool onlyWhiteSpace = true;
1660
1661 while (current != std::end(desc)) {
1662 appendNewLine = false;
1663 if (*previous == ' ' || *previous == '\t') {
1664 lastSpace = current;
1665 }
1666 if (*current != ' ' && *current != '\t') {
1667 onlyWhiteSpace = false;
1668 }
1669
1670 while (*current == '\n') {
1671 previous = current;
1672 ++current;
1673 appendNewLine = true;
1674 }
1675
1676 if (!appendNewLine && size >= allowed) {
1677 if (lastSpace != startLine) {
1678 current = lastSpace;
1679 previous = current;
1680 }
1681 appendNewLine = true;
1682 }
1683
1684 if (appendNewLine) {
1685 stringAppend(result, startLine, current);
1686 startLine = current;
1687 lastSpace = current;
1688
1689 if (*previous != '\n') {
1690 stringAppend(result, "\n");
1691 }
1692
1693 stringAppend(result, start, ' ');
1694
1695 if (*previous != '\n') {
1696 stringAppend(result, lastSpace, current);
1697 }
1698
1699 onlyWhiteSpace = true;
1700 size = 0;
1701 }
1702
1703 previous = current;
1704 ++current;
1705 ++size;
1706 }
1707
1708 //append whatever is left but ignore whitespace
1709 if (!onlyWhiteSpace) {
1710 stringAppend(result, startLine, previous);
1711 }
1712
1713 return result;
1714 }
1715
1716 } // namespace
1717
1718 inline void Options::add_options(
1719 const std::string& group, std::initializer_list<Option> options
1720 ) {
1721 OptionAdder option_adder(*this, group);
1722 for (const auto& option : options) {
1723 option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
1724 }
1725 }
1726
1727 inline OptionAdder Options::add_options(std::string group) {
1728 return OptionAdder(*this, std::move(group));
1729 }
1730
1731 inline OptionAdder& OptionAdder::operator()(
1732 const std::string& opts, const std::string& desc, const std::shared_ptr<const Value>& value,
1733 std::string arg_help
1734 ) {
1735 OptionNames option_names = values::parser_tool::split_option_names(opts);
1736 // Note: All names will be non-empty; but we must separate the short
1737 // (length-1) and longer names
1738 std::string short_name{""};
1739 auto first_short_name_iter =
1740 std::partition(option_names.begin(), option_names.end(), [&](const std::string& name) {
1741 return name.length() > 1;
1742 });
1743 auto num_length_1_names = (option_names.end() - first_short_name_iter);
1744 switch (num_length_1_names) {
1745 case 1:
1746 short_name = *first_short_name_iter;
1747 option_names.erase(first_short_name_iter);
1748 case 0:
1749 break;
1750 default:
1751 throw_or_mimic<exceptions::invalid_option_format>(opts);
1752 };
1753
1754 m_options.add_option(m_group, short_name, option_names, desc, value, std::move(arg_help));
1755
1756 return *this;
1757 }
1758
1759 inline void OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details) {
1760 // TODO: remove the duplicate code here
1761 auto& store = m_parsed[details->hash()];
1762 store.parse_default(details);
1763 m_defaults.emplace_back(details->essential_name(), details->value().get_default_value());
1764 }
1765
1766 inline void OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details) {
1767 auto& store = m_parsed[details->hash()];
1768 store.parse_no_value(details);
1769 }
1770
1771 inline void OptionParser::parse_option(
1772 const std::shared_ptr<OptionDetails>& value, const std::string& /*name*/,
1773 const std::string& arg
1774 ) {
1775 auto hash = value->hash();
1776 auto& result = m_parsed[hash];
1777 result.parse(value, arg);
1778
1779 m_sequential.emplace_back(value->essential_name(), arg);
1780 }
1781
1782 inline void OptionParser::checked_parse_arg(
1783 int argc, const char* const* argv, int& current,
1784 const std::shared_ptr<OptionDetails>& value, const std::string& name
1785 ) {
1786 if (current + 1 >= argc) {
1787 if (value->value().has_implicit()) {
1788 parse_option(value, name, value->value().get_implicit_value());
1789 } else {
1790 throw_or_mimic<exceptions::missing_argument>(name);
1791 }
1792 } else {
1793 if (value->value().has_implicit()) {
1794 parse_option(value, name, value->value().get_implicit_value());
1795 } else {
1796 parse_option(value, name, argv[current + 1]);
1797 ++current;
1798 }
1799 }
1800 }
1801
1802 inline void OptionParser::add_to_option(
1803 OptionMap::const_iterator iter, const std::string& option, const std::string& arg
1804 ) {
1805 parse_option(iter->second, option, arg);
1806 }
1807
1808 inline bool OptionParser::consume_positional(
1809 const std::string& a, PositionalListIterator& next
1810 ) {
1811 while (next != m_positional.end()) {
1812 auto iter = m_options.find(*next);
1813 if (iter != m_options.end()) {
1814 if (!iter->second->value().is_container()) {
1815 auto& result = m_parsed[iter->second->hash()];
1816 if (result.count() == 0) {
1817 add_to_option(iter, *next, a);
1818 ++next;
1819 return true;
1820 }
1821 ++next;
1822 continue;
1823 }
1824 add_to_option(iter, *next, a);
1825 return true;
1826 }
1827 throw_or_mimic<exceptions::no_such_option>(*next);
1828 }
1829
1830 return false;
1831 }
1832
1833 inline void Options::parse_positional(std::string option) {
1834 parse_positional(std::vector<std::string>{std::move(option)});
1835 }
1836
1837 inline void Options::parse_positional(std::vector<std::string> options) {
1838 m_positional = std::move(options);
1839
1840 m_positional_set.insert(m_positional.begin(), m_positional.end());
1841 }
1842
1843 inline void Options::parse_positional(std::initializer_list<std::string> options) {
1844 parse_positional(std::vector<std::string>(options));
1845 }
1846
1847 inline ParseResult Options::parse(int argc, const char* const* argv) {
1848 OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
1849
1850 return parser.parse(argc, argv);
1851 }
1852
1853 inline ParseResult OptionParser::parse(int argc, const char* const* argv) {
1854 int current = 1;
1855 bool consume_remaining = false;
1856 auto next_positional = m_positional.begin();
1857
1858 std::vector<std::string> unmatched;
1859
1860 while (current != argc) {
1861 if (strcmp(argv[current], "--") == 0) {
1862 consume_remaining = true;
1863 ++current;
1864 break;
1865 }
1866 bool matched = false;
1867 values::parser_tool::ArguDesc argu_desc =
1868 values::parser_tool::ParseArgument(argv[current], matched);
1869
1870 if (!matched) {
1871 //not a flag
1872
1873 // but if it starts with a `-`, then it's an error
1874 if (argv[current][0] == '-' && argv[current][1] != '\0') {
1875 if (!m_allow_unrecognised) {
1876 throw_or_mimic<exceptions::invalid_option_syntax>(argv[current]);
1877 }
1878 }
1879
1880 //if true is returned here then it was consumed, otherwise it is
1881 //ignored
1882 if (consume_positional(argv[current], next_positional)) {
1883 } else {
1884 unmatched.emplace_back(argv[current]);
1885 }
1886 //if we return from here then it was parsed successfully, so continue
1887 } else {
1888 //short or long option?
1889 if (argu_desc.grouping) {
1890 const std::string& s = argu_desc.arg_name;
1891
1892 for (std::size_t i = 0; i != s.size(); ++i) {
1893 std::string name(1, s[i]);
1894 auto iter = m_options.find(name);
1895
1896 if (iter == m_options.end()) {
1897 if (m_allow_unrecognised) {
1898 unmatched.push_back(std::string("-") + s[i]);
1899 continue;
1900 }
1901 //error
1902 throw_or_mimic<exceptions::no_such_option>(name);
1903 }
1904
1905 auto value = iter->second;
1906
1907 if (i + 1 == s.size()) {
1908 //it must be the last argument
1909 checked_parse_arg(argc, argv, current, value, name);
1910 } else if (value->value().has_implicit()) {
1911 parse_option(value, name, value->value().get_implicit_value());
1912 } else if (i + 1 < s.size()) {
1913 std::string arg_value = s.substr(i + 1);
1914 parse_option(value, name, arg_value);
1915 break;
1916 } else {
1917 //error
1918 throw_or_mimic<exceptions::option_requires_argument>(name);
1919 }
1920 }
1921 } else if (argu_desc.arg_name.length() != 0) {
1922 const std::string& name = argu_desc.arg_name;
1923
1924 auto iter = m_options.find(name);
1925
1926 if (iter == m_options.end()) {
1927 if (m_allow_unrecognised) {
1928 // keep unrecognised options in argument list, skip to next argument
1929 unmatched.emplace_back(argv[current]);
1930 ++current;
1931 continue;
1932 }
1933 //error
1934 throw_or_mimic<exceptions::no_such_option>(name);
1935 }
1936
1937 auto opt = iter->second;
1938
1939 //equals provided for long option?
1940 if (argu_desc.set_value) {
1941 //parse the option given
1942
1943 parse_option(opt, name, argu_desc.value);
1944 } else {
1945 //parse the next argument
1946 checked_parse_arg(argc, argv, current, opt, name);
1947 }
1948 }
1949 }
1950
1951 ++current;
1952 }
1953
1954 for (auto& opt : m_options) {
1955 auto& detail = opt.second;
1956 const auto& value = detail->value();
1957
1958 auto& store = m_parsed[detail->hash()];
1959
1960 if (value.has_default()) {
1961 if (!store.count() && !store.has_default()) {
1962 parse_default(detail);
1963 }
1964 } else {
1965 parse_no_value(detail);
1966 }
1967 }
1968
1969 if (consume_remaining) {
1970 while (current < argc) {
1971 if (!consume_positional(argv[current], next_positional)) {
1972 break;
1973 }
1974 ++current;
1975 }
1976
1977 //adjust argv for any that couldn't be swallowed
1978 while (current != argc) {
1979 unmatched.emplace_back(argv[current]);
1980 ++current;
1981 }
1982 }
1983
1984 finalise_aliases();
1985
1986 ParseResult parsed(
1987 std::move(m_keys),
1988 std::move(m_parsed),
1989 std::move(m_sequential),
1990 std::move(m_defaults),
1991 std::move(unmatched)
1992 );
1993 return parsed;
1994 }
1995
1996 inline void OptionParser::finalise_aliases() {
1997 for (auto& option : m_options) {
1998 auto& detail = *option.second;
1999 auto hash = detail.hash();
2000 m_keys[detail.short_name()] = hash;
2001 for (const auto& long_name : detail.long_names()) {
2002 m_keys[long_name] = hash;
2003 }
2004
2005 m_parsed.emplace(hash, OptionValue());
2006 }
2007 }
2008
2009 inline void Options::add_option(const std::string& group, const Option& option) {
2010 add_options(group, {option});
2011 }
2012
2013 inline void Options::add_option(
2014 const std::string& group, const std::string& s, const OptionNames& l, std::string desc,
2015 const std::shared_ptr<const Value>& value, std::string arg_help
2016 ) {
2017 auto stringDesc = toLocalString(std::move(desc));
2018 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
2019
2020 if (!s.empty()) {
2021 add_one_option(s, option);
2022 }
2023
2024 for (const auto& long_name : l) {
2025 add_one_option(long_name, option);
2026 }
2027
2028 //add the help details
2029 auto& options = m_help[group];
2030
2031 options.options.emplace_back(HelpOptionDetails{
2032 s,
2033 l,
2034 stringDesc,
2035 value->has_default(),
2036 value->get_default_value(),
2037 value->has_implicit(),
2038 value->get_implicit_value(),
2039 std::move(arg_help),
2040 value->is_container(),
2041 value->is_boolean()});
2042 }
2043
2044 inline void Options::add_one_option(
2045 const std::string& option, const std::shared_ptr<OptionDetails>& details
2046 ) {
2047 auto in = m_options->emplace(option, details);
2048
2049 if (!in.second) {
2050 throw_or_mimic<exceptions::option_already_exists>(option);
2051 }
2052 }
2053
2054 inline String Options::help_one_group(const std::string& g) const {
2055 using OptionHelp = std::vector<std::pair<String, String>>;
2056
2057 auto group = m_help.find(g);
2058 if (group == m_help.end()) {
2059 return "";
2060 }
2061
2062 OptionHelp format;
2063
2064 std::size_t longest = 0;
2065
2066 String result;
2067
2068 if (!g.empty()) {
2069 result += toLocalString(" " + g + " options:\n");
2070 }
2071
2072 for (const auto& o : group->second.options) {
2073 if (o.l.size() && m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2074 !m_show_positional) {
2075 continue;
2076 }
2077
2078 auto s = format_option(o);
2079 longest = (std::max)(longest, stringLength(s));
2080 format.push_back(std::make_pair(s, String()));
2081 }
2082 longest = (std::min)(longest, OPTION_LONGEST);
2083
2084 //widest allowed description -- min 10 chars for helptext/line
2085 std::size_t allowed = 10;
2086 if (m_width > allowed + longest + OPTION_DESC_GAP) {
2087 allowed = m_width - longest - OPTION_DESC_GAP;
2088 }
2089
2090 auto fiter = format.begin();
2091 for (const auto& o : group->second.options) {
2092 if (o.l.size() && m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2093 !m_show_positional) {
2094 continue;
2095 }
2096
2097 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
2098
2099 result += fiter->first;
2100 if (stringLength(fiter->first) > longest) {
2101 result += '\n';
2102 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2103 } else {
2104 result += toLocalString(
2105 std::string(longest + OPTION_DESC_GAP - stringLength(fiter->first), ' ')
2106 );
2107 }
2108 result += d;
2109 result += '\n';
2110
2111 ++fiter;
2112 }
2113
2114 return result;
2115 }
2116
2117 inline void Options::generate_group_help(
2118 String& result, const std::vector<std::string>& print_groups
2119 ) const {
2120 for (std::size_t i = 0; i != print_groups.size(); ++i) {
2121 const String& group_help_text = help_one_group(print_groups[i]);
2122 if (empty(group_help_text)) {
2123 continue;
2124 }
2125 result += group_help_text;
2126 if (i < print_groups.size() - 1) {
2127 result += '\n';
2128 }
2129 }
2130 }
2131
2132 inline void Options::generate_all_groups_help(String& result) const {
2133 std::vector<std::string> all_groups;
2134
2135 std::transform(
2136 m_help.begin(),
2137 m_help.end(),
2138 std::back_inserter(all_groups),
2139 [](const std::map<std::string, HelpGroupDetails>::value_type& group) {
2140 return group.first;
2141 }
2142 );
2143
2144 generate_group_help(result, all_groups);
2145 }
2146
2147 inline std::string Options::help(const std::vector<std::string>& help_groups, bool print_usage)
2148 const {
2149 String result = m_help_string;
2150 if (print_usage) {
2151 result += "\nUsage:\n " + toLocalString(m_program);
2152 }
2153
2154 if (!m_custom_help.empty()) {
2155 result += " " + toLocalString(m_custom_help);
2156 }
2157
2158 if (!m_positional.empty() && !m_positional_help.empty()) {
2159 result += " " + toLocalString(m_positional_help);
2160 }
2161
2162 result += "\n\n";
2163
2164 if (help_groups.empty()) {
2165 generate_all_groups_help(result);
2166 } else {
2167 generate_group_help(result, help_groups);
2168 }
2169
2170 return toUTF8String(result);
2171 }
2172
2173 inline std::vector<std::string> Options::groups() const {
2174 std::vector<std::string> g;
2175
2176 std::transform(
2177 m_help.begin(),
2178 m_help.end(),
2179 std::back_inserter(g),
2180 [](const std::map<std::string, HelpGroupDetails>::value_type& pair) {
2181 return pair.first;
2182 }
2183 );
2184
2185 return g;
2186 }
2187
2188 inline const HelpGroupDetails& Options::group_help(const std::string& group) const {
2189 return m_help.at(group);
2190 }
2191
2192} // namespace cxxopts
2193
2194#endif //CXXOPTS_HPP_INCLUDED
2195
2196#ifdef __GNUC__
2197#pragma GCC diagnostic pop
2198#endif
Definition cxxopts.hpp:1192
Definition cxxopts.hpp:1563
Definition cxxopts.hpp:1040
Definition cxxopts.hpp:1401
Definition cxxopts.hpp:1126
Definition cxxopts.hpp:1446
Definition cxxopts.hpp:1224
Definition cxxopts.hpp:1222
Definition cxxopts.hpp:311
Definition cxxopts.hpp:344
Definition cxxopts.hpp:394
Definition cxxopts.hpp:388
Definition cxxopts.hpp:362
Definition cxxopts.hpp:357
Definition cxxopts.hpp:889
Definition cxxopts.hpp:983
Definition cxxopts.hpp:1120
Definition cxxopts.hpp:1107
Definition cxxopts.hpp:1381
Definition cxxopts.hpp:717
Definition cxxopts.hpp:470
Definition cxxopts.hpp:879