iVS3D v2.0.0
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
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
459{
460 public:
461 explicit option_already_exists(const std::string& option)
462 : specification("Option " + LQUOTE + option + RQUOTE + " already exists")
463 {
464 }
465};
466
468{
469 public:
470 explicit invalid_option_format(const std::string& format)
471 : specification("Invalid option format " + LQUOTE + format + RQUOTE)
472 {
473 }
474};
475
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
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
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
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
517{
518 public:
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
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
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
555{
556 public:
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> void add_value(const std::string& text, std::vector<T>& value);
1104
1105template <typename T>
1106void
1107parse_value(const std::string& text, std::vector<T>& value)
1108{
1109 if (text.empty()) {
1110 return;
1111 }
1112 std::stringstream in(text);
1113 std::string token;
1114 while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
1115 add_value(token, value);
1116 }
1117}
1118
1119template <typename T>
1120void
1121add_value(const std::string& text, T& value)
1122{
1123 parse_value(text, value);
1124}
1125
1126template <typename T>
1127void
1128add_value(const std::string& text, std::vector<T>& value)
1129{
1130 T v;
1131 add_value(text, v);
1132 value.emplace_back(std::move(v));
1133}
1134
1135template <typename T>
1137{
1138 static constexpr bool value = false;
1139};
1140
1141template <typename T>
1142struct type_is_container<std::vector<T>>
1143{
1144 static constexpr bool value = true;
1145};
1146
1147template <typename T>
1148class abstract_value : public Value
1149{
1150 using Self = abstract_value<T>;
1151
1152 public:
1154 : m_result(std::make_shared<T>())
1155 , m_store(m_result.get())
1156 {
1157 }
1158
1159 explicit abstract_value(T* t)
1160 : m_store(t)
1161 {
1162 }
1163
1164 ~abstract_value() override = default;
1165
1166 abstract_value& operator=(const abstract_value&) = default;
1167
1168 abstract_value(const abstract_value& rhs)
1169 {
1170 if (rhs.m_result)
1171 {
1172 m_result = std::make_shared<T>();
1173 m_store = m_result.get();
1174 }
1175 else
1176 {
1177 m_store = rhs.m_store;
1178 }
1179
1180 m_default = rhs.m_default;
1181 m_implicit = rhs.m_implicit;
1182 m_default_value = rhs.m_default_value;
1183 m_implicit_value = rhs.m_implicit_value;
1184 }
1185
1186 void
1187 add(const std::string& text) const override
1188 {
1189 add_value(text, *m_store);
1190 }
1191
1192 void
1193 parse(const std::string& text) const override
1194 {
1195 parse_value(text, *m_store);
1196 }
1197
1198 bool
1199 is_container() const override
1200 {
1202 }
1203
1204 void
1205 parse() const override
1206 {
1207 parse_value(m_default_value, *m_store);
1208 }
1209
1210 bool
1211 has_default() const override
1212 {
1213 return m_default;
1214 }
1215
1216 bool
1217 has_implicit() const override
1218 {
1219 return m_implicit;
1220 }
1221
1222 std::shared_ptr<Value>
1223 default_value(const std::string& value) override
1224 {
1225 m_default = true;
1226 m_default_value = value;
1227 return shared_from_this();
1228 }
1229
1230 std::shared_ptr<Value>
1231 implicit_value(const std::string& value) override
1232 {
1233 m_implicit = true;
1234 m_implicit_value = value;
1235 return shared_from_this();
1236 }
1237
1238 std::shared_ptr<Value>
1239 no_implicit_value() override
1240 {
1241 m_implicit = false;
1242 return shared_from_this();
1243 }
1244
1245 std::string
1246 get_default_value() const override
1247 {
1248 return m_default_value;
1249 }
1250
1251 std::string
1252 get_implicit_value() const override
1253 {
1254 return m_implicit_value;
1255 }
1256
1257 bool
1258 is_boolean() const override
1259 {
1260 return std::is_same<T, bool>::value;
1261 }
1262
1263 const T&
1264 get() const
1265 {
1266 if (m_store == nullptr)
1267 {
1268 return *m_result;
1269 }
1270 return *m_store;
1271 }
1272
1273 protected:
1274 std::shared_ptr<T> m_result{};
1275 T* m_store{};
1276
1277 bool m_default = false;
1278 bool m_implicit = false;
1279
1280 std::string m_default_value{};
1281 std::string m_implicit_value{};
1282};
1283
1284template <typename T>
1286{
1287 public:
1288 using abstract_value<T>::abstract_value;
1289
1290 CXXOPTS_NODISCARD
1291 std::shared_ptr<Value>
1292 clone() const override
1293 {
1294 return std::make_shared<standard_value<T>>(*this);
1295 }
1296};
1297
1298template <>
1299class standard_value<bool> : public abstract_value<bool>
1300{
1301 public:
1302 ~standard_value() override = default;
1303
1305 {
1306 set_default_and_implicit();
1307 }
1308
1309 explicit standard_value(bool* b)
1310 : abstract_value(b)
1311 {
1312 m_implicit = true;
1313 m_implicit_value = "true";
1314 }
1315
1316 std::shared_ptr<Value>
1317 clone() const override
1318 {
1319 return std::make_shared<standard_value<bool>>(*this);
1320 }
1321
1322 private:
1323
1324 void
1325 set_default_and_implicit()
1326 {
1327 m_default = true;
1328 m_default_value = "false";
1329 m_implicit = true;
1330 m_implicit_value = "true";
1331 }
1332};
1333
1334} // namespace values
1335
1336template <typename T>
1337std::shared_ptr<Value>
1338value()
1339{
1340 return std::make_shared<values::standard_value<T>>();
1341}
1342
1343template <typename T>
1344std::shared_ptr<Value>
1345value(T& t)
1346{
1347 return std::make_shared<values::standard_value<T>>(&t);
1348}
1349
1350class OptionAdder;
1351
1352CXXOPTS_NODISCARD
1353inline
1354const std::string&
1355first_or_empty(const OptionNames& long_names)
1356{
1357 static const std::string empty{""};
1358 return long_names.empty() ? empty : long_names.front();
1359}
1360
1362{
1363 public:
1365 (
1366 std::string short_,
1367 OptionNames long_,
1368 String desc,
1369 std::shared_ptr<const Value> val
1370 )
1371 : m_short(std::move(short_))
1372 , m_long(std::move(long_))
1373 , m_desc(std::move(desc))
1374 , m_value(std::move(val))
1375 , m_count(0)
1376 {
1377 m_hash = std::hash<std::string>{}(first_long_name() + m_short);
1378 }
1379
1380 OptionDetails(const OptionDetails& rhs)
1381 : m_desc(rhs.m_desc)
1382 , m_value(rhs.m_value->clone())
1383 , m_count(rhs.m_count)
1384 {
1385 }
1386
1387 OptionDetails(OptionDetails&& rhs) = default;
1388
1389 CXXOPTS_NODISCARD
1390 const String&
1391 description() const
1392 {
1393 return m_desc;
1394 }
1395
1396 CXXOPTS_NODISCARD
1397 const Value&
1398 value() const {
1399 return *m_value;
1400 }
1401
1402 CXXOPTS_NODISCARD
1403 std::shared_ptr<Value>
1404 make_storage() const
1405 {
1406 return m_value->clone();
1407 }
1408
1409 CXXOPTS_NODISCARD
1410 const std::string&
1411 short_name() const
1412 {
1413 return m_short;
1414 }
1415
1416 CXXOPTS_NODISCARD
1417 const std::string&
1418 first_long_name() const
1419 {
1420 return first_or_empty(m_long);
1421 }
1422
1423 CXXOPTS_NODISCARD
1424 const std::string&
1425 essential_name() const
1426 {
1427 return m_long.empty() ? m_short : m_long.front();
1428 }
1429
1430 CXXOPTS_NODISCARD
1431 const OptionNames &
1432 long_names() const
1433 {
1434 return m_long;
1435 }
1436
1437 std::size_t
1438 hash() const
1439 {
1440 return m_hash;
1441 }
1442
1443 private:
1444 std::string m_short{};
1445 OptionNames m_long{};
1446 String m_desc{};
1447 std::shared_ptr<const Value> m_value{};
1448 int m_count;
1449
1450 std::size_t m_hash{};
1451};
1452
1454{
1455 std::string s;
1456 OptionNames l;
1457 String desc;
1458 bool has_default;
1459 std::string default_value;
1460 bool has_implicit;
1461 std::string implicit_value;
1462 std::string arg_help;
1463 bool is_container;
1464 bool is_boolean;
1465};
1466
1468{
1469 std::string name{};
1470 std::string description{};
1471 std::vector<HelpOptionDetails> options{};
1472};
1473
1475{
1476 public:
1477 void
1478 add
1479 (
1480 const std::shared_ptr<const OptionDetails>& details,
1481 const std::string& text
1482 )
1483 {
1484 ensure_value(details);
1485 ++m_count;
1486 m_value->add(text);
1487 m_long_names = &details->long_names();
1488 }
1489
1490 void
1491 parse
1492 (
1493 const std::shared_ptr<const OptionDetails>& details,
1494 const std::string& text
1495 )
1496 {
1497 ensure_value(details);
1498 ++m_count;
1499 m_value->parse(text);
1500 m_long_names = &details->long_names();
1501 }
1502
1503 void
1504 parse_default(const std::shared_ptr<const OptionDetails>& details)
1505 {
1506 ensure_value(details);
1507 m_default = true;
1508 m_long_names = &details->long_names();
1509 m_value->parse();
1510 }
1511
1512 void
1513 parse_no_value(const std::shared_ptr<const OptionDetails>& details)
1514 {
1515 m_long_names = &details->long_names();
1516 }
1517
1518#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1519CXXOPTS_DIAGNOSTIC_PUSH
1520CXXOPTS_IGNORE_WARNING("-Wnull-dereference")
1521#endif
1522
1523 CXXOPTS_NODISCARD
1524 std::size_t
1525 count() const noexcept
1526 {
1527 return m_count;
1528 }
1529
1530#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1531CXXOPTS_DIAGNOSTIC_POP
1532#endif
1533
1534 // TODO: maybe default options should count towards the number of arguments
1535 CXXOPTS_NODISCARD
1536 bool
1537 has_default() const noexcept
1538 {
1539 return m_default;
1540 }
1541
1542 template <typename T>
1543 const T&
1544 as() const
1545 {
1546 if (m_value == nullptr) {
1547 throw_or_mimic<exceptions::option_has_no_value>(
1548 m_long_names == nullptr ? "" : first_or_empty(*m_long_names));
1549 }
1550
1551 return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
1552 }
1553
1554#ifdef CXXOPTS_HAS_OPTIONAL
1555 template <typename T>
1556 std::optional<T>
1557 as_optional() const
1558 {
1559 if (m_value == nullptr) {
1560 return std::nullopt;
1561 }
1562 return as<T>();
1563 }
1564#endif
1565
1566 private:
1567 void
1568 ensure_value(const std::shared_ptr<const OptionDetails>& details)
1569 {
1570 if (m_value == nullptr)
1571 {
1572 m_value = details->make_storage();
1573 }
1574 }
1575
1576
1577 const OptionNames * m_long_names = nullptr;
1578 // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
1579 // where the key has the string we point to.
1580 std::shared_ptr<Value> m_value{};
1581 std::size_t m_count = 0;
1582 bool m_default = false;
1583};
1584
1586{
1587 public:
1588 KeyValue(std::string key_, std::string value_) noexcept
1589 : m_key(std::move(key_))
1590 , m_value(std::move(value_))
1591 {
1592 }
1593
1594 CXXOPTS_NODISCARD
1595 const std::string&
1596 key() const
1597 {
1598 return m_key;
1599 }
1600
1601 CXXOPTS_NODISCARD
1602 const std::string&
1603 value() const
1604 {
1605 return m_value;
1606 }
1607
1608 template <typename T>
1609 T
1610 as() const
1611 {
1612 T result;
1613 values::parse_value(m_value, result);
1614 return result;
1615 }
1616
1617 private:
1618 std::string m_key;
1619 std::string m_value;
1620};
1621
1622using ParsedHashMap = std::unordered_map<std::size_t, OptionValue>;
1623using NameHashMap = std::unordered_map<std::string, std::size_t>;
1624
1626{
1627 public:
1629 {
1630 public:
1631 using iterator_category = std::forward_iterator_tag;
1632 using value_type = KeyValue;
1633 using difference_type = void;
1634 using pointer = const KeyValue*;
1635 using reference = const KeyValue&;
1636
1637 Iterator() = default;
1638 Iterator(const Iterator&) = default;
1639
1640// GCC complains about m_iter not being initialised in the member
1641// initializer list
1642CXXOPTS_DIAGNOSTIC_PUSH
1643CXXOPTS_IGNORE_WARNING("-Weffc++")
1644 Iterator(const ParseResult *pr, bool end=false)
1645 : m_pr(pr)
1646 {
1647 if (end)
1648 {
1649 m_sequential = false;
1650 m_iter = m_pr->m_defaults.end();
1651 }
1652 else
1653 {
1654 m_sequential = true;
1655 m_iter = m_pr->m_sequential.begin();
1656
1657 if (m_iter == m_pr->m_sequential.end())
1658 {
1659 m_sequential = false;
1660 m_iter = m_pr->m_defaults.begin();
1661 }
1662 }
1663 }
1664CXXOPTS_DIAGNOSTIC_POP
1665
1666 Iterator& operator++()
1667 {
1668 ++m_iter;
1669 if(m_sequential && m_iter == m_pr->m_sequential.end())
1670 {
1671 m_sequential = false;
1672 m_iter = m_pr->m_defaults.begin();
1673 return *this;
1674 }
1675 return *this;
1676 }
1677
1678 Iterator operator++(int)
1679 {
1680 Iterator retval = *this;
1681 ++(*this);
1682 return retval;
1683 }
1684
1685 bool operator==(const Iterator& other) const
1686 {
1687 return (m_sequential == other.m_sequential) && (m_iter == other.m_iter);
1688 }
1689
1690 bool operator!=(const Iterator& other) const
1691 {
1692 return !(*this == other);
1693 }
1694
1695 const KeyValue& operator*()
1696 {
1697 return *m_iter;
1698 }
1699
1700 const KeyValue* operator->()
1701 {
1702 return m_iter.operator->();
1703 }
1704
1705 private:
1706 const ParseResult* m_pr;
1707 std::vector<KeyValue>::const_iterator m_iter;
1708 bool m_sequential = true;
1709 };
1710
1711 ParseResult() = default;
1712 ParseResult(const ParseResult&) = default;
1713
1714 ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential,
1715 std::vector<KeyValue> default_opts, std::vector<std::string>&& unmatched_args)
1716 : m_keys(std::move(keys))
1717 , m_values(std::move(values))
1718 , m_sequential(std::move(sequential))
1719 , m_defaults(std::move(default_opts))
1720 , m_unmatched(std::move(unmatched_args))
1721 {
1722 }
1723
1724 ParseResult& operator=(ParseResult&&) = default;
1725 ParseResult& operator=(const ParseResult&) = default;
1726
1727 Iterator
1728 begin() const
1729 {
1730 return Iterator(this);
1731 }
1732
1733 Iterator
1734 end() const
1735 {
1736 return Iterator(this, true);
1737 }
1738
1739 std::size_t
1740 count(const std::string& o) const
1741 {
1742 auto iter = m_keys.find(o);
1743 if (iter == m_keys.end())
1744 {
1745 return 0;
1746 }
1747
1748 auto viter = m_values.find(iter->second);
1749
1750 if (viter == m_values.end())
1751 {
1752 return 0;
1753 }
1754
1755 return viter->second.count();
1756 }
1757
1758 bool
1759 contains(const std::string& o) const
1760 {
1761 return static_cast<bool>(count(o));
1762 }
1763
1764 const OptionValue&
1765 operator[](const std::string& option) const
1766 {
1767 auto iter = m_keys.find(option);
1768
1769 if (iter == m_keys.end())
1770 {
1771 throw_or_mimic<exceptions::requested_option_not_present>(option);
1772 }
1773
1774 auto viter = m_values.find(iter->second);
1775
1776 if (viter == m_values.end())
1777 {
1778 throw_or_mimic<exceptions::requested_option_not_present>(option);
1779 }
1780
1781 return viter->second;
1782 }
1783
1784#ifdef CXXOPTS_HAS_OPTIONAL
1785 template <typename T>
1786 std::optional<T>
1787 as_optional(const std::string& option) const
1788 {
1789 auto iter = m_keys.find(option);
1790 if (iter != m_keys.end())
1791 {
1792 auto viter = m_values.find(iter->second);
1793 if (viter != m_values.end())
1794 {
1795 return viter->second.as_optional<T>();
1796 }
1797 }
1798 return std::nullopt;
1799 }
1800#endif
1801
1802 const std::vector<KeyValue>&
1803 arguments() const
1804 {
1805 return m_sequential;
1806 }
1807
1808 const std::vector<std::string>&
1809 unmatched() const
1810 {
1811 return m_unmatched;
1812 }
1813
1814 const std::vector<KeyValue>&
1815 defaults() const
1816 {
1817 return m_defaults;
1818 }
1819
1820 const std::string
1821 arguments_string() const
1822 {
1823 std::string result;
1824 for(const auto& kv: m_sequential)
1825 {
1826 result += kv.key() + " = " + kv.value() + "\n";
1827 }
1828 for(const auto& kv: m_defaults)
1829 {
1830 result += kv.key() + " = " + kv.value() + " " + "(default)" + "\n";
1831 }
1832 return result;
1833 }
1834
1835 private:
1836 NameHashMap m_keys{};
1837 ParsedHashMap m_values{};
1838 std::vector<KeyValue> m_sequential{};
1839 std::vector<KeyValue> m_defaults{};
1840 std::vector<std::string> m_unmatched{};
1841};
1842
1844{
1845 Option
1846 (
1847 std::string opts,
1848 std::string desc,
1849 std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
1850 std::string arg_help = ""
1851 )
1852 : opts_(std::move(opts))
1853 , desc_(std::move(desc))
1854 , value_(std::move(value))
1855 , arg_help_(std::move(arg_help))
1856 {
1857 }
1858
1859 std::string opts_;
1860 std::string desc_;
1861 std::shared_ptr<const Value> value_;
1862 std::string arg_help_;
1863};
1864
1865using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1866using PositionalList = std::vector<std::string>;
1867using PositionalListIterator = PositionalList::const_iterator;
1868
1870{
1871 public:
1872 OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
1873 : m_options(options)
1874 , m_positional(positional)
1875 , m_allow_unrecognised(allow_unrecognised)
1876 {
1877 }
1878
1880 parse(int argc, const char* const* argv);
1881
1882 bool
1883 consume_positional(const std::string& a, PositionalListIterator& next);
1884
1885 void
1886 checked_parse_arg
1887 (
1888 int argc,
1889 const char* const* argv,
1890 int& current,
1891 const std::shared_ptr<OptionDetails>& value,
1892 const std::string& name
1893 );
1894
1895 void
1896 add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg);
1897
1898 void
1899 parse_option
1900 (
1901 const std::shared_ptr<OptionDetails>& value,
1902 const std::string& name,
1903 const std::string& arg = ""
1904 );
1905
1906 void
1907 parse_default(const std::shared_ptr<OptionDetails>& details);
1908
1909 void
1910 parse_no_value(const std::shared_ptr<OptionDetails>& details);
1911
1912 private:
1913
1914 void finalise_aliases();
1915
1916 const OptionMap& m_options;
1917 const PositionalList& m_positional;
1918
1919 std::vector<KeyValue> m_sequential{};
1920 std::vector<KeyValue> m_defaults{};
1921 bool m_allow_unrecognised;
1922
1923 ParsedHashMap m_parsed{};
1924 NameHashMap m_keys{};
1925};
1926
1928{
1929 public:
1930
1931 explicit Options(std::string program_name, std::string help_string = "")
1932 : m_program(std::move(program_name))
1933 , m_help_string(toLocalString(std::move(help_string)))
1934 , m_custom_help("[OPTION...]")
1935 , m_positional_help("positional parameters")
1936 , m_show_positional(false)
1937 , m_allow_unrecognised(false)
1938 , m_width(76)
1939 , m_tab_expansion(false)
1940 , m_options(std::make_shared<OptionMap>())
1941 {
1942 }
1943
1944 Options&
1945 positional_help(std::string help_text)
1946 {
1947 m_positional_help = std::move(help_text);
1948 return *this;
1949 }
1950
1951 Options&
1952 custom_help(std::string help_text)
1953 {
1954 m_custom_help = std::move(help_text);
1955 return *this;
1956 }
1957
1958 Options&
1959 show_positional_help()
1960 {
1961 m_show_positional = true;
1962 return *this;
1963 }
1964
1965 Options&
1966 allow_unrecognised_options()
1967 {
1968 m_allow_unrecognised = true;
1969 return *this;
1970 }
1971
1972 Options&
1973 set_width(std::size_t width)
1974 {
1975 m_width = width;
1976 return *this;
1977 }
1978
1979 Options&
1980 set_tab_expansion(bool expansion=true)
1981 {
1982 m_tab_expansion = expansion;
1983 return *this;
1984 }
1985
1987 parse(int argc, const char* const* argv);
1988
1990 add_options(std::string group = "");
1991
1992 void
1993 add_options
1994 (
1995 const std::string& group,
1996 std::initializer_list<Option> options
1997 );
1998
1999 void
2000 add_option
2001 (
2002 const std::string& group,
2003 const Option& option
2004 );
2005
2006 void
2007 add_option
2008 (
2009 const std::string& group,
2010 const std::string& s,
2011 const OptionNames& l,
2012 std::string desc,
2013 const std::shared_ptr<const Value>& value,
2014 std::string arg_help
2015 );
2016
2017 void
2018 add_option
2019 (
2020 const std::string& group,
2021 const std::string& short_name,
2022 const std::string& single_long_name,
2023 std::string desc,
2024 const std::shared_ptr<const Value>& value,
2025 std::string arg_help
2026 )
2027 {
2028 OptionNames long_names;
2029 long_names.emplace_back(single_long_name);
2030 add_option(group, short_name, long_names, desc, value, arg_help);
2031 }
2032
2033 //parse positional arguments into the given option
2034 void
2035 parse_positional(std::string option);
2036
2037 void
2038 parse_positional(std::vector<std::string> options);
2039
2040 void
2041 parse_positional(std::initializer_list<std::string> options);
2042
2043 template <typename Iterator>
2044 void
2045 parse_positional(Iterator begin, Iterator end) {
2046 parse_positional(std::vector<std::string>{begin, end});
2047 }
2048
2049 std::string
2050 help(const std::vector<std::string>& groups = {}, bool print_usage=true) const;
2051
2052 std::vector<std::string>
2053 groups() const;
2054
2055 const HelpGroupDetails&
2056 group_help(const std::string& group) const;
2057
2058 const std::string& program() const
2059 {
2060 return m_program;
2061 }
2062
2063 private:
2064
2065 void
2066 add_one_option
2067 (
2068 const std::string& option,
2069 const std::shared_ptr<OptionDetails>& details
2070 );
2071
2072 String
2073 help_one_group(const std::string& group) const;
2074
2075 void
2076 generate_group_help
2077 (
2078 String& result,
2079 const std::vector<std::string>& groups
2080 ) const;
2081
2082 void
2083 generate_all_groups_help(String& result) const;
2084
2085 std::string m_program{};
2086 String m_help_string{};
2087 std::string m_custom_help{};
2088 std::string m_positional_help{};
2089 bool m_show_positional;
2090 bool m_allow_unrecognised;
2091 std::size_t m_width;
2092 bool m_tab_expansion;
2093
2094 std::shared_ptr<OptionMap> m_options;
2095 std::vector<std::string> m_positional{};
2096 std::unordered_set<std::string> m_positional_set{};
2097
2098 //mapping from groups to help options
2099 std::vector<std::string> m_group{};
2100 std::map<std::string, HelpGroupDetails> m_help{};
2101};
2102
2104{
2105 public:
2106
2107 OptionAdder(Options& options, std::string group)
2108 : m_options(options), m_group(std::move(group))
2109 {
2110 }
2111
2113 operator()
2114 (
2115 const std::string& opts,
2116 const std::string& desc,
2117 const std::shared_ptr<const Value>& value
2118 = ::cxxopts::value<bool>(),
2119 std::string arg_help = ""
2120 );
2121
2122 private:
2123 Options& m_options;
2124 std::string m_group;
2125};
2126
2127namespace {
2128constexpr std::size_t OPTION_LONGEST = 30;
2129constexpr std::size_t OPTION_DESC_GAP = 2;
2130
2131String
2132format_option
2133(
2134 const HelpOptionDetails& o
2135)
2136{
2137 const auto& s = o.s;
2138 const auto& l = first_or_empty(o.l);
2139
2140 String result = " ";
2141
2142 if (!s.empty())
2143 {
2144 result += "-" + toLocalString(s);
2145 if (!l.empty())
2146 {
2147 result += ",";
2148 }
2149 }
2150 else
2151 {
2152 result += " ";
2153 }
2154
2155 if (!l.empty())
2156 {
2157 result += " --" + toLocalString(l);
2158 }
2159
2160 auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
2161
2162 if (!o.is_boolean)
2163 {
2164 if (o.has_implicit)
2165 {
2166 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
2167 }
2168 else
2169 {
2170 result += " " + arg;
2171 }
2172 }
2173
2174 return result;
2175}
2176
2177String
2178format_description
2179(
2180 const HelpOptionDetails& o,
2181 std::size_t start,
2182 std::size_t allowed,
2183 bool tab_expansion
2184)
2185{
2186 auto desc = o.desc;
2187
2188 if (o.has_default && (!o.is_boolean || o.default_value != "false"))
2189 {
2190 if(!o.default_value.empty())
2191 {
2192 desc += toLocalString(" (default: " + o.default_value + ")");
2193 }
2194 else
2195 {
2196 desc += toLocalString(" (default: \"\")");
2197 }
2198 }
2199
2200 String result;
2201
2202 if (tab_expansion)
2203 {
2204 String desc2;
2205 auto size = std::size_t{ 0 };
2206 for (auto c = std::begin(desc); c != std::end(desc); ++c)
2207 {
2208 if (*c == '\n')
2209 {
2210 desc2 += *c;
2211 size = 0;
2212 }
2213 else if (*c == '\t')
2214 {
2215 auto skip = 8 - size % 8;
2216 stringAppend(desc2, skip, ' ');
2217 size += skip;
2218 }
2219 else
2220 {
2221 desc2 += *c;
2222 ++size;
2223 }
2224 }
2225 desc = desc2;
2226 }
2227
2228 desc += " ";
2229
2230 auto current = std::begin(desc);
2231 auto previous = current;
2232 auto startLine = current;
2233 auto lastSpace = current;
2234
2235 auto size = std::size_t{};
2236
2237 bool appendNewLine;
2238 bool onlyWhiteSpace = true;
2239
2240 while (current != std::end(desc))
2241 {
2242 appendNewLine = false;
2243 if (*previous == ' ' || *previous == '\t')
2244 {
2245 lastSpace = current;
2246 }
2247 if (*current != ' ' && *current != '\t')
2248 {
2249 onlyWhiteSpace = false;
2250 }
2251
2252 while (*current == '\n')
2253 {
2254 previous = current;
2255 ++current;
2256 appendNewLine = true;
2257 }
2258
2259 if (!appendNewLine && size >= allowed)
2260 {
2261 if (lastSpace != startLine)
2262 {
2263 current = lastSpace;
2264 previous = current;
2265 }
2266 appendNewLine = true;
2267 }
2268
2269 if (appendNewLine)
2270 {
2271 stringAppend(result, startLine, current);
2272 startLine = current;
2273 lastSpace = current;
2274
2275 if (*previous != '\n')
2276 {
2277 stringAppend(result, "\n");
2278 }
2279
2280 stringAppend(result, start, ' ');
2281
2282 if (*previous != '\n')
2283 {
2284 stringAppend(result, lastSpace, current);
2285 }
2286
2287 onlyWhiteSpace = true;
2288 size = 0;
2289 }
2290
2291 previous = current;
2292 ++current;
2293 ++size;
2294 }
2295
2296 //append whatever is left but ignore whitespace
2297 if (!onlyWhiteSpace)
2298 {
2299 stringAppend(result, startLine, previous);
2300 }
2301
2302 return result;
2303}
2304
2305} // namespace
2306
2307inline
2308void
2309Options::add_options
2310(
2311 const std::string &group,
2312 std::initializer_list<Option> options
2313)
2314{
2315 OptionAdder option_adder(*this, group);
2316 for (const auto &option: options)
2317 {
2318 option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
2319 }
2320}
2321
2322inline
2323OptionAdder
2324Options::add_options(std::string group)
2325{
2326 return OptionAdder(*this, std::move(group));
2327}
2328
2329inline
2330OptionAdder&
2331OptionAdder::operator()
2332(
2333 const std::string& opts,
2334 const std::string& desc,
2335 const std::shared_ptr<const Value>& value,
2336 std::string arg_help
2337)
2338{
2339 OptionNames option_names = values::parser_tool::split_option_names(opts);
2340 // Note: All names will be non-empty; but we must separate the short
2341 // (length-1) and longer names
2342 std::string short_name {""};
2343 auto first_short_name_iter =
2344 std::partition(option_names.begin(), option_names.end(),
2345 [&](const std::string& name) { return name.length() > 1; }
2346 );
2347 auto num_length_1_names = (option_names.end() - first_short_name_iter);
2348 switch(num_length_1_names) {
2349 case 1:
2350 short_name = *first_short_name_iter;
2351 option_names.erase(first_short_name_iter);
2352 CXXOPTS_FALLTHROUGH;
2353 case 0:
2354 break;
2355 default:
2356 throw_or_mimic<exceptions::invalid_option_format>(opts);
2357 };
2358
2359 m_options.add_option
2360 (
2361 m_group,
2362 short_name,
2363 option_names,
2364 desc,
2365 value,
2366 std::move(arg_help)
2367 );
2368
2369 return *this;
2370}
2371
2372inline
2373void
2374OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
2375{
2376 // TODO: remove the duplicate code here
2377 auto& store = m_parsed[details->hash()];
2378 store.parse_default(details);
2379 m_defaults.emplace_back(details->essential_name(), details->value().get_default_value());
2380}
2381
2382inline
2383void
2384OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details)
2385{
2386 auto& store = m_parsed[details->hash()];
2387 store.parse_no_value(details);
2388}
2389
2390inline
2391void
2392OptionParser::parse_option
2393(
2394 const std::shared_ptr<OptionDetails>& value,
2395 const std::string& /*name*/,
2396 const std::string& arg
2397)
2398{
2399 auto hash = value->hash();
2400 auto& result = m_parsed[hash];
2401 result.parse(value, arg);
2402
2403 m_sequential.emplace_back(value->essential_name(), arg);
2404}
2405
2406inline
2407void
2408OptionParser::checked_parse_arg
2409(
2410 int argc,
2411 const char* const* argv,
2412 int& current,
2413 const std::shared_ptr<OptionDetails>& value,
2414 const std::string& name
2415)
2416{
2417 if (current + 1 >= argc)
2418 {
2419 if (value->value().has_implicit())
2420 {
2421 parse_option(value, name, value->value().get_implicit_value());
2422 }
2423 else
2424 {
2425 throw_or_mimic<exceptions::missing_argument>(name);
2426 }
2427 }
2428 else
2429 {
2430 if (value->value().has_implicit())
2431 {
2432 parse_option(value, name, value->value().get_implicit_value());
2433 }
2434 else
2435 {
2436 parse_option(value, name, argv[current + 1]);
2437 ++current;
2438 }
2439 }
2440}
2441
2442inline
2443void
2444OptionParser::add_to_option(const std::shared_ptr<OptionDetails>& value, const std::string& arg)
2445{
2446 auto hash = value->hash();
2447 auto& result = m_parsed[hash];
2448 result.add(value, arg);
2449
2450 m_sequential.emplace_back(value->essential_name(), arg);
2451}
2452
2453inline
2454bool
2455OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
2456{
2457 while (next != m_positional.end())
2458 {
2459 auto iter = m_options.find(*next);
2460 if (iter != m_options.end())
2461 {
2462 if (!iter->second->value().is_container())
2463 {
2464 auto& result = m_parsed[iter->second->hash()];
2465 if (result.count() == 0)
2466 {
2467 add_to_option(iter->second, a);
2468 ++next;
2469 return true;
2470 }
2471 ++next;
2472 continue;
2473 }
2474 add_to_option(iter->second, a);
2475 return true;
2476 }
2477 throw_or_mimic<exceptions::no_such_option>(*next);
2478 }
2479
2480 return false;
2481}
2482
2483inline
2484void
2485Options::parse_positional(std::string option)
2486{
2487 parse_positional(std::vector<std::string>{std::move(option)});
2488}
2489
2490inline
2491void
2492Options::parse_positional(std::vector<std::string> options)
2493{
2494 m_positional = std::move(options);
2495
2496 m_positional_set.insert(m_positional.begin(), m_positional.end());
2497}
2498
2499inline
2500void
2501Options::parse_positional(std::initializer_list<std::string> options)
2502{
2503 parse_positional(std::vector<std::string>(options));
2504}
2505
2506inline
2507ParseResult
2508Options::parse(int argc, const char* const* argv)
2509{
2510 OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
2511
2512 return parser.parse(argc, argv);
2513}
2514
2515inline ParseResult
2516OptionParser::parse(int argc, const char* const* argv)
2517{
2518 int current = 1;
2519 bool consume_remaining = false;
2520 auto next_positional = m_positional.begin();
2521
2522 std::vector<std::string> unmatched;
2523
2524 while (current != argc)
2525 {
2526 if (strcmp(argv[current], "--") == 0)
2527 {
2528 consume_remaining = true;
2529 ++current;
2530 break;
2531 }
2532 bool matched = false;
2533 values::parser_tool::ArguDesc argu_desc =
2534 values::parser_tool::ParseArgument(argv[current], matched);
2535
2536 if (!matched)
2537 {
2538 //not a flag
2539
2540 // but if it starts with a `-`, then it's an error
2541 if (argv[current][0] == '-' && argv[current][1] != '\0') {
2542 if (!m_allow_unrecognised) {
2543 throw_or_mimic<exceptions::invalid_option_syntax>(argv[current]);
2544 }
2545 }
2546
2547 //if true is returned here then it was consumed, otherwise it is
2548 //ignored
2549 if (consume_positional(argv[current], next_positional))
2550 {
2551 }
2552 else
2553 {
2554 unmatched.emplace_back(argv[current]);
2555 }
2556 //if we return from here then it was parsed successfully, so continue
2557 }
2558 else
2559 {
2560 //short or long option?
2561 if (argu_desc.grouping)
2562 {
2563 const std::string& s = argu_desc.arg_name;
2564
2565 for (std::size_t i = 0; i != s.size(); ++i)
2566 {
2567 std::string name(1, s[i]);
2568 auto iter = m_options.find(name);
2569
2570 if (iter == m_options.end())
2571 {
2572 if (m_allow_unrecognised)
2573 {
2574 unmatched.push_back(std::string("-") + s[i]);
2575 continue;
2576 }
2577 //error
2578 throw_or_mimic<exceptions::no_such_option>(name);
2579 }
2580
2581 auto value = iter->second;
2582
2583 if (i + 1 == s.size())
2584 {
2585 //it must be the last argument
2586 checked_parse_arg(argc, argv, current, value, name);
2587 }
2588 else if (value->value().has_implicit())
2589 {
2590 parse_option(value, name, value->value().get_implicit_value());
2591 }
2592 else if (i + 1 < s.size())
2593 {
2594 std::string arg_value = s.substr(i + 1);
2595 parse_option(value, name, arg_value);
2596 break;
2597 }
2598 else
2599 {
2600 //error
2601 throw_or_mimic<exceptions::option_requires_argument>(name);
2602 }
2603 }
2604 }
2605 else if (argu_desc.arg_name.length() != 0)
2606 {
2607 const std::string& name = argu_desc.arg_name;
2608
2609 auto iter = m_options.find(name);
2610
2611 if (iter == m_options.end())
2612 {
2613 if (m_allow_unrecognised)
2614 {
2615 // keep unrecognised options in argument list, skip to next argument
2616 unmatched.emplace_back(argv[current]);
2617 ++current;
2618 continue;
2619 }
2620 //error
2621 throw_or_mimic<exceptions::no_such_option>(name);
2622 }
2623
2624 auto opt = iter->second;
2625
2626 //equals provided for long option?
2627 if (argu_desc.set_value)
2628 {
2629 //parse the option given
2630
2631 parse_option(opt, name, argu_desc.value);
2632 }
2633 else
2634 {
2635 //parse the next argument
2636 checked_parse_arg(argc, argv, current, opt, name);
2637 }
2638 }
2639
2640 }
2641
2642 ++current;
2643 }
2644
2645 for (auto& opt : m_options)
2646 {
2647 auto& detail = opt.second;
2648 const auto& value = detail->value();
2649
2650 auto& store = m_parsed[detail->hash()];
2651
2652 if (value.has_default()) {
2653 if (!store.count() && !store.has_default()) {
2654 parse_default(detail);
2655 }
2656 }
2657 else {
2658 parse_no_value(detail);
2659 }
2660 }
2661
2662 if (consume_remaining)
2663 {
2664 while (current < argc)
2665 {
2666 if (!consume_positional(argv[current], next_positional)) {
2667 break;
2668 }
2669 ++current;
2670 }
2671
2672 //adjust argv for any that couldn't be swallowed
2673 while (current != argc) {
2674 unmatched.emplace_back(argv[current]);
2675 ++current;
2676 }
2677 }
2678
2679 finalise_aliases();
2680
2681 ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(m_defaults), std::move(unmatched));
2682 return parsed;
2683}
2684
2685inline
2686void
2687OptionParser::finalise_aliases()
2688{
2689 for (auto& option: m_options)
2690 {
2691 auto& detail = *option.second;
2692 auto hash = detail.hash();
2693 m_keys[detail.short_name()] = hash;
2694 for(const auto& long_name : detail.long_names()) {
2695 m_keys[long_name] = hash;
2696 }
2697
2698 m_parsed.emplace(hash, OptionValue());
2699 }
2700}
2701
2702inline
2703void
2704Options::add_option
2705(
2706 const std::string& group,
2707 const Option& option
2708)
2709{
2710 add_options(group, {option});
2711}
2712
2713inline
2714void
2715Options::add_option
2716(
2717 const std::string& group,
2718 const std::string& s,
2719 const OptionNames& l,
2720 std::string desc,
2721 const std::shared_ptr<const Value>& value,
2722 std::string arg_help
2723)
2724{
2725 auto stringDesc = toLocalString(std::move(desc));
2726 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
2727
2728 if (!s.empty())
2729 {
2730 add_one_option(s, option);
2731 }
2732
2733 for(const auto& long_name : l) {
2734 add_one_option(long_name, option);
2735 }
2736
2737 //add the help details
2738
2739 if (m_help.find(group) == m_help.end())
2740 {
2741 m_group.push_back(group);
2742 }
2743
2744 auto& options = m_help[group];
2745
2746 options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
2747 value->has_default(), value->get_default_value(),
2748 value->has_implicit(), value->get_implicit_value(),
2749 std::move(arg_help),
2750 value->is_container(),
2751 value->is_boolean()});
2752}
2753
2754inline
2755void
2756Options::add_one_option
2757(
2758 const std::string& option,
2759 const std::shared_ptr<OptionDetails>& details
2760)
2761{
2762 auto in = m_options->emplace(option, details);
2763
2764 if (!in.second)
2765 {
2766 throw_or_mimic<exceptions::option_already_exists>(option);
2767 }
2768}
2769
2770inline
2771String
2772Options::help_one_group(const std::string& g) const
2773{
2774 using OptionHelp = std::vector<std::pair<String, String>>;
2775
2776 auto group = m_help.find(g);
2777 if (group == m_help.end())
2778 {
2779 return "";
2780 }
2781
2782 OptionHelp format;
2783
2784 std::size_t longest = 0;
2785
2786 String result;
2787
2788 if (!g.empty())
2789 {
2790 result += toLocalString(" " + g + " options:\n");
2791 }
2792
2793 for (const auto& o : group->second.options)
2794 {
2795 if (o.l.size() &&
2796 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2797 !m_show_positional)
2798 {
2799 continue;
2800 }
2801
2802 auto s = format_option(o);
2803 longest = (std::max)(longest, stringLength(s));
2804 format.push_back(std::make_pair(s, String()));
2805 }
2806 longest = (std::min)(longest, OPTION_LONGEST);
2807
2808 //widest allowed description -- min 10 chars for helptext/line
2809 std::size_t allowed = 10;
2810 if (m_width > allowed + longest + OPTION_DESC_GAP)
2811 {
2812 allowed = m_width - longest - OPTION_DESC_GAP;
2813 }
2814
2815 auto fiter = format.begin();
2816 for (const auto& o : group->second.options)
2817 {
2818 if (o.l.size() &&
2819 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2820 !m_show_positional)
2821 {
2822 continue;
2823 }
2824
2825 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
2826
2827 result += fiter->first;
2828 if (stringLength(fiter->first) > longest)
2829 {
2830 result += '\n';
2831 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2832 }
2833 else
2834 {
2835 result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2836 stringLength(fiter->first),
2837 ' '));
2838 }
2839 result += d;
2840 result += '\n';
2841
2842 ++fiter;
2843 }
2844
2845 return result;
2846}
2847
2848inline
2849void
2850Options::generate_group_help
2851(
2852 String& result,
2853 const std::vector<std::string>& print_groups
2854) const
2855{
2856 for (std::size_t i = 0; i != print_groups.size(); ++i)
2857 {
2858 const String& group_help_text = help_one_group(print_groups[i]);
2859 if (empty(group_help_text))
2860 {
2861 continue;
2862 }
2863 result += group_help_text;
2864 if (i < print_groups.size() - 1)
2865 {
2866 result += '\n';
2867 }
2868 }
2869}
2870
2871inline
2872void
2873Options::generate_all_groups_help(String& result) const
2874{
2875 generate_group_help(result, m_group);
2876}
2877
2878inline
2879std::string
2880Options::help(const std::vector<std::string>& help_groups, bool print_usage) const
2881{
2882 String result = m_help_string;
2883 if(print_usage)
2884 {
2885 result+= "\nUsage:\n " + toLocalString(m_program);
2886 }
2887
2888 if (!m_custom_help.empty())
2889 {
2890 result += " " + toLocalString(m_custom_help);
2891 }
2892
2893 if (!m_positional.empty() && !m_positional_help.empty()) {
2894 result += " " + toLocalString(m_positional_help);
2895 }
2896
2897 result += "\n\n";
2898
2899 if (help_groups.empty())
2900 {
2901 generate_all_groups_help(result);
2902 }
2903 else
2904 {
2905 generate_group_help(result, help_groups);
2906 }
2907
2908 return toUTF8String(result);
2909}
2910
2911inline
2912std::vector<std::string>
2913Options::groups() const
2914{
2915 return m_group;
2916}
2917
2918inline
2919const HelpGroupDetails&
2920Options::group_help(const std::string& group) const
2921{
2922 return m_help.at(group);
2923}
2924
2925} // namespace cxxopts
2926
2927#endif //CXXOPTS_HPP_INCLUDED
Definition cxxopts.hpp:1586
Definition cxxopts.hpp:2104
Definition cxxopts.hpp:1362
Definition cxxopts.hpp:1870
Definition cxxopts.hpp:1475
Definition cxxopts.hpp:1928
Definition cxxopts.hpp:1629
Definition cxxopts.hpp:1626
Definition cxxopts.hpp:370
Definition cxxopts.hpp:421
Definition cxxopts.hpp:495
Definition cxxopts.hpp:486
Definition cxxopts.hpp:450
Definition cxxopts.hpp:440
Definition cxxopts.hpp:1149
Definition cxxopts.hpp:1286
Definition cxxopts.hpp:1468
Definition cxxopts.hpp:1454
Definition cxxopts.hpp:1844
Definition cxxopts.hpp:887
Definition cxxopts.hpp:602
Definition cxxopts.hpp:1137