OpenAstra
 
Loading...
Searching...
No Matches
cxxopts.hpp
1/*
2
3Copyright (c) 2014-2022 Jarryd Beck
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
22
23*/
24
25// vim: ts=2:sw=2:expandtab
26
27#ifndef CXXOPTS_HPP_INCLUDED
28#define CXXOPTS_HPP_INCLUDED
29
30#include <cstdint>
31#include <cstdlib>
32#include <cstring>
33#include <exception>
34#include <limits>
35#include <initializer_list>
36#include <map>
37#include <memory>
38#include <sstream>
39#include <string>
40#include <unordered_map>
41#include <unordered_set>
42#include <utility>
43#include <vector>
44#include <algorithm>
45#include <locale>
46
47#ifdef CXXOPTS_NO_EXCEPTIONS
48#include <iostream>
49#endif
50
51#if defined(__GNUC__) && !defined(__clang__)
52# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
53# define CXXOPTS_NO_REGEX true
54# endif
55#endif
56#if defined(_MSC_VER) && !defined(__clang__)
57#define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern
58#define CXXOPTS_LINKONCE __declspec(selectany) extern
59#else
60#define CXXOPTS_LINKONCE_CONST
61#define CXXOPTS_LINKONCE
62#endif
63
64#ifndef CXXOPTS_NO_REGEX
65# include <regex>
66#endif // CXXOPTS_NO_REGEX
67
68// Nonstandard before C++17, which is coincidentally what we also need for <optional>
69#ifdef __has_include
70# if __has_include(<optional>)
71# include <optional>
72# ifdef __cpp_lib_optional
73# define CXXOPTS_HAS_OPTIONAL
74# endif
75# endif
76# if __has_include(<filesystem>)
77# include <filesystem>
78# ifdef __cpp_lib_filesystem
79# define CXXOPTS_HAS_FILESYSTEM
80# endif
81# endif
82#endif
83
84#define CXXOPTS_FALLTHROUGH
85#ifdef __has_cpp_attribute
86 #if __has_cpp_attribute(fallthrough)
87 #undef CXXOPTS_FALLTHROUGH
88 #define CXXOPTS_FALLTHROUGH [[fallthrough]]
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 3
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 {
129static constexpr struct {
130 uint8_t major, minor, patch;
131} version = {
132 CXXOPTS__VERSION_MAJOR,
133 CXXOPTS__VERSION_MINOR,
134 CXXOPTS__VERSION_PATCH
135};
136} // namespace cxxopts
137
138//when we ask cxxopts to use Unicode, help strings are processed using ICU,
139//which results in the correct lengths being computed for strings when they
140//are formatted for the help output
141//it is necessary to make sure that <unicode/unistr.h> can be found by the
142//compiler, and that icu-uc is linked in to the binary.
143
144#ifdef CXXOPTS_USE_UNICODE
145#include <unicode/unistr.h>
146
147namespace cxxopts {
148
149using String = icu::UnicodeString;
150
151inline
152String
153toLocalString(std::string s)
154{
155 return icu::UnicodeString::fromUTF8(std::move(s));
156}
157
158// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
159// warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
160CXXOPTS_DIAGNOSTIC_PUSH
161CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
162// This will be ignored under other compilers like LLVM clang.
163class UnicodeStringIterator
164{
165 public:
166
167 using iterator_category = std::forward_iterator_tag;
168 using value_type = int32_t;
169 using difference_type = std::ptrdiff_t;
170 using pointer = value_type*;
171 using reference = value_type&;
172
173 UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
174 : s(string)
175 , i(pos)
176 {
177 }
178
179 value_type
180 operator*() const
181 {
182 return s->char32At(i);
183 }
184
185 bool
186 operator==(const UnicodeStringIterator& rhs) const
187 {
188 return s == rhs.s && i == rhs.i;
189 }
190
191 bool
192 operator!=(const UnicodeStringIterator& rhs) const
193 {
194 return !(*this == rhs);
195 }
196
197 UnicodeStringIterator&
198 operator++()
199 {
200 ++i;
201 return *this;
202 }
203
204 UnicodeStringIterator
205 operator+(int32_t v)
206 {
207 return UnicodeStringIterator(s, i + v);
208 }
209
210 private:
211 const icu::UnicodeString* s;
212 int32_t i;
213};
214CXXOPTS_DIAGNOSTIC_POP
215
216inline
217String&
218stringAppend(String&s, String a)
219{
220 return s.append(std::move(a));
221}
222
223inline
224String&
225stringAppend(String& s, std::size_t n, UChar32 c)
226{
227 for (std::size_t i = 0; i != n; ++i)
228 {
229 s.append(c);
230 }
231
232 return s;
233}
234
235template <typename Iterator>
236String&
237stringAppend(String& s, Iterator begin, Iterator end)
238{
239 while (begin != end)
240 {
241 s.append(*begin);
242 ++begin;
243 }
244
245 return s;
246}
247
248inline
249size_t
250stringLength(const String& s)
251{
252 return static_cast<size_t>(s.length());
253}
254
255inline
256std::string
257toUTF8String(const String& s)
258{
259 std::string result;
260 s.toUTF8String(result);
261
262 return result;
263}
264
265inline
266bool
267empty(const String& s)
268{
269 return s.isEmpty();
270}
271
272} // namespace cxxopts
273
274namespace std {
275
276inline
277cxxopts::UnicodeStringIterator
278begin(const icu::UnicodeString& s)
279{
280 return cxxopts::UnicodeStringIterator(&s, 0);
281}
282
283inline
284cxxopts::UnicodeStringIterator
285end(const icu::UnicodeString& s)
286{
287 return cxxopts::UnicodeStringIterator(&s, s.length());
288}
289
290} // namespace std
291
292//ifdef CXXOPTS_USE_UNICODE
293#else
294
295namespace cxxopts {
296
297using String = std::string;
298
299template <typename T>
300T
301toLocalString(T&& t)
302{
303 return std::forward<T>(t);
304}
305
306inline
307std::size_t
308stringLength(const String& s)
309{
310 return s.length();
311}
312
313inline
314String&
315stringAppend(String&s, const String& a)
316{
317 return s.append(a);
318}
319
320inline
321String&
322stringAppend(String& s, std::size_t n, char c)
323{
324 return s.append(n, c);
325}
326
327template <typename Iterator>
328String&
329stringAppend(String& s, Iterator begin, Iterator end)
330{
331 return s.append(begin, end);
332}
333
334template <typename T>
335std::string
336toUTF8String(T&& t)
337{
338 return std::forward<T>(t);
339}
340
341inline
342bool
343empty(const std::string& s)
344{
345 return s.empty();
346}
347
348} // namespace cxxopts
349
350//ifdef CXXOPTS_USE_UNICODE
351#endif
352
353namespace cxxopts {
354
355namespace {
356CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
357CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
358} // namespace
359
360// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we
361// want to silence it: warning: base class 'class
362// std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual
363// destructor This will be ignored under other compilers like LLVM clang.
364CXXOPTS_DIAGNOSTIC_PUSH
365CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
366
367// some older versions of GCC warn under this warning
368CXXOPTS_IGNORE_WARNING("-Weffc++")
369class Value : public std::enable_shared_from_this<Value>
370{
371 public:
372
373 virtual ~Value() = default;
374
375 virtual
376 std::shared_ptr<Value>
377 clone() const = 0;
378
379 virtual void
380 add(const std::string& text) const = 0;
381
382 virtual void
383 parse(const std::string& text) const = 0;
384
385 virtual void
386 parse() const = 0;
387
388 virtual bool
389 has_default() const = 0;
390
391 virtual bool
392 is_container() const = 0;
393
394 virtual bool
395 has_implicit() const = 0;
396
397 virtual std::string
398 get_default_value() const = 0;
399
400 virtual std::string
401 get_implicit_value() const = 0;
402
403 virtual std::shared_ptr<Value>
404 default_value(const std::string& value) = 0;
405
406 virtual std::shared_ptr<Value>
407 implicit_value(const std::string& value) = 0;
408
409 virtual std::shared_ptr<Value>
410 no_implicit_value() = 0;
411
412 virtual bool
413 is_boolean() const = 0;
414};
415
416CXXOPTS_DIAGNOSTIC_POP
417
418namespace exceptions {
419
420class exception : public std::exception
421{
422 public:
423 explicit exception(std::string message)
424 : m_message(std::move(message))
425 {
426 }
427
428 CXXOPTS_NODISCARD
429 const char*
430 what() const noexcept override
431 {
432 return m_message.c_str();
433 }
434
435 private:
436 std::string m_message;
437};
438
439class specification : public exception
440{
441 public:
442
443 explicit specification(const std::string& message)
444 : exception(message)
445 {
446 }
447};
448
449class parsing : public exception
450{
451 public:
452 explicit parsing(const std::string& message)
453 : exception(message)
454 {
455 }
456};
457
458class option_already_exists : public specification
459{
460 public:
461 explicit option_already_exists(const std::string& option)
462 : specification("Option " + LQUOTE + option + RQUOTE + " already exists")
463 {
464 }
465};
466
467class invalid_option_format : public specification
468{
469 public:
470 explicit invalid_option_format(const std::string& format)
471 : specification("Invalid option format " + LQUOTE + format + RQUOTE)
472 {
473 }
474};
475
476class invalid_option_syntax : public parsing {
477 public:
478 explicit invalid_option_syntax(const std::string& text)
479 : parsing("Argument " + LQUOTE + text + RQUOTE +
480 " starts with a - but has incorrect syntax")
481 {
482 }
483};
484
485class no_such_option : public parsing
486{
487 public:
488 explicit no_such_option(const std::string& option)
489 : parsing("Option " + LQUOTE + option + RQUOTE + " does not exist")
490 {
491 }
492};
493
494class missing_argument : public parsing
495{
496 public:
497 explicit missing_argument(const std::string& option)
498 : parsing(
499 "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
500 )
501 {
502 }
503};
504
505class option_requires_argument : public parsing
506{
507 public:
508 explicit option_requires_argument(const std::string& option)
509 : parsing(
510 "Option " + LQUOTE + option + RQUOTE + " requires an argument"
511 )
512 {
513 }
514};
515
516class gratuitous_argument_for_option : public parsing
517{
518 public:
519 gratuitous_argument_for_option
520 (
521 const std::string& option,
522 const std::string& arg
523 )
524 : parsing(
525 "Option " + LQUOTE + option + RQUOTE +
526 " does not take an argument, but argument " +
527 LQUOTE + arg + RQUOTE + " given"
528 )
529 {
530 }
531};
532
533class requested_option_not_present : public parsing
534{
535 public:
536 explicit requested_option_not_present(const std::string& option)
537 : parsing("Option " + LQUOTE + option + RQUOTE + " not present")
538 {
539 }
540};
541
542class option_has_no_value : public exception
543{
544 public:
545 explicit option_has_no_value(const std::string& option)
546 : exception(
547 !option.empty() ?
548 ("Option " + LQUOTE + option + RQUOTE + " has no value") :
549 "Option has no value")
550 {
551 }
552};
553
554class incorrect_argument_type : public parsing
555{
556 public:
557 explicit incorrect_argument_type
558 (
559 const std::string& arg
560 )
561 : parsing(
562 "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
563 )
564 {
565 }
566};
567
568} // namespace exceptions
569
570
571template <typename T>
572void throw_or_mimic(const std::string& text)
573{
574 static_assert(std::is_base_of<std::exception, T>::value,
575 "throw_or_mimic only works on std::exception and "
576 "deriving classes");
577
578#ifndef CXXOPTS_NO_EXCEPTIONS
579 // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
580 throw T{text};
581#else
582 // Otherwise manually instantiate the exception, print what() to stderr,
583 // and exit
584 T exception{text};
585 std::cerr << exception.what() << std::endl;
586 std::exit(EXIT_FAILURE);
587#endif
588}
589
590using OptionNames = std::vector<std::string>;
591
592namespace values {
593
594namespace parser_tool {
595
597{
598 std::string negative = "";
599 std::string base = "";
600 std::string value = "";
601};
602struct ArguDesc {
603 std::string arg_name = "";
604 bool grouping = false;
605 bool set_value = false;
606 std::string value = "";
607};
608
609#ifdef CXXOPTS_NO_REGEX
610inline IntegerDesc SplitInteger(const std::string &text)
611{
612 if (text.empty())
613 {
614 throw_or_mimic<exceptions::incorrect_argument_type>(text);
615 }
616 IntegerDesc desc;
617 const char *pdata = text.c_str();
618 if (*pdata == '-')
619 {
620 pdata += 1;
621 desc.negative = "-";
622 }
623 if (strncmp(pdata, "0x", 2) == 0)
624 {
625 pdata += 2;
626 desc.base = "0x";
627 }
628 if (*pdata != '\0')
629 {
630 desc.value = std::string(pdata);
631 }
632 else
633 {
634 throw_or_mimic<exceptions::incorrect_argument_type>(text);
635 }
636 return desc;
637}
638
639inline bool IsTrueText(const std::string &text)
640{
641 const char *pdata = text.c_str();
642 if (*pdata == 't' || *pdata == 'T')
643 {
644 pdata += 1;
645 if (strncmp(pdata, "rue\0", 4) == 0)
646 {
647 return true;
648 }
649 }
650 else if (strncmp(pdata, "1\0", 2) == 0)
651 {
652 return true;
653 }
654 return false;
655}
656
657inline bool IsFalseText(const std::string &text)
658{
659 const char *pdata = text.c_str();
660 if (*pdata == 'f' || *pdata == 'F')
661 {
662 pdata += 1;
663 if (strncmp(pdata, "alse\0", 5) == 0)
664 {
665 return true;
666 }
667 }
668 else if (strncmp(pdata, "0\0", 2) == 0)
669 {
670 return true;
671 }
672 return false;
673}
674
675inline OptionNames split_option_names(const std::string &text)
676{
677 OptionNames split_names;
678
679 std::string::size_type token_start_pos = 0;
680 auto length = text.length();
681
682 if (length == 0)
683 {
684 throw_or_mimic<exceptions::invalid_option_format>(text);
685 }
686
687 while (token_start_pos < length) {
688 const auto &npos = std::string::npos;
689 auto next_non_space_pos = text.find_first_not_of(' ', token_start_pos);
690 if (next_non_space_pos == npos) {
691 throw_or_mimic<exceptions::invalid_option_format>(text);
692 }
693 token_start_pos = next_non_space_pos;
694 auto next_delimiter_pos = text.find(',', token_start_pos);
695 if (next_delimiter_pos == token_start_pos) {
696 throw_or_mimic<exceptions::invalid_option_format>(text);
697 }
698 if (next_delimiter_pos == npos) {
699 next_delimiter_pos = length;
700 }
701 auto token_length = next_delimiter_pos - token_start_pos;
702 // validate the token itself matches the regex /([:alnum:][-_[:alnum:]]*/
703 {
704 const char* option_name_valid_chars =
705 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
706 "abcdefghijklmnopqrstuvwxyz"
707 "0123456789"
708 "_-.?";
709
710 if (!std::isalnum(text[token_start_pos], std::locale::classic()) ||
711 text.find_first_not_of(option_name_valid_chars, token_start_pos) < next_delimiter_pos) {
712 throw_or_mimic<exceptions::invalid_option_format>(text);
713 }
714 }
715 split_names.emplace_back(text.substr(token_start_pos, token_length));
716 token_start_pos = next_delimiter_pos + 1;
717 }
718 return split_names;
719}
720
721inline ArguDesc ParseArgument(const char *arg, bool &matched)
722{
723 ArguDesc argu_desc;
724 const char *pdata = arg;
725 matched = false;
726 if (strncmp(pdata, "--", 2) == 0)
727 {
728 pdata += 2;
729 if (isalnum(*pdata, std::locale::classic()))
730 {
731 argu_desc.arg_name.push_back(*pdata);
732 pdata += 1;
733 while (isalnum(*pdata, std::locale::classic()) || *pdata == '-' || *pdata == '_')
734 {
735 argu_desc.arg_name.push_back(*pdata);
736 pdata += 1;
737 }
738 if (argu_desc.arg_name.length() > 1)
739 {
740 if (*pdata == '=')
741 {
742 argu_desc.set_value = true;
743 pdata += 1;
744 if (*pdata != '\0')
745 {
746 argu_desc.value = std::string(pdata);
747 }
748 matched = true;
749 }
750 else if (*pdata == '\0')
751 {
752 matched = true;
753 }
754 }
755 }
756 }
757 else if (strncmp(pdata, "-", 1) == 0)
758 {
759 pdata += 1;
760 argu_desc.grouping = true;
761 while (isalnum(*pdata, std::locale::classic()))
762 {
763 argu_desc.arg_name.push_back(*pdata);
764 pdata += 1;
765 }
766 matched = !argu_desc.arg_name.empty() && *pdata == '\0';
767 }
768 return argu_desc;
769}
770
771#else // CXXOPTS_NO_REGEX
772
773namespace {
774CXXOPTS_LINKONCE
775const char* const integer_pattern =
776 "(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)";
777CXXOPTS_LINKONCE
778const char* const truthy_pattern =
779 "(t|T)(rue)?|1";
780CXXOPTS_LINKONCE
781const char* const falsy_pattern =
782 "(f|F)(alse)?|0";
783CXXOPTS_LINKONCE
784const char* const option_pattern =
785 "--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)";
786CXXOPTS_LINKONCE
787const char* const option_specifier_pattern =
788 "([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*";
789CXXOPTS_LINKONCE
790const char* const option_specifier_separator_pattern = ", *";
791
792} // namespace
793
794inline IntegerDesc SplitInteger(const std::string &text)
795{
796 static const std::basic_regex<char> integer_matcher(integer_pattern);
797
798 std::smatch match;
799 std::regex_match(text, match, integer_matcher);
800
801 if (match.length() == 0)
802 {
803 throw_or_mimic<exceptions::incorrect_argument_type>(text);
804 }
805
806 IntegerDesc desc;
807 desc.negative = match[1];
808 desc.base = match[2];
809 desc.value = match[3];
810
811 if (match.length(4) > 0)
812 {
813 desc.base = match[5];
814 desc.value = "0";
815 return desc;
816 }
817
818 return desc;
819}
820
821inline bool IsTrueText(const std::string &text)
822{
823 static const std::basic_regex<char> truthy_matcher(truthy_pattern);
824 std::smatch result;
825 std::regex_match(text, result, truthy_matcher);
826 return !result.empty();
827}
828
829inline bool IsFalseText(const std::string &text)
830{
831 static const std::basic_regex<char> falsy_matcher(falsy_pattern);
832 std::smatch result;
833 std::regex_match(text, result, falsy_matcher);
834 return !result.empty();
835}
836
837// Gets the option names specified via a single, comma-separated string,
838// and returns the separate, space-discarded, non-empty names
839// (without considering which or how many are single-character)
840inline OptionNames split_option_names(const std::string &text)
841{
842 static const std::basic_regex<char> option_specifier_matcher(option_specifier_pattern);
843 if (!std::regex_match(text.c_str(), option_specifier_matcher))
844 {
845 throw_or_mimic<exceptions::invalid_option_format>(text);
846 }
847
848 OptionNames split_names;
849
850 static const std::basic_regex<char> option_specifier_separator_matcher(option_specifier_separator_pattern);
851 constexpr int use_non_matches { -1 };
852 auto token_iterator = std::sregex_token_iterator(
853 text.begin(), text.end(), option_specifier_separator_matcher, use_non_matches);
854 std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names));
855 return split_names;
856}
857
858inline ArguDesc ParseArgument(const char *arg, bool &matched)
859{
860 static const std::basic_regex<char> option_matcher(option_pattern);
861 std::match_results<const char*> result;
862 std::regex_match(arg, result, option_matcher);
863 matched = !result.empty();
864
865 ArguDesc argu_desc;
866 if (matched) {
867 argu_desc.arg_name = result[1].str();
868 argu_desc.set_value = result[2].length() > 0;
869 argu_desc.value = result[3].str();
870 if (result[4].length() > 0)
871 {
872 argu_desc.grouping = true;
873 argu_desc.arg_name = result[4].str();
874 }
875 }
876
877 return argu_desc;
878}
879
880#endif // CXXOPTS_NO_REGEX
881#undef CXXOPTS_NO_REGEX
882} // namespace parser_tool
883
884namespace detail {
885
886template <typename T, bool B>
888
889template <typename T>
890struct SignedCheck<T, true>
891{
892 template <typename U>
893 void
894 operator()(bool negative, U u, const std::string& text)
895 {
896 if (negative)
897 {
898 if (u > static_cast<U>((std::numeric_limits<T>::min)()))
899 {
900 throw_or_mimic<exceptions::incorrect_argument_type>(text);
901 }
902 }
903 else
904 {
905 if (u > static_cast<U>((std::numeric_limits<T>::max)()))
906 {
907 throw_or_mimic<exceptions::incorrect_argument_type>(text);
908 }
909 }
910 }
911};
912
913template <typename T>
914struct SignedCheck<T, false>
915{
916 template <typename U>
917 void
918 operator()(bool, U, const std::string&) const {}
919};
920
921template <typename T, typename U>
922void
923check_signed_range(bool negative, U value, const std::string& text)
924{
925 SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
926}
927
928} // namespace detail
929
930template <typename R, typename T>
931void
932checked_negate(R& r, T&& t, const std::string&, std::true_type)
933{
934 // if we got to here, then `t` is a positive number that fits into
935 // `R`. So to avoid MSVC C4146, we first cast it to `R`.
936 // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
937 r = static_cast<R>(-static_cast<R>(t-1)-1);
938}
939
940template <typename R, typename T>
941void
942checked_negate(R&, T&&, const std::string& text, std::false_type)
943{
944 throw_or_mimic<exceptions::incorrect_argument_type>(text);
945}
946
947template <typename T>
948void
949integer_parser(const std::string& text, T& value)
950{
951 parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
952
953 using US = typename std::make_unsigned<T>::type;
954 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
955
956 const bool negative = int_desc.negative.length() > 0;
957 const uint8_t base = int_desc.base.length() > 0 ? 16 : 10;
958 const std::string & value_match = int_desc.value;
959
960 US result = 0;
961
962 for (char ch : value_match)
963 {
964 US digit = 0;
965
966 if (ch >= '0' && ch <= '9')
967 {
968 digit = static_cast<US>(ch - '0');
969 }
970 else if (base == 16 && ch >= 'a' && ch <= 'f')
971 {
972 digit = static_cast<US>(ch - 'a' + 10);
973 }
974 else if (base == 16 && ch >= 'A' && ch <= 'F')
975 {
976 digit = static_cast<US>(ch - 'A' + 10);
977 }
978 else
979 {
980 throw_or_mimic<exceptions::incorrect_argument_type>(text);
981 }
982
983 US limit = 0;
984 if (negative)
985 {
986 limit = static_cast<US>(std::abs(static_cast<intmax_t>((std::numeric_limits<T>::min)())));
987 }
988 else
989 {
990 limit = (std::numeric_limits<T>::max)();
991 }
992
993 if (base != 0 && result > limit / base)
994 {
995 throw_or_mimic<exceptions::incorrect_argument_type>(text);
996 }
997 if (result * base > limit - digit)
998 {
999 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1000 }
1001
1002 result = static_cast<US>(result * base + digit);
1003 }
1004
1005 detail::check_signed_range<T>(negative, result, text);
1006
1007 if (negative)
1008 {
1009 checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
1010 }
1011 else
1012 {
1013 value = static_cast<T>(result);
1014 }
1015}
1016
1017template <typename T>
1018void stringstream_parser(const std::string& text, T& value)
1019{
1020 std::stringstream in(text);
1021 in >> value;
1022 if (!in) {
1023 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1024 }
1025}
1026
1027template <typename T,
1028 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr
1029 >
1030void parse_value(const std::string& text, T& value)
1031{
1032 integer_parser(text, value);
1033}
1034
1035inline
1036void
1037parse_value(const std::string& text, bool& value)
1038{
1039 if (parser_tool::IsTrueText(text))
1040 {
1041 value = true;
1042 return;
1043 }
1044
1045 if (parser_tool::IsFalseText(text))
1046 {
1047 value = false;
1048 return;
1049 }
1050
1051 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1052}
1053
1054inline
1055void
1056parse_value(const std::string& text, std::string& value)
1057{
1058 value = text;
1059}
1060
1061// The fallback parser. It uses the stringstream parser to parse all types
1062// that have not been overloaded explicitly. It has to be placed in the
1063// source code before all other more specialized templates.
1064template <typename T,
1065 typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr
1066 >
1067void
1068parse_value(const std::string& text, T& value) {
1069 stringstream_parser(text, value);
1070}
1071
1072#ifdef CXXOPTS_HAS_OPTIONAL
1073template <typename T>
1074void
1075parse_value(const std::string& text, std::optional<T>& value)
1076{
1077 T result;
1078 parse_value(text, result);
1079 value = std::move(result);
1080}
1081#endif
1082
1083#ifdef CXXOPTS_HAS_FILESYSTEM
1084inline
1085void
1086parse_value(const std::string& text, std::filesystem::path& value)
1087{
1088 value.assign(text);
1089}
1090#endif
1091
1092inline
1093void parse_value(const std::string& text, char& c)
1094{
1095 if (text.length() != 1)
1096 {
1097 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1098 }
1099
1100 c = text[0];
1101}
1102
1103template <typename T>
1104void
1105parse_value(const std::string& text, std::vector<T>& value)
1106{
1107 if (text.empty()) {
1108 T v;
1109 parse_value(text, v);
1110 value.emplace_back(std::move(v));
1111 return;
1112 }
1113 std::stringstream in(text);
1114 std::string token;
1115 while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
1116 T v;
1117 parse_value(token, v);
1118 value.emplace_back(std::move(v));
1119 }
1120}
1121
1122template <typename T>
1123void
1124add_value(const std::string& text, T& value)
1125{
1126 parse_value(text, value);
1127}
1128
1129template <typename T>
1130void
1131add_value(const std::string& text, std::vector<T>& value)
1132{
1133 T v;
1134 add_value(text, v);
1135 value.emplace_back(std::move(v));
1136}
1137
1138template <typename T>
1140{
1141 static constexpr bool value = false;
1142};
1143
1144template <typename T>
1145struct type_is_container<std::vector<T>>
1146{
1147 static constexpr bool value = true;
1148};
1149
1150template <typename T>
1151class abstract_value : public Value
1152{
1153 using Self = abstract_value<T>;
1154
1155 public:
1156 abstract_value()
1157 : m_result(std::make_shared<T>())
1158 , m_store(m_result.get())
1159 {
1160 }
1161
1162 explicit abstract_value(T* t)
1163 : m_store(t)
1164 {
1165 }
1166
1167 ~abstract_value() override = default;
1168
1169 abstract_value& operator=(const abstract_value&) = default;
1170
1171 abstract_value(const abstract_value& rhs)
1172 {
1173 if (rhs.m_result)
1174 {
1175 m_result = std::make_shared<T>();
1176 m_store = m_result.get();
1177 }
1178 else
1179 {
1180 m_store = rhs.m_store;
1181 }
1182
1183 m_default = rhs.m_default;
1184 m_implicit = rhs.m_implicit;
1185 m_default_value = rhs.m_default_value;
1186 m_implicit_value = rhs.m_implicit_value;
1187 }
1188
1189 void
1190 add(const std::string& text) const override
1191 {
1192 add_value(text, *m_store);
1193 }
1194
1195 void
1196 parse(const std::string& text) const override
1197 {
1198 parse_value(text, *m_store);
1199 }
1200
1201 bool
1202 is_container() const override
1203 {
1204 return type_is_container<T>::value;
1205 }
1206
1207 void
1208 parse() const override
1209 {
1210 parse_value(m_default_value, *m_store);
1211 }
1212
1213 bool
1214 has_default() const override
1215 {
1216 return m_default;
1217 }
1218
1219 bool
1220 has_implicit() const override
1221 {
1222 return m_implicit;
1223 }
1224
1225 std::shared_ptr<Value>
1226 default_value(const std::string& value) override
1227 {
1228 m_default = true;
1229 m_default_value = value;
1230 return shared_from_this();
1231 }
1232
1233 std::shared_ptr<Value>
1234 implicit_value(const std::string& value) override
1235 {
1236 m_implicit = true;
1237 m_implicit_value = value;
1238 return shared_from_this();
1239 }
1240
1241 std::shared_ptr<Value>
1242 no_implicit_value() override
1243 {
1244 m_implicit = false;
1245 return shared_from_this();
1246 }
1247
1248 std::string
1249 get_default_value() const override
1250 {
1251 return m_default_value;
1252 }
1253
1254 std::string
1255 get_implicit_value() const override
1256 {
1257 return m_implicit_value;
1258 }
1259
1260 bool
1261 is_boolean() const override
1262 {
1263 return std::is_same<T, bool>::value;
1264 }
1265
1266 const T&
1267 get() const
1268 {
1269 if (m_store == nullptr)
1270 {
1271 return *m_result;
1272 }
1273 return *m_store;
1274 }
1275
1276 protected:
1277 std::shared_ptr<T> m_result{};
1278 T* m_store{};
1279
1280 bool m_default = false;
1281 bool m_implicit = false;
1282
1283 std::string m_default_value{};
1284 std::string m_implicit_value{};
1285};
1286
1287template <typename T>
1288class standard_value : public abstract_value<T>
1289{
1290 public:
1291 using abstract_value<T>::abstract_value;
1292
1293 CXXOPTS_NODISCARD
1294 std::shared_ptr<Value>
1295 clone() const override
1296 {
1297 return std::make_shared<standard_value<T>>(*this);
1298 }
1299};
1300
1301template <>
1302class standard_value<bool> : public abstract_value<bool>
1303{
1304 public:
1305 ~standard_value() override = default;
1306
1307 standard_value()
1308 {
1309 set_default_and_implicit();
1310 }
1311
1312 explicit standard_value(bool* b)
1313 : abstract_value(b)
1314 {
1315 m_implicit = true;
1316 m_implicit_value = "true";
1317 }
1318
1319 std::shared_ptr<Value>
1320 clone() const override
1321 {
1322 return std::make_shared<standard_value<bool>>(*this);
1323 }
1324
1325 private:
1326
1327 void
1328 set_default_and_implicit()
1329 {
1330 m_default = true;
1331 m_default_value = "false";
1332 m_implicit = true;
1333 m_implicit_value = "true";
1334 }
1335};
1336
1337} // namespace values
1338
1339template <typename T>
1340std::shared_ptr<Value>
1341value()
1342{
1343 return std::make_shared<values::standard_value<T>>();
1344}
1345
1346template <typename T>
1347std::shared_ptr<Value>
1348value(T& t)
1349{
1350 return std::make_shared<values::standard_value<T>>(&t);
1351}
1352
1353class OptionAdder;
1354
1355CXXOPTS_NODISCARD
1356inline
1357const std::string&
1358first_or_empty(const OptionNames& long_names)
1359{
1360 static const std::string empty{""};
1361 return long_names.empty() ? empty : long_names.front();
1362}
1363
1364class OptionDetails
1365{
1366 public:
1367 OptionDetails
1368 (
1369 std::string short_,
1370 OptionNames long_,
1371 String desc,
1372 std::shared_ptr<const Value> val
1373 )
1374 : m_short(std::move(short_))
1375 , m_long(std::move(long_))
1376 , m_desc(std::move(desc))
1377 , m_value(std::move(val))
1378 , m_count(0)
1379 {
1380 m_hash = std::hash<std::string>{}(first_long_name() + m_short);
1381 }
1382
1383 OptionDetails(const OptionDetails& rhs)
1384 : m_desc(rhs.m_desc)
1385 , m_value(rhs.m_value->clone())
1386 , m_count(rhs.m_count)
1387 {
1388 }
1389
1390 OptionDetails(OptionDetails&& rhs) = default;
1391
1392 CXXOPTS_NODISCARD
1393 const String&
1394 description() const
1395 {
1396 return m_desc;
1397 }
1398
1399 CXXOPTS_NODISCARD
1400 const Value&
1401 value() const {
1402 return *m_value;
1403 }
1404
1405 CXXOPTS_NODISCARD
1406 std::shared_ptr<Value>
1407 make_storage() const
1408 {
1409 return m_value->clone();
1410 }
1411
1412 CXXOPTS_NODISCARD
1413 const std::string&
1414 short_name() const
1415 {
1416 return m_short;
1417 }
1418
1419 CXXOPTS_NODISCARD
1420 const std::string&
1421 first_long_name() const
1422 {
1423 return first_or_empty(m_long);
1424 }
1425
1426 CXXOPTS_NODISCARD
1427 const std::string&
1428 essential_name() const
1429 {
1430 return m_long.empty() ? m_short : m_long.front();
1431 }
1432
1433 CXXOPTS_NODISCARD
1434 const OptionNames &
1435 long_names() const
1436 {
1437 return m_long;
1438 }
1439
1440 std::size_t
1441 hash() const
1442 {
1443 return m_hash;
1444 }
1445
1446 private:
1447 std::string m_short{};
1448 OptionNames m_long{};
1449 String m_desc{};
1450 std::shared_ptr<const Value> m_value{};
1451 int m_count;
1452
1453 std::size_t m_hash{};
1454};
1455
1457{
1458 std::string s;
1459 OptionNames l;
1460 String desc;
1461 bool has_default;
1462 std::string default_value;
1463 bool has_implicit;
1464 std::string implicit_value;
1465 std::string arg_help;
1466 bool is_container;
1467 bool is_boolean;
1468};
1469
1471{
1472 std::string name{};
1473 std::string description{};
1474 std::vector<HelpOptionDetails> options{};
1475};
1476
1478{
1479 public:
1480 void
1481 add
1482 (
1483 const std::shared_ptr<const OptionDetails>& details,
1484 const std::string& text
1485 )
1486 {
1487 ensure_value(details);
1488 ++m_count;
1489 m_value->add(text);
1490 m_long_names = &details->long_names();
1491 }
1492
1493 void
1494 parse
1495 (
1496 const std::shared_ptr<const OptionDetails>& details,
1497 const std::string& text
1498 )
1499 {
1500 ensure_value(details);
1501 ++m_count;
1502 m_value->parse(text);
1503 m_long_names = &details->long_names();
1504 }
1505
1506 void
1507 parse_default(const std::shared_ptr<const OptionDetails>& details)
1508 {
1509 ensure_value(details);
1510 m_default = true;
1511 m_long_names = &details->long_names();
1512 m_value->parse();
1513 }
1514
1515 void
1516 parse_no_value(const std::shared_ptr<const OptionDetails>& details)
1517 {
1518 m_long_names = &details->long_names();
1519 }
1520
1521#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1522CXXOPTS_DIAGNOSTIC_PUSH
1523CXXOPTS_IGNORE_WARNING("-Wnull-dereference")
1524#endif
1525
1526 CXXOPTS_NODISCARD
1527 std::size_t
1528 count() const noexcept
1529 {
1530 return m_count;
1531 }
1532
1533#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1534CXXOPTS_DIAGNOSTIC_POP
1535#endif
1536
1537 // TODO: maybe default options should count towards the number of arguments
1538 CXXOPTS_NODISCARD
1539 bool
1540 has_default() const noexcept
1541 {
1542 return m_default;
1543 }
1544
1545 template <typename T>
1546 const T&
1547 as() const
1548 {
1549 if (m_value == nullptr) {
1550 throw_or_mimic<exceptions::option_has_no_value>(
1551 m_long_names == nullptr ? "" : first_or_empty(*m_long_names));
1552 }
1553
1554 return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
1555 }
1556
1557#ifdef CXXOPTS_HAS_OPTIONAL
1558 template <typename T>
1559 std::optional<T>
1560 as_optional() const
1561 {
1562 if (m_value == nullptr) {
1563 return std::nullopt;
1564 }
1565 return as<T>();
1566 }
1567#endif
1568
1569 private:
1570 void
1571 ensure_value(const std::shared_ptr<const OptionDetails>& details)
1572 {
1573 if (m_value == nullptr)
1574 {
1575 m_value = details->make_storage();
1576 }
1577 }
1578
1579
1580 const OptionNames * m_long_names = nullptr;
1581 // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
1582 // where the key has the string we point to.
1583 std::shared_ptr<Value> m_value{};
1584 std::size_t m_count = 0;
1585 bool m_default = false;
1586};
1587
1588class KeyValue
1589{
1590 public:
1591 KeyValue(std::string key_, std::string value_) noexcept
1592 : m_key(std::move(key_))
1593 , m_value(std::move(value_))
1594 {
1595 }
1596
1597 CXXOPTS_NODISCARD
1598 const std::string&
1599 key() const
1600 {
1601 return m_key;
1602 }
1603
1604 CXXOPTS_NODISCARD
1605 const std::string&
1606 value() const
1607 {
1608 return m_value;
1609 }
1610
1611 template <typename T>
1612 T
1613 as() const
1614 {
1615 T result;
1616 values::parse_value(m_value, result);
1617 return result;
1618 }
1619
1620 private:
1621 std::string m_key;
1622 std::string m_value;
1623};
1624
1625using ParsedHashMap = std::unordered_map<std::size_t, OptionValue>;
1626using NameHashMap = std::unordered_map<std::string, std::size_t>;
1627
1628class ParseResult
1629{
1630 public:
1631 class Iterator
1632 {
1633 public:
1634 using iterator_category = std::forward_iterator_tag;
1635 using value_type = KeyValue;
1636 using difference_type = void;
1637 using pointer = const KeyValue*;
1638 using reference = const KeyValue&;
1639
1640 Iterator() = default;
1641 Iterator(const Iterator&) = default;
1642
1643// GCC complains about m_iter not being initialised in the member
1644// initializer list
1645CXXOPTS_DIAGNOSTIC_PUSH
1646CXXOPTS_IGNORE_WARNING("-Weffc++")
1647 Iterator(const ParseResult *pr, bool end=false)
1648 : m_pr(pr)
1649 {
1650 if (end)
1651 {
1652 m_sequential = false;
1653 m_iter = m_pr->m_defaults.end();
1654 }
1655 else
1656 {
1657 m_sequential = true;
1658 m_iter = m_pr->m_sequential.begin();
1659
1660 if (m_iter == m_pr->m_sequential.end())
1661 {
1662 m_sequential = false;
1663 m_iter = m_pr->m_defaults.begin();
1664 }
1665 }
1666 }
1667CXXOPTS_DIAGNOSTIC_POP
1668
1669 Iterator& operator++()
1670 {
1671 ++m_iter;
1672 if(m_sequential && m_iter == m_pr->m_sequential.end())
1673 {
1674 m_sequential = false;
1675 m_iter = m_pr->m_defaults.begin();
1676 return *this;
1677 }
1678 return *this;
1679 }
1680
1681 Iterator operator++(int)
1682 {
1683 Iterator retval = *this;
1684 ++(*this);
1685 return retval;
1686 }
1687
1688 bool operator==(const Iterator& other) const
1689 {
1690 return (m_sequential == other.m_sequential) && (m_iter == other.m_iter);
1691 }
1692
1693 bool operator!=(const Iterator& other) const
1694 {
1695 return !(*this == other);
1696 }
1697
1698 const KeyValue& operator*()
1699 {
1700 return *m_iter;
1701 }
1702
1703 const KeyValue* operator->()
1704 {
1705 return m_iter.operator->();
1706 }
1707
1708 private:
1709 const ParseResult* m_pr;
1710 std::vector<KeyValue>::const_iterator m_iter;
1711 bool m_sequential = true;
1712 };
1713
1714 ParseResult() = default;
1715 ParseResult(const ParseResult&) = default;
1716
1717 ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential,
1718 std::vector<KeyValue> default_opts, std::vector<std::string>&& unmatched_args)
1719 : m_keys(std::move(keys))
1720 , m_values(std::move(values))
1721 , m_sequential(std::move(sequential))
1722 , m_defaults(std::move(default_opts))
1723 , m_unmatched(std::move(unmatched_args))
1724 {
1725 }
1726
1727 ParseResult& operator=(ParseResult&&) = default;
1728 ParseResult& operator=(const ParseResult&) = default;
1729
1730 Iterator
1731 begin() const
1732 {
1733 return Iterator(this);
1734 }
1735
1736 Iterator
1737 end() const
1738 {
1739 return Iterator(this, true);
1740 }
1741
1742 std::size_t
1743 count(const std::string& o) const
1744 {
1745 auto iter = m_keys.find(o);
1746 if (iter == m_keys.end())
1747 {
1748 return 0;
1749 }
1750
1751 auto viter = m_values.find(iter->second);
1752
1753 if (viter == m_values.end())
1754 {
1755 return 0;
1756 }
1757
1758 return viter->second.count();
1759 }
1760
1761 bool
1762 contains(const std::string& o) const
1763 {
1764 return static_cast<bool>(count(o));
1765 }
1766
1767 const OptionValue&
1768 operator[](const std::string& option) const
1769 {
1770 auto iter = m_keys.find(option);
1771
1772 if (iter == m_keys.end())
1773 {
1774 throw_or_mimic<exceptions::requested_option_not_present>(option);
1775 }
1776
1777 auto viter = m_values.find(iter->second);
1778
1779 if (viter == m_values.end())
1780 {
1781 throw_or_mimic<exceptions::requested_option_not_present>(option);
1782 }
1783
1784 return viter->second;
1785 }
1786
1787#ifdef CXXOPTS_HAS_OPTIONAL
1788 template <typename T>
1789 std::optional<T>
1790 as_optional(const std::string& option) const
1791 {
1792 auto iter = m_keys.find(option);
1793 if (iter != m_keys.end())
1794 {
1795 auto viter = m_values.find(iter->second);
1796 if (viter != m_values.end())
1797 {
1798 return viter->second.as_optional<T>();
1799 }
1800 }
1801 return std::nullopt;
1802 }
1803#endif
1804
1805 const std::vector<KeyValue>&
1806 arguments() const
1807 {
1808 return m_sequential;
1809 }
1810
1811 const std::vector<std::string>&
1812 unmatched() const
1813 {
1814 return m_unmatched;
1815 }
1816
1817 const std::vector<KeyValue>&
1818 defaults() const
1819 {
1820 return m_defaults;
1821 }
1822
1823 const std::string
1824 arguments_string() const
1825 {
1826 std::string result;
1827 for(const auto& kv: m_sequential)
1828 {
1829 result += kv.key() + " = " + kv.value() + "\n";
1830 }
1831 for(const auto& kv: m_defaults)
1832 {
1833 result += kv.key() + " = " + kv.value() + " " + "(default)" + "\n";
1834 }
1835 return result;
1836 }
1837
1838 private:
1839 NameHashMap m_keys{};
1840 ParsedHashMap m_values{};
1841 std::vector<KeyValue> m_sequential{};
1842 std::vector<KeyValue> m_defaults{};
1843 std::vector<std::string> m_unmatched{};
1844};
1845
1846struct Option
1847{
1848 Option
1849 (
1850 std::string opts,
1851 std::string desc,
1852 std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
1853 std::string arg_help = ""
1854 )
1855 : opts_(std::move(opts))
1856 , desc_(std::move(desc))
1857 , value_(std::move(value))
1858 , arg_help_(std::move(arg_help))
1859 {
1860 }
1861
1862 std::string opts_;
1863 std::string desc_;
1864 std::shared_ptr<const Value> value_;
1865 std::string arg_help_;
1866};
1867
1868using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1869using PositionalList = std::vector<std::string>;
1870using PositionalListIterator = PositionalList::const_iterator;
1871
1872class OptionParser
1873{
1874 public:
1875 OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
1876 : m_options(options)
1877 , m_positional(positional)
1878 , m_allow_unrecognised(allow_unrecognised)
1879 {
1880 }
1881
1883 parse(int argc, const char* const* argv);
1884
1885 bool
1886 consume_positional(const std::string& a, PositionalListIterator& next);
1887
1888 void
1889 checked_parse_arg
1890 (
1891 int argc,
1892 const char* const* argv,
1893 int& current,
1894 const std::shared_ptr<OptionDetails>& value,
1895 const std::string& name
1896 );
1897
1898 void
1899 add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg);
1900
1901 void
1902 parse_option
1903 (
1904 const std::shared_ptr<OptionDetails>& value,
1905 const std::string& name,
1906 const std::string& arg = ""
1907 );
1908
1909 void
1910 parse_default(const std::shared_ptr<OptionDetails>& details);
1911
1912 void
1913 parse_no_value(const std::shared_ptr<OptionDetails>& details);
1914
1915 private:
1916
1917 void finalise_aliases();
1918
1919 const OptionMap& m_options;
1920 const PositionalList& m_positional;
1921
1922 std::vector<KeyValue> m_sequential{};
1923 std::vector<KeyValue> m_defaults{};
1924 bool m_allow_unrecognised;
1925
1926 ParsedHashMap m_parsed{};
1927 NameHashMap m_keys{};
1928};
1929
1930class Options
1931{
1932 public:
1933
1934 explicit Options(std::string program_name, std::string help_string = "")
1935 : m_program(std::move(program_name))
1936 , m_help_string(toLocalString(std::move(help_string)))
1937 , m_custom_help("[OPTION...]")
1938 , m_positional_help("positional parameters")
1939 , m_show_positional(false)
1940 , m_allow_unrecognised(false)
1941 , m_width(76)
1942 , m_tab_expansion(false)
1943 , m_options(std::make_shared<OptionMap>())
1944 {
1945 }
1946
1947 Options&
1948 positional_help(std::string help_text)
1949 {
1950 m_positional_help = std::move(help_text);
1951 return *this;
1952 }
1953
1954 Options&
1955 custom_help(std::string help_text)
1956 {
1957 m_custom_help = std::move(help_text);
1958 return *this;
1959 }
1960
1961 Options&
1962 show_positional_help()
1963 {
1964 m_show_positional = true;
1965 return *this;
1966 }
1967
1968 Options&
1969 allow_unrecognised_options()
1970 {
1971 m_allow_unrecognised = true;
1972 return *this;
1973 }
1974
1975 Options&
1976 set_width(std::size_t width)
1977 {
1978 m_width = width;
1979 return *this;
1980 }
1981
1982 Options&
1983 set_tab_expansion(bool expansion=true)
1984 {
1985 m_tab_expansion = expansion;
1986 return *this;
1987 }
1988
1990 parse(int argc, const char* const* argv);
1991
1993 add_options(std::string group = "");
1994
1995 void
1996 add_options
1997 (
1998 const std::string& group,
1999 std::initializer_list<Option> options
2000 );
2001
2002 void
2003 add_option
2004 (
2005 const std::string& group,
2006 const Option& option
2007 );
2008
2009 void
2010 add_option
2011 (
2012 const std::string& group,
2013 const std::string& s,
2014 const OptionNames& l,
2015 std::string desc,
2016 const std::shared_ptr<const Value>& value,
2017 std::string arg_help
2018 );
2019
2020 void
2021 add_option
2022 (
2023 const std::string& group,
2024 const std::string& short_name,
2025 const std::string& single_long_name,
2026 std::string desc,
2027 const std::shared_ptr<const Value>& value,
2028 std::string arg_help
2029 )
2030 {
2031 OptionNames long_names;
2032 long_names.emplace_back(single_long_name);
2033 add_option(group, short_name, long_names, desc, value, arg_help);
2034 }
2035
2036 //parse positional arguments into the given option
2037 void
2038 parse_positional(std::string option);
2039
2040 void
2041 parse_positional(std::vector<std::string> options);
2042
2043 void
2044 parse_positional(std::initializer_list<std::string> options);
2045
2046 template <typename Iterator>
2047 void
2048 parse_positional(Iterator begin, Iterator end) {
2049 parse_positional(std::vector<std::string>{begin, end});
2050 }
2051
2052 std::string
2053 help(const std::vector<std::string>& groups = {}, bool print_usage=true) const;
2054
2055 std::vector<std::string>
2056 groups() const;
2057
2058 const HelpGroupDetails&
2059 group_help(const std::string& group) const;
2060
2061 const std::string& program() const
2062 {
2063 return m_program;
2064 }
2065
2066 private:
2067
2068 void
2069 add_one_option
2070 (
2071 const std::string& option,
2072 const std::shared_ptr<OptionDetails>& details
2073 );
2074
2075 String
2076 help_one_group(const std::string& group) const;
2077
2078 void
2079 generate_group_help
2080 (
2081 String& result,
2082 const std::vector<std::string>& groups
2083 ) const;
2084
2085 void
2086 generate_all_groups_help(String& result) const;
2087
2088 std::string m_program{};
2089 String m_help_string{};
2090 std::string m_custom_help{};
2091 std::string m_positional_help{};
2092 bool m_show_positional;
2093 bool m_allow_unrecognised;
2094 std::size_t m_width;
2095 bool m_tab_expansion;
2096
2097 std::shared_ptr<OptionMap> m_options;
2098 std::vector<std::string> m_positional{};
2099 std::unordered_set<std::string> m_positional_set{};
2100
2101 //mapping from groups to help options
2102 std::vector<std::string> m_group{};
2103 std::map<std::string, HelpGroupDetails> m_help{};
2104};
2105
2106class OptionAdder
2107{
2108 public:
2109
2110 OptionAdder(Options& options, std::string group)
2111 : m_options(options), m_group(std::move(group))
2112 {
2113 }
2114
2115 OptionAdder&
2116 operator()
2117 (
2118 const std::string& opts,
2119 const std::string& desc,
2120 const std::shared_ptr<const Value>& value
2121 = ::cxxopts::value<bool>(),
2122 std::string arg_help = ""
2123 );
2124
2125 private:
2126 Options& m_options;
2127 std::string m_group;
2128};
2129
2130namespace {
2131constexpr std::size_t OPTION_LONGEST = 30;
2132constexpr std::size_t OPTION_DESC_GAP = 2;
2133
2134String
2135format_option
2136(
2137 const HelpOptionDetails& o
2138)
2139{
2140 const auto& s = o.s;
2141 const auto& l = first_or_empty(o.l);
2142
2143 String result = " ";
2144
2145 if (!s.empty())
2146 {
2147 result += "-" + toLocalString(s);
2148 if (!l.empty())
2149 {
2150 result += ",";
2151 }
2152 }
2153 else
2154 {
2155 result += " ";
2156 }
2157
2158 if (!l.empty())
2159 {
2160 result += " --" + toLocalString(l);
2161 }
2162
2163 auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
2164
2165 if (!o.is_boolean)
2166 {
2167 if (o.has_implicit)
2168 {
2169 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
2170 }
2171 else
2172 {
2173 result += " " + arg;
2174 }
2175 }
2176
2177 return result;
2178}
2179
2180String
2181format_description
2182(
2183 const HelpOptionDetails& o,
2184 std::size_t start,
2185 std::size_t allowed,
2186 bool tab_expansion
2187)
2188{
2189 auto desc = o.desc;
2190
2191 if (o.has_default && (!o.is_boolean || o.default_value != "false"))
2192 {
2193 if(!o.default_value.empty())
2194 {
2195 desc += toLocalString(" (default: " + o.default_value + ")");
2196 }
2197 else
2198 {
2199 desc += toLocalString(" (default: \"\")");
2200 }
2201 }
2202
2203 String result;
2204
2205 if (tab_expansion)
2206 {
2207 String desc2;
2208 auto size = std::size_t{ 0 };
2209 for (auto c = std::begin(desc); c != std::end(desc); ++c)
2210 {
2211 if (*c == '\n')
2212 {
2213 desc2 += *c;
2214 size = 0;
2215 }
2216 else if (*c == '\t')
2217 {
2218 auto skip = 8 - size % 8;
2219 stringAppend(desc2, skip, ' ');
2220 size += skip;
2221 }
2222 else
2223 {
2224 desc2 += *c;
2225 ++size;
2226 }
2227 }
2228 desc = desc2;
2229 }
2230
2231 desc += " ";
2232
2233 auto current = std::begin(desc);
2234 auto previous = current;
2235 auto startLine = current;
2236 auto lastSpace = current;
2237
2238 auto size = std::size_t{};
2239
2240 bool appendNewLine;
2241 bool onlyWhiteSpace = true;
2242
2243 while (current != std::end(desc))
2244 {
2245 appendNewLine = false;
2246 if (*previous == ' ' || *previous == '\t')
2247 {
2248 lastSpace = current;
2249 }
2250 if (*current != ' ' && *current != '\t')
2251 {
2252 onlyWhiteSpace = false;
2253 }
2254
2255 while (*current == '\n')
2256 {
2257 previous = current;
2258 ++current;
2259 appendNewLine = true;
2260 }
2261
2262 if (!appendNewLine && size >= allowed)
2263 {
2264 if (lastSpace != startLine)
2265 {
2266 current = lastSpace;
2267 previous = current;
2268 }
2269 appendNewLine = true;
2270 }
2271
2272 if (appendNewLine)
2273 {
2274 stringAppend(result, startLine, current);
2275 startLine = current;
2276 lastSpace = current;
2277
2278 if (*previous != '\n')
2279 {
2280 stringAppend(result, "\n");
2281 }
2282
2283 stringAppend(result, start, ' ');
2284
2285 if (*previous != '\n')
2286 {
2287 stringAppend(result, lastSpace, current);
2288 }
2289
2290 onlyWhiteSpace = true;
2291 size = 0;
2292 }
2293
2294 previous = current;
2295 ++current;
2296 ++size;
2297 }
2298
2299 //append whatever is left but ignore whitespace
2300 if (!onlyWhiteSpace)
2301 {
2302 stringAppend(result, startLine, previous);
2303 }
2304
2305 return result;
2306}
2307
2308} // namespace
2309
2310inline
2311void
2312Options::add_options
2313(
2314 const std::string &group,
2315 std::initializer_list<Option> options
2316)
2317{
2318 OptionAdder option_adder(*this, group);
2319 for (const auto &option: options)
2320 {
2321 option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
2322 }
2323}
2324
2325inline
2327Options::add_options(std::string group)
2328{
2329 return OptionAdder(*this, std::move(group));
2330}
2331
2332inline
2334OptionAdder::operator()
2335(
2336 const std::string& opts,
2337 const std::string& desc,
2338 const std::shared_ptr<const Value>& value,
2339 std::string arg_help
2340)
2341{
2342 OptionNames option_names = values::parser_tool::split_option_names(opts);
2343 // Note: All names will be non-empty; but we must separate the short
2344 // (length-1) and longer names
2345 std::string short_name {""};
2346 auto first_short_name_iter =
2347 std::partition(option_names.begin(), option_names.end(),
2348 [&](const std::string& name) { return name.length() > 1; }
2349 );
2350 auto num_length_1_names = (option_names.end() - first_short_name_iter);
2351 switch(num_length_1_names) {
2352 case 1:
2353 short_name = *first_short_name_iter;
2354 option_names.erase(first_short_name_iter);
2355 CXXOPTS_FALLTHROUGH;
2356 case 0:
2357 break;
2358 default:
2359 throw_or_mimic<exceptions::invalid_option_format>(opts);
2360 };
2361
2362 m_options.add_option
2363 (
2364 m_group,
2365 short_name,
2366 option_names,
2367 desc,
2368 value,
2369 std::move(arg_help)
2370 );
2371
2372 return *this;
2373}
2374
2375inline
2376void
2377OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
2378{
2379 // TODO: remove the duplicate code here
2380 auto& store = m_parsed[details->hash()];
2381 store.parse_default(details);
2382 m_defaults.emplace_back(details->essential_name(), details->value().get_default_value());
2383}
2384
2385inline
2386void
2387OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details)
2388{
2389 auto& store = m_parsed[details->hash()];
2390 store.parse_no_value(details);
2391}
2392
2393inline
2394void
2395OptionParser::parse_option
2396(
2397 const std::shared_ptr<OptionDetails>& value,
2398 const std::string& /*name*/,
2399 const std::string& arg
2400)
2401{
2402 auto hash = value->hash();
2403 auto& result = m_parsed[hash];
2404 result.parse(value, arg);
2405
2406 m_sequential.emplace_back(value->essential_name(), arg);
2407}
2408
2409inline
2410void
2411OptionParser::checked_parse_arg
2412(
2413 int argc,
2414 const char* const* argv,
2415 int& current,
2416 const std::shared_ptr<OptionDetails>& value,
2417 const std::string& name
2418)
2419{
2420 if (current + 1 >= argc)
2421 {
2422 if (value->value().has_implicit())
2423 {
2424 parse_option(value, name, value->value().get_implicit_value());
2425 }
2426 else
2427 {
2428 throw_or_mimic<exceptions::missing_argument>(name);
2429 }
2430 }
2431 else
2432 {
2433 if (value->value().has_implicit())
2434 {
2435 parse_option(value, name, value->value().get_implicit_value());
2436 }
2437 else
2438 {
2439 parse_option(value, name, argv[current + 1]);
2440 ++current;
2441 }
2442 }
2443}
2444
2445inline
2446void
2447OptionParser::add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg)
2448{
2449 auto hash = value->hash();
2450 auto& result = m_parsed[hash];
2451 result.add(value, arg);
2452
2453 m_sequential.emplace_back(value->essential_name(), arg);
2454}
2455
2456inline
2457bool
2458OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
2459{
2460 while (next != m_positional.end())
2461 {
2462 auto iter = m_options.find(*next);
2463 if (iter != m_options.end())
2464 {
2465 if (!iter->second->value().is_container())
2466 {
2467 auto& result = m_parsed[iter->second->hash()];
2468 if (result.count() == 0)
2469 {
2470 add_to_option(iter->second, a);
2471 ++next;
2472 return true;
2473 }
2474 ++next;
2475 continue;
2476 }
2477 add_to_option(iter->second, a);
2478 return true;
2479 }
2480 throw_or_mimic<exceptions::no_such_option>(*next);
2481 }
2482
2483 return false;
2484}
2485
2486inline
2487void
2488Options::parse_positional(std::string option)
2489{
2490 parse_positional(std::vector<std::string>{std::move(option)});
2491}
2492
2493inline
2494void
2495Options::parse_positional(std::vector<std::string> options)
2496{
2497 m_positional = std::move(options);
2498
2499 m_positional_set.insert(m_positional.begin(), m_positional.end());
2500}
2501
2502inline
2503void
2504Options::parse_positional(std::initializer_list<std::string> options)
2505{
2506 parse_positional(std::vector<std::string>(options));
2507}
2508
2509inline
2511Options::parse(int argc, const char* const* argv)
2512{
2513 OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
2514
2515 return parser.parse(argc, argv);
2516}
2517
2518inline ParseResult
2519OptionParser::parse(int argc, const char* const* argv)
2520{
2521 int current = 1;
2522 bool consume_remaining = false;
2523 auto next_positional = m_positional.begin();
2524
2525 std::vector<std::string> unmatched;
2526
2527 while (current != argc)
2528 {
2529 if (strcmp(argv[current], "--") == 0)
2530 {
2531 consume_remaining = true;
2532 ++current;
2533 break;
2534 }
2535 bool matched = false;
2536 values::parser_tool::ArguDesc argu_desc =
2537 values::parser_tool::ParseArgument(argv[current], matched);
2538
2539 if (!matched)
2540 {
2541 //not a flag
2542
2543 // but if it starts with a `-`, then it's an error
2544 if (argv[current][0] == '-' && argv[current][1] != '\0') {
2545 if (!m_allow_unrecognised) {
2546 throw_or_mimic<exceptions::invalid_option_syntax>(argv[current]);
2547 }
2548 }
2549
2550 //if true is returned here then it was consumed, otherwise it is
2551 //ignored
2552 if (consume_positional(argv[current], next_positional))
2553 {
2554 }
2555 else
2556 {
2557 unmatched.emplace_back(argv[current]);
2558 }
2559 //if we return from here then it was parsed successfully, so continue
2560 }
2561 else
2562 {
2563 //short or long option?
2564 if (argu_desc.grouping)
2565 {
2566 const std::string& s = argu_desc.arg_name;
2567
2568 for (std::size_t i = 0; i != s.size(); ++i)
2569 {
2570 std::string name(1, s[i]);
2571 auto iter = m_options.find(name);
2572
2573 if (iter == m_options.end())
2574 {
2575 if (m_allow_unrecognised)
2576 {
2577 unmatched.push_back(std::string("-") + s[i]);
2578 continue;
2579 }
2580 //error
2581 throw_or_mimic<exceptions::no_such_option>(name);
2582 }
2583
2584 auto value = iter->second;
2585
2586 if (i + 1 == s.size())
2587 {
2588 //it must be the last argument
2589 checked_parse_arg(argc, argv, current, value, name);
2590 }
2591 else if (value->value().has_implicit())
2592 {
2593 parse_option(value, name, value->value().get_implicit_value());
2594 }
2595 else if (i + 1 < s.size())
2596 {
2597 std::string arg_value = s.substr(i + 1);
2598 parse_option(value, name, arg_value);
2599 break;
2600 }
2601 else
2602 {
2603 //error
2604 throw_or_mimic<exceptions::option_requires_argument>(name);
2605 }
2606 }
2607 }
2608 else if (argu_desc.arg_name.length() != 0)
2609 {
2610 const std::string& name = argu_desc.arg_name;
2611
2612 auto iter = m_options.find(name);
2613
2614 if (iter == m_options.end())
2615 {
2616 if (m_allow_unrecognised)
2617 {
2618 // keep unrecognised options in argument list, skip to next argument
2619 unmatched.emplace_back(argv[current]);
2620 ++current;
2621 continue;
2622 }
2623 //error
2624 throw_or_mimic<exceptions::no_such_option>(name);
2625 }
2626
2627 auto opt = iter->second;
2628
2629 //equals provided for long option?
2630 if (argu_desc.set_value)
2631 {
2632 //parse the option given
2633
2634 parse_option(opt, name, argu_desc.value);
2635 }
2636 else
2637 {
2638 //parse the next argument
2639 checked_parse_arg(argc, argv, current, opt, name);
2640 }
2641 }
2642
2643 }
2644
2645 ++current;
2646 }
2647
2648 for (auto& opt : m_options)
2649 {
2650 auto& detail = opt.second;
2651 const auto& value = detail->value();
2652
2653 auto& store = m_parsed[detail->hash()];
2654
2655 if (value.has_default()) {
2656 if (!store.count() && !store.has_default()) {
2657 parse_default(detail);
2658 }
2659 }
2660 else {
2661 parse_no_value(detail);
2662 }
2663 }
2664
2665 if (consume_remaining)
2666 {
2667 while (current < argc)
2668 {
2669 if (!consume_positional(argv[current], next_positional)) {
2670 break;
2671 }
2672 ++current;
2673 }
2674
2675 //adjust argv for any that couldn't be swallowed
2676 while (current != argc) {
2677 unmatched.emplace_back(argv[current]);
2678 ++current;
2679 }
2680 }
2681
2682 finalise_aliases();
2683
2684 ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(m_defaults), std::move(unmatched));
2685 return parsed;
2686}
2687
2688inline
2689void
2690OptionParser::finalise_aliases()
2691{
2692 for (auto& option: m_options)
2693 {
2694 auto& detail = *option.second;
2695 auto hash = detail.hash();
2696 m_keys[detail.short_name()] = hash;
2697 for(const auto& long_name : detail.long_names()) {
2698 m_keys[long_name] = hash;
2699 }
2700
2701 m_parsed.emplace(hash, OptionValue());
2702 }
2703}
2704
2705inline
2706void
2707Options::add_option
2708(
2709 const std::string& group,
2710 const Option& option
2711)
2712{
2713 add_options(group, {option});
2714}
2715
2716inline
2717void
2718Options::add_option
2719(
2720 const std::string& group,
2721 const std::string& s,
2722 const OptionNames& l,
2723 std::string desc,
2724 const std::shared_ptr<const Value>& value,
2725 std::string arg_help
2726)
2727{
2728 auto stringDesc = toLocalString(std::move(desc));
2729 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
2730
2731 if (!s.empty())
2732 {
2733 add_one_option(s, option);
2734 }
2735
2736 for(const auto& long_name : l) {
2737 add_one_option(long_name, option);
2738 }
2739
2740 //add the help details
2741
2742 if (m_help.find(group) == m_help.end())
2743 {
2744 m_group.push_back(group);
2745 }
2746
2747 auto& options = m_help[group];
2748
2749 options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
2750 value->has_default(), value->get_default_value(),
2751 value->has_implicit(), value->get_implicit_value(),
2752 std::move(arg_help),
2753 value->is_container(),
2754 value->is_boolean()});
2755}
2756
2757inline
2758void
2759Options::add_one_option
2760(
2761 const std::string& option,
2762 const std::shared_ptr<OptionDetails>& details
2763)
2764{
2765 auto in = m_options->emplace(option, details);
2766
2767 if (!in.second)
2768 {
2769 throw_or_mimic<exceptions::option_already_exists>(option);
2770 }
2771}
2772
2773inline
2774String
2775Options::help_one_group(const std::string& g) const
2776{
2777 using OptionHelp = std::vector<std::pair<String, String>>;
2778
2779 auto group = m_help.find(g);
2780 if (group == m_help.end())
2781 {
2782 return "";
2783 }
2784
2785 OptionHelp format;
2786
2787 std::size_t longest = 0;
2788
2789 String result;
2790
2791 if (!g.empty())
2792 {
2793 result += toLocalString(" " + g + " options:\n");
2794 }
2795
2796 for (const auto& o : group->second.options)
2797 {
2798 if (o.l.size() &&
2799 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2800 !m_show_positional)
2801 {
2802 continue;
2803 }
2804
2805 auto s = format_option(o);
2806 longest = (std::max)(longest, stringLength(s));
2807 format.push_back(std::make_pair(s, String()));
2808 }
2809 longest = (std::min)(longest, OPTION_LONGEST);
2810
2811 //widest allowed description -- min 10 chars for helptext/line
2812 std::size_t allowed = 10;
2813 if (m_width > allowed + longest + OPTION_DESC_GAP)
2814 {
2815 allowed = m_width - longest - OPTION_DESC_GAP;
2816 }
2817
2818 auto fiter = format.begin();
2819 for (const auto& o : group->second.options)
2820 {
2821 if (o.l.size() &&
2822 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2823 !m_show_positional)
2824 {
2825 continue;
2826 }
2827
2828 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
2829
2830 result += fiter->first;
2831 if (stringLength(fiter->first) > longest)
2832 {
2833 result += '\n';
2834 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2835 }
2836 else
2837 {
2838 result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2839 stringLength(fiter->first),
2840 ' '));
2841 }
2842 result += d;
2843 result += '\n';
2844
2845 ++fiter;
2846 }
2847
2848 return result;
2849}
2850
2851inline
2852void
2853Options::generate_group_help
2854(
2855 String& result,
2856 const std::vector<std::string>& print_groups
2857) const
2858{
2859 for (std::size_t i = 0; i != print_groups.size(); ++i)
2860 {
2861 const String& group_help_text = help_one_group(print_groups[i]);
2862 if (empty(group_help_text))
2863 {
2864 continue;
2865 }
2866 result += group_help_text;
2867 if (i < print_groups.size() - 1)
2868 {
2869 result += '\n';
2870 }
2871 }
2872}
2873
2874inline
2875void
2876Options::generate_all_groups_help(String& result) const
2877{
2878 generate_group_help(result, m_group);
2879}
2880
2881inline
2882std::string
2883Options::help(const std::vector<std::string>& help_groups, bool print_usage) const
2884{
2885 String result = m_help_string;
2886 if(print_usage)
2887 {
2888 result+= "\nUsage:\n " + toLocalString(m_program);
2889 }
2890
2891 if (!m_custom_help.empty())
2892 {
2893 result += " " + toLocalString(m_custom_help);
2894 }
2895
2896 if (!m_positional.empty() && !m_positional_help.empty()) {
2897 result += " " + toLocalString(m_positional_help);
2898 }
2899
2900 result += "\n\n";
2901
2902 if (help_groups.empty())
2903 {
2904 generate_all_groups_help(result);
2905 }
2906 else
2907 {
2908 generate_group_help(result, help_groups);
2909 }
2910
2911 return toUTF8String(result);
2912}
2913
2914inline
2915std::vector<std::string>
2916Options::groups() const
2917{
2918 return m_group;
2919}
2920
2921inline
2922const HelpGroupDetails&
2923Options::group_help(const std::string& group) const
2924{
2925 return m_help.at(group);
2926}
2927
2928} // namespace cxxopts
2929
2930#endif //CXXOPTS_HPP_INCLUDED
Definition cxxopts.hpp:1589
Definition cxxopts.hpp:2107
Definition cxxopts.hpp:1478
Definition cxxopts.hpp:1931
Definition cxxopts.hpp:1629
Definition cxxopts.hpp:370
Definition cxxopts.hpp:1289
@ store
store tags as binary type
Definition json.hpp:9884
@ string
string value
Definition json.hpp:3008
std::size_t hash(const BasicJsonType &j)
hash a JSON value
Definition json.hpp:6386
Definition cxxopts.hpp:1471
Definition cxxopts.hpp:1457
Definition cxxopts.hpp:1847
Definition cxxopts.hpp:887
Definition cxxopts.hpp:602
Definition cxxopts.hpp:1140