27#ifndef CXXOPTS_HPP_INCLUDED
28#define CXXOPTS_HPP_INCLUDED
35#include <initializer_list>
40#include <unordered_map>
41#include <unordered_set>
47#ifdef CXXOPTS_NO_EXCEPTIONS
51#if defined(__GNUC__) && !defined(__clang__)
52# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
53# define CXXOPTS_NO_REGEX true
56#if defined(_MSC_VER) && !defined(__clang__)
57#define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern
58#define CXXOPTS_LINKONCE __declspec(selectany) extern
60#define CXXOPTS_LINKONCE_CONST
61#define CXXOPTS_LINKONCE
64#ifndef CXXOPTS_NO_REGEX
70# if __has_include(<optional>)
72# ifdef __cpp_lib_optional
73# define CXXOPTS_HAS_OPTIONAL
76# if __has_include(<filesystem>)
78# ifdef __cpp_lib_filesystem
79# define CXXOPTS_HAS_FILESYSTEM
84#define CXXOPTS_FALLTHROUGH
85#ifdef __has_cpp_attribute
86 #if __has_cpp_attribute(fallthrough)
87 #undef CXXOPTS_FALLTHROUGH
88 #define CXXOPTS_FALLTHROUGH [[fallthrough]]
92#if __cplusplus >= 201603L
93#define CXXOPTS_NODISCARD [[nodiscard]]
95#define CXXOPTS_NODISCARD
98#ifndef CXXOPTS_VECTOR_DELIMITER
99#define CXXOPTS_VECTOR_DELIMITER ','
102#define CXXOPTS__VERSION_MAJOR 3
103#define CXXOPTS__VERSION_MINOR 3
104#define CXXOPTS__VERSION_PATCH 1
106#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
107 #define CXXOPTS_NULL_DEREF_IGNORE
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)
117#define CXXOPTS_DIAGNOSTIC_PUSH
118#define CXXOPTS_DIAGNOSTIC_POP
119#define CXXOPTS_IGNORE_WARNING(x)
122#ifdef CXXOPTS_NO_RTTI
123#define CXXOPTS_RTTI_CAST static_cast
125#define CXXOPTS_RTTI_CAST dynamic_cast
129static constexpr struct {
130 uint8_t major, minor, patch;
132 CXXOPTS__VERSION_MAJOR,
133 CXXOPTS__VERSION_MINOR,
134 CXXOPTS__VERSION_PATCH
144#ifdef CXXOPTS_USE_UNICODE
145#include <unicode/unistr.h>
149using String = icu::UnicodeString;
153toLocalString(std::string s)
155 return icu::UnicodeString::fromUTF8(std::move(s));
160CXXOPTS_DIAGNOSTIC_PUSH
161CXXOPTS_IGNORE_WARNING(
"-Wnon-virtual-dtor")
163class UnicodeStringIterator
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&;
173 UnicodeStringIterator(
const icu::UnicodeString*
string, int32_t pos)
182 return s->char32At(i);
186 operator==(
const UnicodeStringIterator& rhs)
const
188 return s == rhs.s && i == rhs.i;
192 operator!=(
const UnicodeStringIterator& rhs)
const
194 return !(*
this == rhs);
197 UnicodeStringIterator&
204 UnicodeStringIterator
207 return UnicodeStringIterator(s, i + v);
211 const icu::UnicodeString* s;
214CXXOPTS_DIAGNOSTIC_POP
218stringAppend(String&s, String a)
220 return s.append(std::move(a));
225stringAppend(String& s, std::size_t n, UChar32 c)
227 for (std::size_t i = 0; i != n; ++i)
235template <
typename Iterator>
237stringAppend(String& s, Iterator begin, Iterator end)
250stringLength(
const String& s)
252 return static_cast<size_t>(s.length());
257toUTF8String(
const String& s)
260 s.toUTF8String(result);
267empty(
const String& s)
277cxxopts::UnicodeStringIterator
278begin(
const icu::UnicodeString& s)
280 return cxxopts::UnicodeStringIterator(&s, 0);
284cxxopts::UnicodeStringIterator
285end(
const icu::UnicodeString& s)
287 return cxxopts::UnicodeStringIterator(&s, s.length());
297using String = std::string;
303 return std::forward<T>(t);
308stringLength(
const String& s)
315stringAppend(String&s,
const String& a)
322stringAppend(String& s, std::size_t n,
char c)
324 return s.append(n, c);
327template <
typename Iterator>
329stringAppend(String& s, Iterator begin, Iterator end)
331 return s.append(begin, end);
338 return std::forward<T>(t);
343empty(
const std::string& s)
356CXXOPTS_LINKONCE_CONST std::string LQUOTE(
"\'");
357CXXOPTS_LINKONCE_CONST std::string RQUOTE(
"\'");
364CXXOPTS_DIAGNOSTIC_PUSH
365CXXOPTS_IGNORE_WARNING(
"-Wnon-virtual-dtor")
368CXXOPTS_IGNORE_WARNING(
"-Weffc++")
373 virtual ~Value() =
default;
376 std::shared_ptr<Value>
380 add(
const std::string& text)
const = 0;
383 parse(
const std::string& text)
const = 0;
389 has_default()
const = 0;
392 is_container()
const = 0;
395 has_implicit()
const = 0;
398 get_default_value()
const = 0;
401 get_implicit_value()
const = 0;
403 virtual std::shared_ptr<Value>
404 default_value(
const std::string& value) = 0;
406 virtual std::shared_ptr<Value>
407 implicit_value(
const std::string& value) = 0;
409 virtual std::shared_ptr<Value>
410 no_implicit_value() = 0;
413 is_boolean()
const = 0;
416CXXOPTS_DIAGNOSTIC_POP
418namespace exceptions {
420class exception :
public std::exception
423 explicit exception(std::string message)
424 : m_message(std::move(message))
430 what()
const noexcept override
432 return m_message.c_str();
436 std::string m_message;
439class specification :
public exception
443 explicit specification(
const std::string& message)
449class parsing :
public exception
452 explicit parsing(
const std::string& message)
458class option_already_exists :
public specification
461 explicit option_already_exists(
const std::string& option)
462 : specification(
"Option " + LQUOTE + option + RQUOTE +
" already exists")
467class invalid_option_format :
public specification
470 explicit invalid_option_format(
const std::string& format)
471 : specification(
"Invalid option format " + LQUOTE + format + RQUOTE)
476class invalid_option_syntax :
public parsing {
478 explicit invalid_option_syntax(
const std::string& text)
479 : parsing(
"Argument " + LQUOTE + text + RQUOTE +
480 " starts with a - but has incorrect syntax")
485class no_such_option :
public parsing
488 explicit no_such_option(
const std::string& option)
489 : parsing(
"Option " + LQUOTE + option + RQUOTE +
" does not exist")
494class missing_argument :
public parsing
497 explicit missing_argument(
const std::string& option)
499 "Option " + LQUOTE + option + RQUOTE +
" is missing an argument"
505class option_requires_argument :
public parsing
508 explicit option_requires_argument(
const std::string& option)
510 "Option " + LQUOTE + option + RQUOTE +
" requires an argument"
516class gratuitous_argument_for_option :
public parsing
519 gratuitous_argument_for_option
521 const std::string& option,
522 const std::string& arg
525 "Option " + LQUOTE + option + RQUOTE +
526 " does not take an argument, but argument " +
527 LQUOTE + arg + RQUOTE +
" given"
533class requested_option_not_present :
public parsing
536 explicit requested_option_not_present(
const std::string& option)
537 : parsing(
"Option " + LQUOTE + option + RQUOTE +
" not present")
542class option_has_no_value :
public exception
545 explicit option_has_no_value(
const std::string& option)
548 (
"Option " + LQUOTE + option + RQUOTE +
" has no value") :
549 "Option has no value")
554class incorrect_argument_type :
public parsing
557 explicit incorrect_argument_type
559 const std::string& arg
562 "Argument " + LQUOTE + arg + RQUOTE +
" failed to parse"
572void throw_or_mimic(
const std::string& text)
574 static_assert(std::is_base_of<std::exception, T>::value,
575 "throw_or_mimic only works on std::exception and "
578#ifndef CXXOPTS_NO_EXCEPTIONS
585 std::cerr << exception.what() << std::endl;
586 std::exit(EXIT_FAILURE);
590using OptionNames = std::vector<std::string>;
594namespace parser_tool {
598 std::string negative =
"";
599 std::string base =
"";
600 std::string value =
"";
603 std::string arg_name =
"";
604 bool grouping =
false;
605 bool set_value =
false;
606 std::string value =
"";
609#ifdef CXXOPTS_NO_REGEX
610inline IntegerDesc SplitInteger(
const std::string &text)
614 throw_or_mimic<exceptions::incorrect_argument_type>(text);
617 const char *pdata = text.c_str();
623 if (strncmp(pdata,
"0x", 2) == 0)
630 desc.value = std::string(pdata);
634 throw_or_mimic<exceptions::incorrect_argument_type>(text);
639inline bool IsTrueText(
const std::string &text)
641 const char *pdata = text.c_str();
642 if (*pdata ==
't' || *pdata ==
'T')
645 if (strncmp(pdata,
"rue\0", 4) == 0)
650 else if (strncmp(pdata,
"1\0", 2) == 0)
657inline bool IsFalseText(
const std::string &text)
659 const char *pdata = text.c_str();
660 if (*pdata ==
'f' || *pdata ==
'F')
663 if (strncmp(pdata,
"alse\0", 5) == 0)
668 else if (strncmp(pdata,
"0\0", 2) == 0)
675inline OptionNames split_option_names(
const std::string &text)
677 OptionNames split_names;
679 std::string::size_type token_start_pos = 0;
680 auto length = text.length();
684 throw_or_mimic<exceptions::invalid_option_format>(text);
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);
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);
698 if (next_delimiter_pos == npos) {
699 next_delimiter_pos = length;
701 auto token_length = next_delimiter_pos - token_start_pos;
704 const char* option_name_valid_chars =
705 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
706 "abcdefghijklmnopqrstuvwxyz"
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);
715 split_names.emplace_back(text.substr(token_start_pos, token_length));
716 token_start_pos = next_delimiter_pos + 1;
721inline ArguDesc ParseArgument(
const char *arg,
bool &matched)
724 const char *pdata = arg;
726 if (strncmp(pdata,
"--", 2) == 0)
729 if (isalnum(*pdata, std::locale::classic()))
731 argu_desc.arg_name.push_back(*pdata);
733 while (isalnum(*pdata, std::locale::classic()) || *pdata ==
'-' || *pdata ==
'_')
735 argu_desc.arg_name.push_back(*pdata);
738 if (argu_desc.arg_name.length() > 1)
742 argu_desc.set_value =
true;
746 argu_desc.value = std::string(pdata);
750 else if (*pdata ==
'\0')
757 else if (strncmp(pdata,
"-", 1) == 0)
760 argu_desc.grouping =
true;
761 while (isalnum(*pdata, std::locale::classic()))
763 argu_desc.arg_name.push_back(*pdata);
766 matched = !argu_desc.arg_name.empty() && *pdata ==
'\0';
775const char*
const integer_pattern =
776 "(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)";
778const char*
const truthy_pattern =
781const char*
const falsy_pattern =
784const char*
const option_pattern =
785 "--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)";
787const char*
const option_specifier_pattern =
788 "([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*";
790const char*
const option_specifier_separator_pattern =
", *";
794inline IntegerDesc SplitInteger(
const std::string &text)
796 static const std::basic_regex<char> integer_matcher(integer_pattern);
799 std::regex_match(text, match, integer_matcher);
801 if (match.length() == 0)
803 throw_or_mimic<exceptions::incorrect_argument_type>(text);
807 desc.negative = match[1];
808 desc.base = match[2];
809 desc.value = match[3];
811 if (match.length(4) > 0)
813 desc.base = match[5];
821inline bool IsTrueText(
const std::string &text)
823 static const std::basic_regex<char> truthy_matcher(truthy_pattern);
825 std::regex_match(text, result, truthy_matcher);
826 return !result.empty();
829inline bool IsFalseText(
const std::string &text)
831 static const std::basic_regex<char> falsy_matcher(falsy_pattern);
833 std::regex_match(text, result, falsy_matcher);
834 return !result.empty();
840inline OptionNames split_option_names(
const std::string &text)
842 static const std::basic_regex<char> option_specifier_matcher(option_specifier_pattern);
843 if (!std::regex_match(text.c_str(), option_specifier_matcher))
845 throw_or_mimic<exceptions::invalid_option_format>(text);
848 OptionNames split_names;
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));
858inline ArguDesc ParseArgument(
const char *arg,
bool &matched)
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();
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)
872 argu_desc.grouping =
true;
873 argu_desc.arg_name = result[4].str();
881#undef CXXOPTS_NO_REGEX
886template <
typename T,
bool B>
892 template <
typename U>
894 operator()(
bool negative, U u,
const std::string& text)
898 if (u >
static_cast<U
>((std::numeric_limits<T>::min)()))
900 throw_or_mimic<exceptions::incorrect_argument_type>(text);
905 if (u >
static_cast<U
>((std::numeric_limits<T>::max)()))
907 throw_or_mimic<exceptions::incorrect_argument_type>(text);
916 template <
typename U>
918 operator()(
bool, U,
const std::string&)
const {}
921template <
typename T,
typename U>
923check_signed_range(
bool negative, U value,
const std::string& text)
930template <
typename R,
typename T>
932checked_negate(R& r, T&& t,
const std::string&, std::true_type)
937 r =
static_cast<R
>(-
static_cast<R
>(t-1)-1);
940template <
typename R,
typename T>
942checked_negate(R&, T&&,
const std::string& text, std::false_type)
944 throw_or_mimic<exceptions::incorrect_argument_type>(text);
949integer_parser(
const std::string& text, T& value)
953 using US =
typename std::make_unsigned<T>::type;
954 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
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;
962 for (
char ch : value_match)
966 if (ch >=
'0' && ch <=
'9')
968 digit =
static_cast<US
>(ch -
'0');
970 else if (base == 16 && ch >=
'a' && ch <=
'f')
972 digit =
static_cast<US
>(ch -
'a' + 10);
974 else if (base == 16 && ch >=
'A' && ch <=
'F')
976 digit =
static_cast<US
>(ch -
'A' + 10);
980 throw_or_mimic<exceptions::incorrect_argument_type>(text);
986 limit =
static_cast<US
>(std::abs(
static_cast<intmax_t
>((std::numeric_limits<T>::min)())));
990 limit = (std::numeric_limits<T>::max)();
993 if (base != 0 && result > limit / base)
995 throw_or_mimic<exceptions::incorrect_argument_type>(text);
997 if (result * base > limit - digit)
999 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1002 result =
static_cast<US
>(result * base + digit);
1005 detail::check_signed_range<T>(negative, result, text);
1009 checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
1013 value =
static_cast<T
>(result);
1017template <
typename T>
1018void stringstream_parser(
const std::string& text, T& value)
1020 std::stringstream in(text);
1023 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1027template <
typename T,
1028 typename std::enable_if<std::is_integral<T>::value>::type* =
nullptr
1030void parse_value(
const std::string& text, T& value)
1032 integer_parser(text, value);
1037parse_value(
const std::string& text,
bool& value)
1039 if (parser_tool::IsTrueText(text))
1045 if (parser_tool::IsFalseText(text))
1051 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1056parse_value(
const std::string& text, std::string& value)
1064template <
typename T,
1065 typename std::enable_if<!std::is_integral<T>::value>::type* =
nullptr
1068parse_value(
const std::string& text, T& value) {
1069 stringstream_parser(text, value);
1072#ifdef CXXOPTS_HAS_OPTIONAL
1073template <
typename T>
1075parse_value(
const std::string& text, std::optional<T>& value)
1078 parse_value(text, result);
1079 value = std::move(result);
1083#ifdef CXXOPTS_HAS_FILESYSTEM
1086parse_value(
const std::string& text, std::filesystem::path& value)
1093void parse_value(
const std::string& text,
char& c)
1095 if (text.length() != 1)
1097 throw_or_mimic<exceptions::incorrect_argument_type>(text);
1103template <
typename T>
1105parse_value(
const std::string& text, std::vector<T>& value)
1109 parse_value(text, v);
1110 value.emplace_back(std::move(v));
1113 std::stringstream in(text);
1115 while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
1117 parse_value(token, v);
1118 value.emplace_back(std::move(v));
1122template <
typename T>
1124add_value(
const std::string& text, T& value)
1126 parse_value(text, value);
1129template <
typename T>
1131add_value(
const std::string& text, std::vector<T>& value)
1135 value.emplace_back(std::move(v));
1138template <
typename T>
1141 static constexpr bool value =
false;
1144template <
typename T>
1147 static constexpr bool value =
true;
1150template <
typename T>
1153 using Self = abstract_value<T>;
1157 : m_result(std::make_shared<T>())
1158 , m_store(m_result.get())
1162 explicit abstract_value(T* t)
1167 ~abstract_value()
override =
default;
1169 abstract_value& operator=(
const abstract_value&) =
default;
1171 abstract_value(
const abstract_value& rhs)
1175 m_result = std::make_shared<T>();
1176 m_store = m_result.get();
1180 m_store = rhs.m_store;
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;
1190 add(
const std::string& text)
const override
1192 add_value(text, *m_store);
1196 parse(
const std::string& text)
const override
1198 parse_value(text, *m_store);
1202 is_container()
const override
1204 return type_is_container<T>::value;
1208 parse()
const override
1210 parse_value(m_default_value, *m_store);
1214 has_default()
const override
1220 has_implicit()
const override
1225 std::shared_ptr<Value>
1226 default_value(
const std::string& value)
override
1229 m_default_value = value;
1230 return shared_from_this();
1233 std::shared_ptr<Value>
1234 implicit_value(
const std::string& value)
override
1237 m_implicit_value = value;
1238 return shared_from_this();
1241 std::shared_ptr<Value>
1242 no_implicit_value()
override
1245 return shared_from_this();
1249 get_default_value()
const override
1251 return m_default_value;
1255 get_implicit_value()
const override
1257 return m_implicit_value;
1261 is_boolean()
const override
1263 return std::is_same<T, bool>::value;
1269 if (m_store ==
nullptr)
1277 std::shared_ptr<T> m_result{};
1280 bool m_default =
false;
1281 bool m_implicit =
false;
1283 std::string m_default_value{};
1284 std::string m_implicit_value{};
1287template <
typename T>
1291 using abstract_value<T>::abstract_value;
1294 std::shared_ptr<Value>
1295 clone()
const override
1297 return std::make_shared<standard_value<T>>(*this);
1302class standard_value<bool> :
public abstract_value<bool>
1305 ~standard_value()
override =
default;
1309 set_default_and_implicit();
1312 explicit standard_value(
bool* b)
1316 m_implicit_value =
"true";
1319 std::shared_ptr<Value>
1320 clone()
const override
1322 return std::make_shared<standard_value<bool>>(*this);
1328 set_default_and_implicit()
1331 m_default_value =
"false";
1333 m_implicit_value =
"true";
1339template <
typename T>
1340std::shared_ptr<Value>
1343 return std::make_shared<values::standard_value<T>>();
1346template <
typename T>
1347std::shared_ptr<Value>
1350 return std::make_shared<values::standard_value<T>>(&t);
1358first_or_empty(
const OptionNames& long_names)
1360 static const std::string empty{
""};
1361 return long_names.empty() ? empty : long_names.front();
1372 std::shared_ptr<const Value> val
1374 : m_short(std::move(short_))
1375 , m_long(std::move(long_))
1376 , m_desc(std::move(desc))
1377 , m_value(std::move(val))
1380 m_hash = std::hash<std::string>{}(first_long_name() + m_short);
1383 OptionDetails(
const OptionDetails& rhs)
1384 : m_desc(rhs.m_desc)
1385 , m_value(rhs.m_value->clone())
1386 , m_count(rhs.m_count)
1390 OptionDetails(OptionDetails&& rhs) =
default;
1406 std::shared_ptr<Value>
1407 make_storage()
const
1409 return m_value->clone();
1421 first_long_name()
const
1423 return first_or_empty(m_long);
1428 essential_name()
const
1430 return m_long.empty() ? m_short : m_long.front();
1447 std::string m_short{};
1448 OptionNames m_long{};
1450 std::shared_ptr<const Value> m_value{};
1453 std::size_t m_hash{};
1462 std::string default_value;
1464 std::string implicit_value;
1465 std::string arg_help;
1473 std::string description{};
1474 std::vector<HelpOptionDetails> options{};
1483 const std::shared_ptr<const OptionDetails>& details,
1484 const std::string& text
1487 ensure_value(details);
1490 m_long_names = &details->long_names();
1496 const std::shared_ptr<const OptionDetails>& details,
1497 const std::string& text
1500 ensure_value(details);
1502 m_value->parse(text);
1503 m_long_names = &details->long_names();
1507 parse_default(
const std::shared_ptr<const OptionDetails>& details)
1509 ensure_value(details);
1511 m_long_names = &details->long_names();
1516 parse_no_value(
const std::shared_ptr<const OptionDetails>& details)
1518 m_long_names = &details->long_names();
1521#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1522CXXOPTS_DIAGNOSTIC_PUSH
1523CXXOPTS_IGNORE_WARNING(
"-Wnull-dereference")
1528 count()
const noexcept
1533#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1534CXXOPTS_DIAGNOSTIC_POP
1540 has_default()
const noexcept
1545 template <
typename T>
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));
1554 return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
1557#ifdef CXXOPTS_HAS_OPTIONAL
1558 template <
typename T>
1562 if (m_value ==
nullptr) {
1563 return std::nullopt;
1571 ensure_value(
const std::shared_ptr<const OptionDetails>& details)
1573 if (m_value ==
nullptr)
1575 m_value = details->make_storage();
1580 const OptionNames * m_long_names =
nullptr;
1583 std::shared_ptr<Value> m_value{};
1584 std::size_t m_count = 0;
1585 bool m_default =
false;
1591 KeyValue(std::string key_, std::string value_) noexcept
1592 : m_key(std::move(key_))
1593 , m_value(std::move(value_))
1611 template <
typename T>
1616 values::parse_value(m_value, result);
1622 std::string m_value;
1625using ParsedHashMap = std::unordered_map<std::size_t, OptionValue>;
1626using NameHashMap = std::unordered_map<std::string, std::size_t>;
1634 using iterator_category = std::forward_iterator_tag;
1636 using difference_type = void;
1640 Iterator() =
default;
1641 Iterator(
const Iterator&) =
default;
1645CXXOPTS_DIAGNOSTIC_PUSH
1646CXXOPTS_IGNORE_WARNING(
"-Weffc++")
1647 Iterator(
const ParseResult *pr,
bool end=
false)
1652 m_sequential =
false;
1653 m_iter = m_pr->m_defaults.end();
1657 m_sequential =
true;
1658 m_iter = m_pr->m_sequential.begin();
1660 if (m_iter == m_pr->m_sequential.end())
1662 m_sequential =
false;
1663 m_iter = m_pr->m_defaults.begin();
1667CXXOPTS_DIAGNOSTIC_POP
1669 Iterator& operator++()
1672 if(m_sequential && m_iter == m_pr->m_sequential.end())
1674 m_sequential =
false;
1675 m_iter = m_pr->m_defaults.begin();
1681 Iterator operator++(
int)
1683 Iterator retval = *
this;
1688 bool operator==(
const Iterator& other)
const
1690 return (m_sequential == other.m_sequential) && (m_iter == other.m_iter);
1693 bool operator!=(
const Iterator& other)
const
1695 return !(*
this == other);
1705 return m_iter.operator->();
1709 const ParseResult* m_pr;
1710 std::vector<KeyValue>::const_iterator m_iter;
1711 bool m_sequential =
true;
1714 ParseResult() =
default;
1715 ParseResult(
const ParseResult&) =
default;
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))
1733 return Iterator(
this);
1739 return Iterator(
this,
true);
1743 count(
const std::string& o)
const
1745 auto iter = m_keys.find(o);
1746 if (iter == m_keys.end())
1751 auto viter = m_values.find(iter->second);
1753 if (viter == m_values.end())
1758 return viter->second.count();
1762 contains(
const std::string& o)
const
1764 return static_cast<bool>(count(o));
1768 operator[](
const std::string& option)
const
1770 auto iter = m_keys.find(option);
1772 if (iter == m_keys.end())
1774 throw_or_mimic<exceptions::requested_option_not_present>(option);
1777 auto viter = m_values.find(iter->second);
1779 if (viter == m_values.end())
1781 throw_or_mimic<exceptions::requested_option_not_present>(option);
1784 return viter->second;
1787#ifdef CXXOPTS_HAS_OPTIONAL
1788 template <
typename T>
1790 as_optional(
const std::string& option)
const
1792 auto iter = m_keys.find(option);
1793 if (iter != m_keys.end())
1795 auto viter = m_values.find(iter->second);
1796 if (viter != m_values.end())
1798 return viter->second.as_optional<T>();
1801 return std::nullopt;
1805 const std::vector<KeyValue>&
1808 return m_sequential;
1811 const std::vector<std::string>&
1817 const std::vector<KeyValue>&
1824 arguments_string()
const
1827 for(
const auto& kv: m_sequential)
1829 result += kv.key() +
" = " + kv.value() +
"\n";
1831 for(
const auto& kv: m_defaults)
1833 result += kv.key() +
" = " + kv.value() +
" " +
"(default)" +
"\n";
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{};
1852 std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
1853 std::string arg_help =
""
1855 : opts_(std::move(opts))
1856 , desc_(std::move(desc))
1857 , value_(std::move(value))
1858 , arg_help_(std::move(arg_help))
1864 std::shared_ptr<const Value> value_;
1865 std::string arg_help_;
1868using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1869using PositionalList = std::vector<std::string>;
1870using PositionalListIterator = PositionalList::const_iterator;
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)
1883 parse(
int argc,
const char*
const* argv);
1886 consume_positional(
const std::string& a, PositionalListIterator& next);
1892 const char*
const* argv,
1894 const std::shared_ptr<OptionDetails>& value,
1895 const std::string& name
1899 add_to_option(
const std::shared_ptr<OptionDetails>& value,
const std::string& arg);
1904 const std::shared_ptr<OptionDetails>& value,
1905 const std::string& name,
1906 const std::string& arg =
""
1910 parse_default(
const std::shared_ptr<OptionDetails>& details);
1913 parse_no_value(
const std::shared_ptr<OptionDetails>& details);
1917 void finalise_aliases();
1919 const OptionMap& m_options;
1920 const PositionalList& m_positional;
1922 std::vector<KeyValue> m_sequential{};
1923 std::vector<KeyValue> m_defaults{};
1924 bool m_allow_unrecognised;
1926 ParsedHashMap m_parsed{};
1927 NameHashMap m_keys{};
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)
1942 , m_tab_expansion(
false)
1943 , m_options(std::make_shared<OptionMap>())
1948 positional_help(std::string help_text)
1950 m_positional_help = std::move(help_text);
1955 custom_help(std::string help_text)
1957 m_custom_help = std::move(help_text);
1962 show_positional_help()
1964 m_show_positional =
true;
1969 allow_unrecognised_options()
1971 m_allow_unrecognised =
true;
1976 set_width(std::size_t width)
1983 set_tab_expansion(
bool expansion=
true)
1985 m_tab_expansion = expansion;
1990 parse(
int argc,
const char*
const* argv);
1993 add_options(std::string group =
"");
1998 const std::string& group,
1999 std::initializer_list<Option> options
2005 const std::string& group,
2012 const std::string& group,
2013 const std::string& s,
2014 const OptionNames& l,
2016 const std::shared_ptr<const Value>& value,
2017 std::string arg_help
2023 const std::string& group,
2024 const std::string& short_name,
2025 const std::string& single_long_name,
2027 const std::shared_ptr<const Value>& value,
2028 std::string arg_help
2031 OptionNames long_names;
2032 long_names.emplace_back(single_long_name);
2033 add_option(group, short_name, long_names, desc, value, arg_help);
2038 parse_positional(std::string option);
2041 parse_positional(std::vector<std::string> options);
2044 parse_positional(std::initializer_list<std::string> options);
2046 template <
typename Iterator>
2048 parse_positional(Iterator begin, Iterator end) {
2049 parse_positional(std::vector<std::string>{begin, end});
2053 help(
const std::vector<std::string>& groups = {},
bool print_usage=
true)
const;
2055 std::vector<std::string>
2059 group_help(
const std::string& group)
const;
2061 const std::string& program()
const
2071 const std::string& option,
2072 const std::shared_ptr<OptionDetails>& details
2076 help_one_group(
const std::string& group)
const;
2082 const std::vector<std::string>& groups
2086 generate_all_groups_help(String& result)
const;
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;
2097 std::shared_ptr<OptionMap> m_options;
2098 std::vector<std::string> m_positional{};
2099 std::unordered_set<std::string> m_positional_set{};
2102 std::vector<std::string> m_group{};
2103 std::map<std::string, HelpGroupDetails> m_help{};
2110 OptionAdder(
Options& options, std::string group)
2111 : m_options(options), m_group(std::move(group))
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 =
""
2127 std::string m_group;
2131constexpr std::size_t OPTION_LONGEST = 30;
2132constexpr std::size_t OPTION_DESC_GAP = 2;
2140 const auto& s = o.s;
2141 const auto& l = first_or_empty(o.l);
2143 String result =
" ";
2147 result +=
"-" + toLocalString(s);
2160 result +=
" --" + toLocalString(l);
2163 auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) :
"arg";
2169 result +=
" [=" + arg +
"(=" + toLocalString(o.implicit_value) +
")]";
2173 result +=
" " + arg;
2185 std::size_t allowed,
2191 if (o.has_default && (!o.is_boolean || o.default_value !=
"false"))
2193 if(!o.default_value.empty())
2195 desc += toLocalString(
" (default: " + o.default_value +
")");
2199 desc += toLocalString(
" (default: \"\")");
2208 auto size = std::size_t{ 0 };
2209 for (
auto c = std::begin(desc); c != std::end(desc); ++c)
2216 else if (*c ==
'\t')
2218 auto skip = 8 - size % 8;
2219 stringAppend(desc2, skip,
' ');
2233 auto current = std::begin(desc);
2234 auto previous = current;
2235 auto startLine = current;
2236 auto lastSpace = current;
2238 auto size = std::size_t{};
2241 bool onlyWhiteSpace =
true;
2243 while (current != std::end(desc))
2245 appendNewLine =
false;
2246 if (*previous ==
' ' || *previous ==
'\t')
2248 lastSpace = current;
2250 if (*current !=
' ' && *current !=
'\t')
2252 onlyWhiteSpace =
false;
2255 while (*current ==
'\n')
2259 appendNewLine =
true;
2262 if (!appendNewLine && size >= allowed)
2264 if (lastSpace != startLine)
2266 current = lastSpace;
2269 appendNewLine =
true;
2274 stringAppend(result, startLine, current);
2275 startLine = current;
2276 lastSpace = current;
2278 if (*previous !=
'\n')
2280 stringAppend(result,
"\n");
2283 stringAppend(result, start,
' ');
2285 if (*previous !=
'\n')
2287 stringAppend(result, lastSpace, current);
2290 onlyWhiteSpace =
true;
2300 if (!onlyWhiteSpace)
2302 stringAppend(result, startLine, previous);
2314 const std::string &group,
2315 std::initializer_list<Option> options
2318 OptionAdder option_adder(*
this, group);
2319 for (
const auto &option: options)
2321 option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
2327Options::add_options(std::string group)
2329 return OptionAdder(*
this, std::move(group));
2334OptionAdder::operator()
2336 const std::string& opts,
2337 const std::string& desc,
2338 const std::shared_ptr<const Value>& value,
2339 std::string arg_help
2342 OptionNames option_names = values::parser_tool::split_option_names(opts);
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; }
2350 auto num_length_1_names = (option_names.end() - first_short_name_iter);
2351 switch(num_length_1_names) {
2353 short_name = *first_short_name_iter;
2354 option_names.erase(first_short_name_iter);
2355 CXXOPTS_FALLTHROUGH;
2359 throw_or_mimic<exceptions::invalid_option_format>(opts);
2362 m_options.add_option
2377OptionParser::parse_default(
const std::shared_ptr<OptionDetails>& details)
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());
2387OptionParser::parse_no_value(
const std::shared_ptr<OptionDetails>& details)
2389 auto&
store = m_parsed[details->hash()];
2390 store.parse_no_value(details);
2395OptionParser::parse_option
2397 const std::shared_ptr<OptionDetails>& value,
2398 const std::string& ,
2399 const std::string& arg
2402 auto hash = value->hash();
2403 auto& result = m_parsed[
hash];
2404 result.parse(value, arg);
2406 m_sequential.emplace_back(value->essential_name(), arg);
2411OptionParser::checked_parse_arg
2414 const char*
const* argv,
2416 const std::shared_ptr<OptionDetails>& value,
2417 const std::string& name
2420 if (current + 1 >= argc)
2422 if (value->value().has_implicit())
2424 parse_option(value, name, value->value().get_implicit_value());
2428 throw_or_mimic<exceptions::missing_argument>(name);
2433 if (value->value().has_implicit())
2435 parse_option(value, name, value->value().get_implicit_value());
2439 parse_option(value, name, argv[current + 1]);
2447OptionParser::add_to_option(
const std::shared_ptr<OptionDetails>& value,
const std::string& arg)
2449 auto hash = value->hash();
2450 auto& result = m_parsed[
hash];
2451 result.add(value, arg);
2453 m_sequential.emplace_back(value->essential_name(), arg);
2458OptionParser::consume_positional(
const std::string& a, PositionalListIterator& next)
2460 while (next != m_positional.end())
2462 auto iter = m_options.find(*next);
2463 if (iter != m_options.end())
2465 if (!iter->second->value().is_container())
2467 auto& result = m_parsed[iter->second->hash()];
2468 if (result.count() == 0)
2470 add_to_option(iter->second, a);
2477 add_to_option(iter->second, a);
2480 throw_or_mimic<exceptions::no_such_option>(*next);
2488Options::parse_positional(std::string option)
2490 parse_positional(std::vector<std::string>{std::move(option)});
2495Options::parse_positional(std::vector<std::string> options)
2497 m_positional = std::move(options);
2499 m_positional_set.insert(m_positional.begin(), m_positional.end());
2504Options::parse_positional(std::initializer_list<std::string> options)
2506 parse_positional(std::vector<std::string>(options));
2511Options::parse(
int argc,
const char*
const* argv)
2513 OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
2515 return parser.parse(argc, argv);
2519OptionParser::parse(
int argc,
const char*
const* argv)
2522 bool consume_remaining =
false;
2523 auto next_positional = m_positional.begin();
2525 std::vector<std::string> unmatched;
2527 while (current != argc)
2529 if (strcmp(argv[current],
"--") == 0)
2531 consume_remaining =
true;
2535 bool matched =
false;
2536 values::parser_tool::ArguDesc argu_desc =
2537 values::parser_tool::ParseArgument(argv[current], matched);
2544 if (argv[current][0] ==
'-' && argv[current][1] !=
'\0') {
2545 if (!m_allow_unrecognised) {
2546 throw_or_mimic<exceptions::invalid_option_syntax>(argv[current]);
2552 if (consume_positional(argv[current], next_positional))
2557 unmatched.emplace_back(argv[current]);
2564 if (argu_desc.grouping)
2566 const std::string& s = argu_desc.arg_name;
2568 for (std::size_t i = 0; i != s.size(); ++i)
2570 std::string name(1, s[i]);
2571 auto iter = m_options.find(name);
2573 if (iter == m_options.end())
2575 if (m_allow_unrecognised)
2577 unmatched.push_back(std::string(
"-") + s[i]);
2581 throw_or_mimic<exceptions::no_such_option>(name);
2584 auto value = iter->second;
2586 if (i + 1 == s.size())
2589 checked_parse_arg(argc, argv, current, value, name);
2591 else if (value->value().has_implicit())
2593 parse_option(value, name, value->value().get_implicit_value());
2595 else if (i + 1 < s.size())
2597 std::string arg_value = s.substr(i + 1);
2598 parse_option(value, name, arg_value);
2604 throw_or_mimic<exceptions::option_requires_argument>(name);
2608 else if (argu_desc.arg_name.length() != 0)
2610 const std::string& name = argu_desc.arg_name;
2612 auto iter = m_options.find(name);
2614 if (iter == m_options.end())
2616 if (m_allow_unrecognised)
2619 unmatched.emplace_back(argv[current]);
2624 throw_or_mimic<exceptions::no_such_option>(name);
2627 auto opt = iter->second;
2630 if (argu_desc.set_value)
2634 parse_option(opt, name, argu_desc.value);
2639 checked_parse_arg(argc, argv, current, opt, name);
2648 for (
auto& opt : m_options)
2650 auto& detail = opt.second;
2651 const auto& value = detail->value();
2653 auto&
store = m_parsed[detail->hash()];
2655 if (value.has_default()) {
2656 if (!
store.count() && !
store.has_default()) {
2657 parse_default(detail);
2661 parse_no_value(detail);
2665 if (consume_remaining)
2667 while (current < argc)
2669 if (!consume_positional(argv[current], next_positional)) {
2676 while (current != argc) {
2677 unmatched.emplace_back(argv[current]);
2684 ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(m_defaults), std::move(unmatched));
2690OptionParser::finalise_aliases()
2692 for (
auto& option: m_options)
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;
2701 m_parsed.emplace(hash, OptionValue());
2709 const std::string& group,
2713 add_options(group, {option});
2720 const std::string& group,
2721 const std::string& s,
2722 const OptionNames& l,
2724 const std::shared_ptr<const Value>& value,
2725 std::string arg_help
2728 auto stringDesc = toLocalString(std::move(desc));
2729 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
2733 add_one_option(s, option);
2736 for(
const auto& long_name : l) {
2737 add_one_option(long_name, option);
2742 if (m_help.find(group) == m_help.end())
2744 m_group.push_back(group);
2747 auto& options = m_help[group];
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()});
2759Options::add_one_option
2761 const std::string& option,
2762 const std::shared_ptr<OptionDetails>& details
2765 auto in = m_options->emplace(option, details);
2769 throw_or_mimic<exceptions::option_already_exists>(option);
2775Options::help_one_group(
const std::string& g)
const
2777 using OptionHelp = std::vector<std::pair<String, String>>;
2779 auto group = m_help.find(g);
2780 if (group == m_help.end())
2787 std::size_t longest = 0;
2793 result += toLocalString(
" " + g +
" options:\n");
2796 for (
const auto& o : group->second.options)
2799 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2805 auto s = format_option(o);
2806 longest = (std::max)(longest, stringLength(s));
2807 format.push_back(std::make_pair(s, String()));
2809 longest = (std::min)(longest, OPTION_LONGEST);
2812 std::size_t allowed = 10;
2813 if (m_width > allowed + longest + OPTION_DESC_GAP)
2815 allowed = m_width - longest - OPTION_DESC_GAP;
2818 auto fiter = format.begin();
2819 for (
const auto& o : group->second.options)
2822 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2828 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
2830 result += fiter->first;
2831 if (stringLength(fiter->first) > longest)
2834 result += toLocalString(std::string(longest + OPTION_DESC_GAP,
' '));
2838 result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2839 stringLength(fiter->first),
2853Options::generate_group_help
2856 const std::vector<std::string>& print_groups
2859 for (std::size_t i = 0; i != print_groups.size(); ++i)
2861 const String& group_help_text = help_one_group(print_groups[i]);
2862 if (empty(group_help_text))
2866 result += group_help_text;
2867 if (i < print_groups.size() - 1)
2876Options::generate_all_groups_help(String& result)
const
2878 generate_group_help(result, m_group);
2883Options::help(
const std::vector<std::string>& help_groups,
bool print_usage)
const
2885 String result = m_help_string;
2888 result+=
"\nUsage:\n " + toLocalString(m_program);
2891 if (!m_custom_help.empty())
2893 result +=
" " + toLocalString(m_custom_help);
2896 if (!m_positional.empty() && !m_positional_help.empty()) {
2897 result +=
" " + toLocalString(m_positional_help);
2902 if (help_groups.empty())
2904 generate_all_groups_help(result);
2908 generate_group_help(result, help_groups);
2911 return toUTF8String(result);
2915std::vector<std::string>
2916Options::groups()
const
2923Options::group_help(
const std::string& group)
const
2925 return m_help.at(group);
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:1140