27#ifndef CXXOPTS_HPP_INCLUDED
28#define CXXOPTS_HPP_INCLUDED
35#include <initializer_list>
42#include <unordered_map>
43#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
71#if __has_include(<optional>)
73#ifdef __cpp_lib_optional
74#define CXXOPTS_HAS_OPTIONAL
77#if __has_include(<filesystem>)
79#ifdef __cpp_lib_filesystem
80#define CXXOPTS_HAS_FILESYSTEM
85#define CXXOPTS_FALLTHROUGH
86#ifdef __has_cpp_attribute
87#if __has_cpp_attribute(fallthrough)
88#undef CXXOPTS_FALLTHROUGH
89#define CXXOPTS_FALLTHROUGH [[fallthrough]]
93#if __cplusplus >= 201603L
94#define CXXOPTS_NODISCARD [[nodiscard]]
96#define CXXOPTS_NODISCARD
99#ifndef CXXOPTS_VECTOR_DELIMITER
100#define CXXOPTS_VECTOR_DELIMITER ','
103#define CXXOPTS__VERSION_MAJOR 3
104#define CXXOPTS__VERSION_MINOR 2
105#define CXXOPTS__VERSION_PATCH 1
107#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
108#define CXXOPTS_NULL_DEREF_IGNORE
112#define DO_PRAGMA(x) _Pragma(#x)
113#define CXXOPTS_DIAGNOSTIC_PUSH DO_PRAGMA(GCC diagnostic push)
114#define CXXOPTS_DIAGNOSTIC_POP DO_PRAGMA(GCC diagnostic pop)
115#define CXXOPTS_IGNORE_WARNING(x) DO_PRAGMA(GCC diagnostic ignored x)
118#define CXXOPTS_DIAGNOSTIC_PUSH
119#define CXXOPTS_DIAGNOSTIC_POP
120#define CXXOPTS_IGNORE_WARNING(x)
123#ifdef CXXOPTS_NO_RTTI
124#define CXXOPTS_RTTI_CAST static_cast
126#define CXXOPTS_RTTI_CAST dynamic_cast
130static constexpr struct {
142#ifdef CXXOPTS_USE_UNICODE
143#include <unicode/unistr.h>
147using String = icu::UnicodeString;
150 return icu::UnicodeString::fromUTF8(std::move(s));
160class UnicodeStringIterator {
162 using iterator_category = std::forward_iterator_tag;
163 using value_type = int32_t;
164 using difference_type = std::ptrdiff_t;
165 using pointer = value_type *;
166 using reference = value_type &;
168 UnicodeStringIterator(
const icu::UnicodeString *
string, int32_t pos)
169 : s(string), i(pos) {}
171 value_type operator*()
const {
return s->char32At(i); }
173 bool operator==(
const UnicodeStringIterator &rhs)
const {
174 return s == rhs.s && i == rhs.i;
177 bool operator!=(
const UnicodeStringIterator &rhs)
const {
178 return !(*
this == rhs);
181 UnicodeStringIterator &operator++() {
186 UnicodeStringIterator operator+(int32_t v) {
187 return UnicodeStringIterator(s, i + v);
191 const icu::UnicodeString *s;
197 return s.append(std::move(a));
201 for (std::size_t i = 0; i != n; ++i) {
208template <
typename Iterator>
210 while (begin != end) {
219 return static_cast<size_t>(s.length());
224 s.toUTF8String(result);
229inline bool empty(
const String &s) {
return s.isEmpty(); }
235inline cxxopts::UnicodeStringIterator begin(
const icu::UnicodeString &s) {
236 return cxxopts::UnicodeStringIterator(&s, 0);
239inline cxxopts::UnicodeStringIterator end(
const icu::UnicodeString &s) {
240 return cxxopts::UnicodeStringIterator(&s, s.length());
254 return std::forward<T>(t);
262 return s.append(n, c);
265template <
typename Iterator>
267 return s.append(begin, end);
272 return std::forward<T>(t);
275inline bool empty(
const std::string &s) {
return s.empty(); }
302 virtual std::shared_ptr<Value>
clone()
const = 0;
304 virtual void add(
const std::string &text)
const = 0;
306 virtual void parse(
const std::string &text)
const = 0;
331namespace exceptions {
335 explicit exception(std::string message) : m_message(std::move(message)) {}
338 const char *
what() const noexcept
override {
return m_message.c_str(); }
341 std::string m_message;
358 " already exists") {}
364 :
specification(
"Invalid option format " + LQUOTE + format + RQUOTE) {}
370 :
parsing(
"Argument " + LQUOTE + text + RQUOTE +
371 " starts with a - but has incorrect syntax") {}
377 :
parsing(
"Option " + LQUOTE + option + RQUOTE +
" does not exist") {}
383 :
parsing(
"Option " + LQUOTE + option + RQUOTE +
384 " is missing an argument") {}
390 :
parsing(
"Option " + LQUOTE + option + RQUOTE +
391 " requires an argument") {}
397 const std::string &arg)
398 :
parsing(
"Option " + LQUOTE + option + RQUOTE +
399 " does not take an argument, but argument " + LQUOTE + arg +
400 RQUOTE +
" given") {}
406 :
parsing(
"Option " + LQUOTE + option + RQUOTE +
" not present") {}
413 ? (
"Option " + LQUOTE + option + RQUOTE +
" has no value")
414 :
"Option has no value") {}
420 :
parsing(
"Argument " + LQUOTE + arg + RQUOTE +
" failed to parse") {}
427 static_assert(std::is_base_of<std::exception, T>::value,
428 "throw_or_mimic only works on std::exception and "
431#ifndef CXXOPTS_NO_EXCEPTIONS
438 std::cerr << exception.
what() << std::endl;
439 std::exit(EXIT_FAILURE);
447namespace parser_tool {
461#ifdef CXXOPTS_NO_REGEX
464 throw_or_mimic<exceptions::incorrect_argument_type>(text);
467 const char *pdata = text.c_str();
472 if (strncmp(pdata,
"0x", 2) == 0) {
476 if (*pdata !=
'\0') {
477 desc.value = std::string(pdata);
479 throw_or_mimic<exceptions::incorrect_argument_type>(text);
484inline bool IsTrueText(
const std::string &text) {
485 const char *pdata = text.c_str();
486 if (*pdata ==
't' || *pdata ==
'T') {
488 if (strncmp(pdata,
"rue\0", 4) == 0) {
491 }
else if (strncmp(pdata,
"1\0", 2) == 0) {
498 const char *pdata = text.c_str();
499 if (*pdata ==
'f' || *pdata ==
'F') {
501 if (strncmp(pdata,
"alse\0", 5) == 0) {
504 }
else if (strncmp(pdata,
"0\0", 2) == 0) {
513 std::string::size_type token_start_pos = 0;
514 auto length = text.length();
517 throw_or_mimic<exceptions::invalid_option_format>(text);
520 while (token_start_pos < length) {
521 const auto &npos = std::string::npos;
522 auto next_non_space_pos = text.find_first_not_of(
' ', token_start_pos);
523 if (next_non_space_pos == npos) {
524 throw_or_mimic<exceptions::invalid_option_format>(text);
526 token_start_pos = next_non_space_pos;
527 auto next_delimiter_pos = text.find(
',', token_start_pos);
528 if (next_delimiter_pos == token_start_pos) {
529 throw_or_mimic<exceptions::invalid_option_format>(text);
531 if (next_delimiter_pos == npos) {
532 next_delimiter_pos = length;
534 auto token_length = next_delimiter_pos - token_start_pos;
537 const char *option_name_valid_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
538 "abcdefghijklmnopqrstuvwxyz"
542 if (!std::isalnum(text[token_start_pos], std::locale::classic()) ||
543 text.find_first_not_of(option_name_valid_chars, token_start_pos) <
544 next_delimiter_pos) {
545 throw_or_mimic<exceptions::invalid_option_format>(text);
548 split_names.emplace_back(text.substr(token_start_pos, token_length));
549 token_start_pos = next_delimiter_pos + 1;
554inline ArguDesc
ParseArgument(
const char *arg,
bool &matched) {
556 const char *pdata = arg;
558 if (strncmp(pdata,
"--", 2) == 0) {
560 if (isalnum(*pdata, std::locale::classic())) {
561 argu_desc.arg_name.push_back(*pdata);
563 while (isalnum(*pdata, std::locale::classic()) || *pdata ==
'-' ||
565 argu_desc.arg_name.push_back(*pdata);
568 if (argu_desc.arg_name.length() > 1) {
570 argu_desc.set_value =
true;
572 if (*pdata !=
'\0') {
573 argu_desc.value = std::string(pdata);
576 }
else if (*pdata ==
'\0') {
581 }
else if (strncmp(pdata,
"-", 1) == 0) {
583 argu_desc.grouping =
true;
584 while (isalnum(*pdata, std::locale::classic())) {
585 argu_desc.arg_name.push_back(*pdata);
588 matched = !argu_desc.arg_name.empty() && *pdata ==
'\0';
597const char *
const integer_pattern =
"(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)";
599const char *
const truthy_pattern =
"(t|T)(rue)?|1";
601const char *
const falsy_pattern =
"(f|F)(alse)?|0";
603const char *
const option_pattern =
604 "--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)";
606const char *
const option_specifier_pattern =
607 "([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*";
609const char *
const option_specifier_separator_pattern =
", *";
614 static const std::basic_regex<char> integer_matcher(integer_pattern);
617 std::regex_match(text, match, integer_matcher);
619 if (match.length() == 0) {
620 throw_or_mimic<exceptions::incorrect_argument_type>(text);
625 desc.
base = match[2];
626 desc.
value = match[3];
628 if (match.length(4) > 0) {
629 desc.
base = match[5];
638 static const std::basic_regex<char> truthy_matcher(truthy_pattern);
640 std::regex_match(text, result, truthy_matcher);
641 return !result.empty();
645 static const std::basic_regex<char> falsy_matcher(falsy_pattern);
647 std::regex_match(text, result, falsy_matcher);
648 return !result.empty();
655 static const std::basic_regex<char> option_specifier_matcher(
656 option_specifier_pattern);
657 if (!std::regex_match(text.c_str(), option_specifier_matcher)) {
658 throw_or_mimic<exceptions::invalid_option_format>(text);
663 static const std::basic_regex<char> option_specifier_separator_matcher(
664 option_specifier_separator_pattern);
665 constexpr int use_non_matches{-1};
666 auto token_iterator = std::sregex_token_iterator(
667 text.begin(), text.end(), option_specifier_separator_matcher,
669 std::copy(token_iterator, std::sregex_token_iterator(),
670 std::back_inserter(split_names));
675 static const std::basic_regex<char> option_matcher(option_pattern);
676 std::match_results<const char *> result;
677 std::regex_match(arg, result, option_matcher);
678 matched = !result.empty();
682 argu_desc.
arg_name = result[1].str();
683 argu_desc.
set_value = result[2].length() > 0;
684 argu_desc.
value = result[3].str();
685 if (result[4].length() > 0) {
687 argu_desc.
arg_name = result[4].str();
695#undef CXXOPTS_NO_REGEX
700template <
typename T,
bool B>
705 template <
typename U>
706 void operator()(
bool negative, U u,
const std::string &text) {
708 if (u >
static_cast<U
>((std::numeric_limits<T>::min)())) {
709 throw_or_mimic<exceptions::incorrect_argument_type>(text);
712 if (u >
static_cast<U
>((std::numeric_limits<T>::max)())) {
713 throw_or_mimic<exceptions::incorrect_argument_type>(text);
721 template <
typename U>
725template <
typename T,
typename U>
732template <
typename R,
typename T>
737 r =
static_cast<R
>(-
static_cast<R
>(t - 1) - 1);
740template <
typename R,
typename T>
742 throw_or_mimic<exceptions::incorrect_argument_type>(text);
749 using US =
typename std::make_unsigned<T>::type;
750 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
752 const bool negative = int_desc.
negative.length() > 0;
753 const uint8_t base = int_desc.
base.length() > 0 ? 16 : 10;
754 const std::string &value_match = int_desc.
value;
758 for (
char ch : value_match) {
761 if (ch >=
'0' && ch <=
'9') {
762 digit =
static_cast<US
>(ch -
'0');
763 }
else if (base == 16 && ch >=
'a' && ch <=
'f') {
764 digit =
static_cast<US
>(ch -
'a' + 10);
765 }
else if (base == 16 && ch >=
'A' && ch <=
'F') {
766 digit =
static_cast<US
>(ch -
'A' + 10);
768 throw_or_mimic<exceptions::incorrect_argument_type>(text);
773 limit =
static_cast<US
>(
774 std::abs(
static_cast<intmax_t
>((std::numeric_limits<T>::min)())));
776 limit = (std::numeric_limits<T>::max)();
779 if (base != 0 && result > limit / base) {
780 throw_or_mimic<exceptions::incorrect_argument_type>(text);
782 if (result * base > limit - digit) {
783 throw_or_mimic<exceptions::incorrect_argument_type>(text);
786 result =
static_cast<US
>(result * base + digit);
789 detail::check_signed_range<T>(negative, result, text);
792 checked_negate<T>(
value, result, text,
793 std::integral_constant<bool, is_signed>());
795 value =
static_cast<T
>(result);
801 std::stringstream in(text);
804 throw_or_mimic<exceptions::incorrect_argument_type>(text);
809 typename std::enable_if<std::is_integral<T>::value>::type * =
nullptr>
825 throw_or_mimic<exceptions::incorrect_argument_type>(text);
835template <typename T, typename std::enable_if<!std::is_integral<T>::value>::type
841#ifdef CXXOPTS_HAS_OPTIONAL
846 value = std::move(result);
850#ifdef CXXOPTS_HAS_FILESYSTEM
851inline void parse_value(
const std::string &text, std::filesystem::path &
value) {
857 if (text.length() != 1) {
858 throw_or_mimic<exceptions::incorrect_argument_type>(text);
869 value.emplace_back(std::move(v));
872 std::stringstream in(text);
877 value.emplace_back(std::move(v));
890 value.emplace_back(std::move(v));
895 static constexpr bool value =
false;
930 void add(
const std::string &text)
const override {
934 void parse(
const std::string &text)
const override {
949 return shared_from_this();
955 return shared_from_this();
960 return shared_from_this();
967 bool is_boolean()
const override {
return std::is_same<T, bool>::value; }
993 std::shared_ptr<Value>
clone()
const override {
994 return std::make_shared<standard_value<T>>(*this);
1010 std::shared_ptr<Value>
clone()
const override {
1011 return std::make_shared<standard_value<bool>>(*this);
1015 void set_default_and_implicit() {
1025template <
typename T>
1027 return std::make_shared<values::standard_value<T>>();
1030template <
typename T>
1032 return std::make_shared<values::standard_value<T>>(&t);
1039 static const std::string
empty{
""};
1040 return long_names.empty() ?
empty : long_names.front();
1046 std::shared_ptr<const Value> val)
1047 : m_short(std::move(short_)), m_long(std::move(long_)),
1048 m_desc(std::move(desc)), m_value(std::move(val)), m_count(0) {
1053 : m_desc(rhs.m_desc), m_value(rhs.m_value->clone()),
1054 m_count(rhs.m_count) {}
1075 return m_long.empty() ? m_short : m_long.front();
1081 std::size_t
hash()
const {
return m_hash; }
1084 std::string m_short{};
1087 std::shared_ptr<const Value> m_value{};
1090 std::size_t m_hash{};
1114 void add(
const std::shared_ptr<const OptionDetails> &details,
1115 const std::string &text) {
1116 ensure_value(details);
1119 m_long_names = &details->long_names();
1122 void parse(
const std::shared_ptr<const OptionDetails> &details,
1123 const std::string &text) {
1124 ensure_value(details);
1126 m_value->parse(text);
1127 m_long_names = &details->long_names();
1131 ensure_value(details);
1133 m_long_names = &details->long_names();
1138 m_long_names = &details->long_names();
1141#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1147 std::size_t
count() const noexcept {
return m_count; }
1149#if defined(CXXOPTS_NULL_DEREF_IGNORE)
1157 template <
typename T>
1159 if (m_value ==
nullptr) {
1160 throw_or_mimic<exceptions::option_has_no_value>(
1164 return CXXOPTS_RTTI_CAST<const values::standard_value<T> &>(*m_value).get();
1167#ifdef CXXOPTS_HAS_OPTIONAL
1168 template <
typename T>
1169 std::optional<T> as_optional()
const {
1170 if (m_value ==
nullptr) {
1171 return std::nullopt;
1178 void ensure_value(
const std::shared_ptr<const OptionDetails> &details) {
1179 if (m_value ==
nullptr) {
1180 m_value = details->make_storage();
1187 std::shared_ptr<Value> m_value{};
1188 std::size_t m_count = 0;
1189 bool m_default =
false;
1194 KeyValue(std::string key_, std::string value_) noexcept
1195 : m_key(std::move(key_)), m_value(std::move(value_)) {}
1198 const std::string &
key()
const {
return m_key; }
1201 const std::string &
value()
const {
return m_value; }
1203 template <
typename T>
1212 std::string m_value;
1237 m_sequential =
false;
1238 m_iter = m_pr->m_defaults.end();
1240 m_sequential =
true;
1241 m_iter = m_pr->m_sequential.begin();
1243 if (m_iter == m_pr->m_sequential.end()) {
1244 m_sequential =
false;
1245 m_iter = m_pr->m_defaults.begin();
1253 if (m_sequential && m_iter == m_pr->m_sequential.end()) {
1254 m_sequential =
false;
1255 m_iter = m_pr->m_defaults.begin();
1268 return (m_sequential == other.m_sequential) && (m_iter == other.m_iter);
1279 std::vector<KeyValue>::const_iterator m_iter;
1280 bool m_sequential =
true;
1287 std::vector<KeyValue> sequential,
1288 std::vector<KeyValue> default_opts,
1289 std::vector<std::string> &&unmatched_args)
1290 : m_keys(std::move(keys)), m_values(std::move(values)),
1291 m_sequential(std::move(sequential)),
1292 m_defaults(std::move(default_opts)),
1293 m_unmatched(std::move(unmatched_args)) {}
1302 std::size_t
count(
const std::string &o)
const {
1303 auto iter = m_keys.find(o);
1304 if (iter == m_keys.end()) {
1308 auto viter = m_values.find(iter->second);
1310 if (viter == m_values.end()) {
1314 return viter->second.count();
1318 return static_cast<bool>(
count(o));
1322 auto iter = m_keys.find(option);
1324 if (iter == m_keys.end()) {
1325 throw_or_mimic<exceptions::requested_option_not_present>(option);
1328 auto viter = m_values.find(iter->second);
1330 if (viter == m_values.end()) {
1331 throw_or_mimic<exceptions::requested_option_not_present>(option);
1334 return viter->second;
1337#ifdef CXXOPTS_HAS_OPTIONAL
1338 template <
typename T>
1339 std::optional<T> as_optional(
const std::string &option)
const {
1340 auto iter = m_keys.find(option);
1341 if (iter != m_keys.end()) {
1342 auto viter = m_values.find(iter->second);
1343 if (viter != m_values.end()) {
1344 return viter->second.as_optional<T>();
1347 return std::nullopt;
1351 const std::vector<KeyValue> &
arguments()
const {
return m_sequential; }
1353 const std::vector<std::string> &
unmatched()
const {
return m_unmatched; }
1355 const std::vector<KeyValue> &
defaults()
const {
return m_defaults; }
1359 for (
const auto &kv : m_sequential) {
1360 result += kv.key() +
" = " + kv.value() +
"\n";
1362 for (
const auto &kv : m_defaults) {
1363 result += kv.key() +
" = " + kv.value() +
" " +
"(default)" +
"\n";
1371 std::vector<KeyValue> m_sequential{};
1372 std::vector<KeyValue> m_defaults{};
1373 std::vector<std::string> m_unmatched{};
1378 std::shared_ptr<const Value>
value = ::cxxopts::value<bool>(),
1379 std::string arg_help =
"")
1380 :
opts_(std::move(opts)),
desc_(std::move(desc)),
1390 std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
1397 bool allow_unrecognised)
1398 : m_options(options), m_positional(positional),
1399 m_allow_unrecognised(allow_unrecognised) {}
1406 const std::shared_ptr<OptionDetails> &
value,
1407 const std::string &name);
1410 const std::string &arg);
1413 const std::string &name,
const std::string &arg =
"");
1415 void parse_default(
const std::shared_ptr<OptionDetails> &details);
1417 void parse_no_value(
const std::shared_ptr<OptionDetails> &details);
1420 void finalise_aliases();
1425 std::vector<KeyValue> m_sequential{};
1426 std::vector<KeyValue> m_defaults{};
1427 bool m_allow_unrecognised;
1435 explicit Options(std::string program_name, std::string help_string =
"")
1436 : m_program(std::move(program_name)),
1438 m_custom_help(
"[OPTION...]"),
1439 m_positional_help(
"positional parameters"), m_show_positional(false),
1440 m_allow_unrecognised(false), m_width(76), m_tab_expansion(false),
1441 m_options(std::make_shared<
OptionMap>()) {}
1444 m_positional_help = std::move(help_text);
1449 m_custom_help = std::move(help_text);
1454 m_show_positional =
true;
1459 m_allow_unrecognised =
true;
1469 m_tab_expansion = expansion;
1478 std::initializer_list<Option> options);
1482 void add_option(
const std::string &group,
const std::string &s,
1484 const std::shared_ptr<const Value> &
value,
1485 std::string arg_help);
1487 void add_option(
const std::string &group,
const std::string &short_name,
1488 const std::string &single_long_name, std::string desc,
1489 const std::shared_ptr<const Value> &
value,
1490 std::string arg_help) {
1492 long_names.emplace_back(single_long_name);
1503 template <
typename Iterator>
1508 std::string
help(
const std::vector<std::string> &
groups = {},
1509 bool print_usage =
true)
const;
1511 std::vector<std::string>
groups()
const;
1513 const HelpGroupDetails &
group_help(
const std::string &group)
const;
1515 const std::string &
program()
const {
return m_program; }
1518 void add_one_option(
const std::string &option,
1519 const std::shared_ptr<OptionDetails> &details);
1521 String help_one_group(
const std::string &group)
const;
1523 void generate_group_help(
String &result,
1524 const std::vector<std::string> &
groups)
const;
1526 void generate_all_groups_help(
String &result)
const;
1528 std::string m_program{};
1530 std::string m_custom_help{};
1531 std::string m_positional_help{};
1532 bool m_show_positional;
1533 bool m_allow_unrecognised;
1534 std::size_t m_width;
1535 bool m_tab_expansion;
1537 std::shared_ptr<OptionMap> m_options;
1538 std::vector<std::string> m_positional{};
1539 std::unordered_set<std::string> m_positional_set{};
1542 std::vector<std::string> m_group{};
1543 std::map<std::string, HelpGroupDetails> m_help{};
1549 : m_options(options), m_group(std::move(group)) {}
1552 const std::string &opts,
const std::string &desc,
1553 const std::shared_ptr<const Value> &
value = ::cxxopts::value<bool>(),
1554 std::string arg_help =
"");
1558 std::string m_group;
1562constexpr std::size_t OPTION_LONGEST = 30;
1563constexpr std::size_t OPTION_DESC_GAP = 2;
1565String format_option(
const HelpOptionDetails &o) {
1566 const auto &s = o.s;
1584 auto arg = !o.arg_help.empty() ?
toLocalString(o.arg_help) :
"arg";
1586 if (!o.is_boolean) {
1587 if (o.has_implicit) {
1588 result +=
" [=" + arg +
"(=" +
toLocalString(o.implicit_value) +
")]";
1590 result +=
" " + arg;
1597String format_description(
const HelpOptionDetails &o, std::size_t start,
1598 std::size_t allowed,
bool tab_expansion) {
1601 if (o.has_default && (!o.is_boolean || o.default_value !=
"false")) {
1602 if (!o.default_value.empty()) {
1603 desc +=
toLocalString(
" (default: " + o.default_value +
")");
1611 if (tab_expansion) {
1613 auto size = std::size_t{0};
1614 for (
auto c = std::begin(desc); c != std::end(desc); ++c) {
1618 }
else if (*c ==
'\t') {
1619 auto skip = 8 - size % 8;
1632 auto current = std::begin(desc);
1633 auto previous = current;
1634 auto startLine = current;
1635 auto lastSpace = current;
1637 auto size = std::size_t{};
1640 bool onlyWhiteSpace =
true;
1642 while (current != std::end(desc)) {
1643 appendNewLine =
false;
1644 if (*previous ==
' ' || *previous ==
'\t') {
1645 lastSpace = current;
1647 if (*current !=
' ' && *current !=
'\t') {
1648 onlyWhiteSpace =
false;
1651 while (*current ==
'\n') {
1654 appendNewLine =
true;
1657 if (!appendNewLine && size >= allowed) {
1658 if (lastSpace != startLine) {
1659 current = lastSpace;
1662 appendNewLine =
true;
1665 if (appendNewLine) {
1667 startLine = current;
1668 lastSpace = current;
1670 if (*previous !=
'\n') {
1676 if (*previous !=
'\n') {
1680 onlyWhiteSpace =
true;
1690 if (!onlyWhiteSpace) {
1700 std::initializer_list<Option> options) {
1702 for (
const auto &option : options) {
1703 option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
1713 const std::shared_ptr<const Value> &
value,
1714 std::string arg_help) {
1718 std::string short_name{
""};
1719 auto first_short_name_iter = std::partition(
1720 option_names.begin(), option_names.end(),
1721 [&](
const std::string &name) { return name.length() > 1; });
1722 auto num_length_1_names = (option_names.end() - first_short_name_iter);
1723 switch (num_length_1_names) {
1725 short_name = *first_short_name_iter;
1726 option_names.erase(first_short_name_iter);
1731 throw_or_mimic<exceptions::invalid_option_format>(opts);
1735 std::move(arg_help));
1743 auto &store = m_parsed[details->hash()];
1744 store.parse_default(details);
1745 m_defaults.emplace_back(details->essential_name(),
1746 details->value().get_default_value());
1751 auto &store = m_parsed[details->hash()];
1752 store.parse_no_value(details);
1757 const std::string & ,
1758 const std::string &arg) {
1759 auto hash =
value->hash();
1760 auto &result = m_parsed[hash];
1761 result.parse(
value, arg);
1763 m_sequential.emplace_back(
value->essential_name(), arg);
1768 const std::shared_ptr<OptionDetails> &
value,
1769 const std::string &name) {
1770 if (current + 1 >= argc) {
1771 if (
value->value().has_implicit()) {
1774 throw_or_mimic<exceptions::missing_argument>(name);
1777 if (
value->value().has_implicit()) {
1788 const std::string &arg) {
1789 auto hash =
value->hash();
1790 auto &result = m_parsed[hash];
1791 result.add(
value, arg);
1793 m_sequential.emplace_back(
value->essential_name(), arg);
1798 while (next != m_positional.end()) {
1799 auto iter = m_options.find(*next);
1800 if (iter != m_options.end()) {
1801 if (!iter->second->value().is_container()) {
1802 auto &result = m_parsed[iter->second->hash()];
1803 if (result.count() == 0) {
1814 throw_or_mimic<exceptions::no_such_option>(*next);
1825 m_positional = std::move(options);
1827 m_positional_set.insert(m_positional.begin(), m_positional.end());
1836 OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
1838 return parser.
parse(argc, argv);
1843 bool consume_remaining =
false;
1844 auto next_positional = m_positional.begin();
1846 std::vector<std::string> unmatched;
1848 while (current != argc) {
1849 if (strcmp(argv[current],
"--") == 0) {
1850 consume_remaining =
true;
1854 bool matched =
false;
1862 if (argv[current][0] ==
'-' && argv[current][1] !=
'\0') {
1863 if (!m_allow_unrecognised) {
1864 throw_or_mimic<exceptions::invalid_option_syntax>(argv[current]);
1872 unmatched.emplace_back(argv[current]);
1878 const std::string &s = argu_desc.
arg_name;
1880 for (std::size_t i = 0; i != s.size(); ++i) {
1881 std::string name(1, s[i]);
1882 auto iter = m_options.find(name);
1884 if (iter == m_options.end()) {
1885 if (m_allow_unrecognised) {
1886 unmatched.push_back(std::string(
"-") + s[i]);
1890 throw_or_mimic<exceptions::no_such_option>(name);
1893 auto value = iter->second;
1895 if (i + 1 == s.size()) {
1898 }
else if (
value->value().has_implicit()) {
1900 }
else if (i + 1 < s.size()) {
1901 std::string arg_value = s.substr(i + 1);
1906 throw_or_mimic<exceptions::option_requires_argument>(name);
1909 }
else if (argu_desc.
arg_name.length() != 0) {
1910 const std::string &name = argu_desc.
arg_name;
1912 auto iter = m_options.find(name);
1914 if (iter == m_options.end()) {
1915 if (m_allow_unrecognised) {
1917 unmatched.emplace_back(argv[current]);
1922 throw_or_mimic<exceptions::no_such_option>(name);
1925 auto opt = iter->second;
1942 for (
auto &opt : m_options) {
1943 auto &detail = opt.second;
1944 const auto &
value = detail->value();
1946 auto &store = m_parsed[detail->hash()];
1948 if (
value.has_default()) {
1949 if (!store.count() && !store.has_default()) {
1957 if (consume_remaining) {
1958 while (current < argc) {
1966 while (current != argc) {
1967 unmatched.emplace_back(argv[current]);
1974 ParseResult parsed(std::move(m_keys), std::move(m_parsed),
1975 std::move(m_sequential), std::move(m_defaults),
1976 std::move(unmatched));
1980inline void OptionParser::finalise_aliases() {
1981 for (
auto &option : m_options) {
1982 auto &detail = *option.second;
1983 auto hash = detail.hash();
1984 m_keys[detail.short_name()] = hash;
1985 for (
const auto &long_name : detail.long_names()) {
1986 m_keys[long_name] = hash;
1989 m_parsed.emplace(hash, OptionValue());
2000 const std::shared_ptr<const Value> &
value,
2001 std::string arg_help) {
2003 auto option = std::make_shared<OptionDetails>(s, l, stringDesc,
value);
2006 add_one_option(s, option);
2009 for (
const auto &long_name : l) {
2010 add_one_option(long_name, option);
2015 if (m_help.find(group) == m_help.end()) {
2016 m_group.push_back(group);
2019 auto &options = m_help[group];
2023 value->has_implicit(),
value->get_implicit_value(), std::move(arg_help),
2024 value->is_container(),
value->is_boolean()});
2028Options::add_one_option(
const std::string &option,
2029 const std::shared_ptr<OptionDetails> &details) {
2030 auto in = m_options->emplace(option, details);
2033 throw_or_mimic<exceptions::option_already_exists>(option);
2037inline String Options::help_one_group(
const std::string &g)
const {
2038 using OptionHelp = std::vector<std::pair<String, String>>;
2040 auto group = m_help.find(g);
2041 if (group == m_help.end()) {
2047 std::size_t longest = 0;
2055 for (
const auto &o : group->second.options) {
2057 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2058 !m_show_positional) {
2062 auto s = format_option(o);
2064 format.push_back(std::make_pair(s,
String()));
2066 longest = (std::min)(longest, OPTION_LONGEST);
2069 std::size_t allowed = 10;
2070 if (m_width > allowed + longest + OPTION_DESC_GAP) {
2071 allowed = m_width - longest - OPTION_DESC_GAP;
2074 auto fiter = format.begin();
2075 for (
const auto &o : group->second.options) {
2077 m_positional_set.find(o.l.front()) != m_positional_set.end() &&
2078 !m_show_positional) {
2082 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed,
2085 result += fiter->first;
2088 result +=
toLocalString(std::string(longest + OPTION_DESC_GAP,
' '));
2091 longest + OPTION_DESC_GAP -
stringLength(fiter->first),
' '));
2102inline void Options::generate_group_help(
2103 String &result,
const std::vector<std::string> &print_groups)
const {
2104 for (std::size_t i = 0; i != print_groups.size(); ++i) {
2105 const String &group_help_text = help_one_group(print_groups[i]);
2106 if (
empty(group_help_text)) {
2109 result += group_help_text;
2110 if (i < print_groups.size() - 1) {
2116inline void Options::generate_all_groups_help(
String &result)
const {
2117 generate_group_help(result, m_group);
2121 bool print_usage)
const {
2122 String result = m_help_string;
2127 if (!m_custom_help.empty()) {
2131 if (!m_positional.empty() && !m_positional_help.empty()) {
2137 if (help_groups.empty()) {
2138 generate_all_groups_help(result);
2140 generate_group_help(result, help_groups);
2150 return m_help.at(group);
CXXOPTS_NODISCARD const std::string & key() const
KeyValue(std::string key_, std::string value_) noexcept
CXXOPTS_NODISCARD const std::string & value() const
OptionAdder & operator()(const std::string &opts, const std::string &desc, const std::shared_ptr< const Value > &value=::cxxopts::value< bool >(), std::string arg_help="")
OptionAdder(Options &options, std::string group)
CXXOPTS_NODISCARD const std::string & essential_name() const
CXXOPTS_NODISCARD const OptionNames & long_names() const
CXXOPTS_NODISCARD const Value & value() const
CXXOPTS_NODISCARD std::shared_ptr< Value > make_storage() const
CXXOPTS_NODISCARD const String & description() const
OptionDetails(const OptionDetails &rhs)
OptionDetails(std::string short_, OptionNames long_, String desc, std::shared_ptr< const Value > val)
CXXOPTS_NODISCARD const std::string & first_long_name() const
CXXOPTS_NODISCARD const std::string & short_name() const
OptionDetails(OptionDetails &&rhs)=default
void parse_option(const std::shared_ptr< OptionDetails > &value, const std::string &name, const std::string &arg="")
OptionParser(const OptionMap &options, const PositionalList &positional, bool allow_unrecognised)
void checked_parse_arg(int argc, const char *const *argv, int ¤t, const std::shared_ptr< OptionDetails > &value, const std::string &name)
void parse_default(const std::shared_ptr< OptionDetails > &details)
ParseResult parse(int argc, const char *const *argv)
bool consume_positional(const std::string &a, PositionalListIterator &next)
void add_to_option(const std::shared_ptr< OptionDetails > &value, const std::string &arg)
void parse_no_value(const std::shared_ptr< OptionDetails > &details)
void parse_default(const std::shared_ptr< const OptionDetails > &details)
void parse(const std::shared_ptr< const OptionDetails > &details, const std::string &text)
void parse_no_value(const std::shared_ptr< const OptionDetails > &details)
CXXOPTS_NODISCARD std::size_t count() const noexcept
CXXOPTS_NODISCARD bool has_default() const noexcept
void add(const std::shared_ptr< const OptionDetails > &details, const std::string &text)
std::string help(const std::vector< std::string > &groups={}, bool print_usage=true) const
void parse_positional(std::string option)
Options & set_width(std::size_t width)
void parse_positional(Iterator begin, Iterator end)
ParseResult parse(int argc, const char *const *argv)
Options & show_positional_help()
std::vector< std::string > groups() const
Options(std::string program_name, std::string help_string="")
OptionAdder add_options(std::string group="")
Options & set_tab_expansion(bool expansion=true)
Options & allow_unrecognised_options()
const std::string & program() const
const HelpGroupDetails & group_help(const std::string &group) const
void add_option(const std::string &group, const Option &option)
void add_option(const std::string &group, const std::string &short_name, const std::string &single_long_name, std::string desc, const std::shared_ptr< const Value > &value, std::string arg_help)
Options & custom_help(std::string help_text)
Options & positional_help(std::string help_text)
std::forward_iterator_tag iterator_category
bool operator!=(const Iterator &other) const
Iterator(const Iterator &)=default
bool operator==(const Iterator &other) const
CXXOPTS_DIAGNOSTIC_POP Iterator & operator++()
const KeyValue & operator*()
const KeyValue * operator->()
ParseResult(NameHashMap &&keys, ParsedHashMap &&values, std::vector< KeyValue > sequential, std::vector< KeyValue > default_opts, std::vector< std::string > &&unmatched_args)
ParseResult(const ParseResult &)=default
ParseResult & operator=(ParseResult &&)=default
std::size_t count(const std::string &o) const
bool contains(const std::string &o) const
const OptionValue & operator[](const std::string &option) const
const std::vector< KeyValue > & arguments() const
const std::vector< std::string > & unmatched() const
const std::vector< KeyValue > & defaults() const
const std::string arguments_string() const
ParseResult & operator=(const ParseResult &)=default
virtual std::shared_ptr< Value > clone() const =0
virtual std::string get_default_value() const =0
virtual void parse(const std::string &text) const =0
virtual std::string get_implicit_value() const =0
virtual std::shared_ptr< Value > no_implicit_value()=0
virtual bool is_container() const =0
virtual bool is_boolean() const =0
virtual void parse() const =0
virtual std::shared_ptr< Value > default_value(const std::string &value)=0
virtual bool has_default() const =0
virtual bool has_implicit() const =0
virtual void add(const std::string &text) const =0
virtual std::shared_ptr< Value > implicit_value(const std::string &value)=0
exception(std::string message)
CXXOPTS_NODISCARD const char * what() const noexcept override
gratuitous_argument_for_option(const std::string &option, const std::string &arg)
incorrect_argument_type(const std::string &arg)
invalid_option_syntax(const std::string &text)
missing_argument(const std::string &option)
no_such_option(const std::string &option)
option_already_exists(const std::string &option)
option_has_no_value(const std::string &option)
option_requires_argument(const std::string &option)
parsing(const std::string &message)
requested_option_not_present(const std::string &option)
specification(const std::string &message)
std::shared_ptr< T > m_result
std::string m_default_value
bool is_container() const override
std::shared_ptr< Value > no_implicit_value() override
std::shared_ptr< Value > default_value(const std::string &value) override
abstract_value(const abstract_value &rhs)
std::string get_implicit_value() const override
std::string m_implicit_value
bool has_default() const override
std::string get_default_value() const override
void parse() const override
std::shared_ptr< Value > implicit_value(const std::string &value) override
bool is_boolean() const override
void parse(const std::string &text) const override
void add(const std::string &text) const override
~abstract_value() override=default
abstract_value & operator=(const abstract_value &)=default
bool has_implicit() const override
std::shared_ptr< Value > clone() const override
~standard_value() override=default
CXXOPTS_NODISCARD std::shared_ptr< Value > clone() const override
#define CXXOPTS__VERSION_MAJOR
#define CXXOPTS_VECTOR_DELIMITER
#define CXXOPTS_NODISCARD
#define CXXOPTS_FALLTHROUGH
#define CXXOPTS_IGNORE_WARNING(x)
#define CXXOPTS__VERSION_MINOR
#define CXXOPTS__VERSION_PATCH
#define CXXOPTS_LINKONCE_CONST
#define CXXOPTS_DIAGNOSTIC_POP
#define CXXOPTS_DIAGNOSTIC_PUSH
void check_signed_range(bool negative, U value, const std::string &text)
void integer_parser(const std::string &text, T &value)
void checked_negate(R &r, T &&t, const std::string &, std::true_type)
void stringstream_parser(const std::string &text, T &value)
void parse_value(const std::string &text, T &value)
void add_value(const std::string &text, T &value)
std::string toUTF8String(T &&t)
bool empty(const std::string &s)
void throw_or_mimic(const std::string &text)
std::vector< std::string > OptionNames
CXXOPTS_NODISCARD const std::string & first_or_empty(const OptionNames &long_names)
std::size_t stringLength(const String &s)
std::unordered_map< std::size_t, OptionValue > ParsedHashMap
std::shared_ptr< Value > value()
PositionalList::const_iterator PositionalListIterator
std::vector< std::string > PositionalList
std::unordered_map< std::string, std::size_t > NameHashMap
std::unordered_map< std::string, std::shared_ptr< OptionDetails > > OptionMap
String & stringAppend(String &s, const String &a)
bool operator!=(const Port &rhs) const
bool operator==(const Port &rhs) const
std::vector< HelpOptionDetails > options
std::string implicit_value
std::string default_value
std::shared_ptr< const Value > value_
Option(std::string opts, std::string desc, std::shared_ptr< const Value > value=::cxxopts::value< bool >(), std::string arg_help="")
void operator()(bool, U, const std::string &) const
void operator()(bool negative, U u, const std::string &text)
static constexpr bool value