25 #ifndef CXXOPTS_HPP_INCLUDED
26 #define CXXOPTS_HPP_INCLUDED
37 #include <unordered_map>
38 #include <unordered_set>
41 #ifdef __cpp_lib_optional
43 #define CXXOPTS_HAS_OPTIONAL
46 #define CXXOPTS__VERSION_MAJOR 2
47 #define CXXOPTS__VERSION_MINOR 2
48 #define CXXOPTS__VERSION_PATCH 0
52 static constexpr
struct
64 #ifdef CXXOPTS_USE_UNICODE
65 #include <unicode/unistr.h>
69 typedef icu::UnicodeString
String;
74 return icu::UnicodeString::fromUTF8(std::move(
s));
77 class UnicodeStringIterator :
public std::iterator<std::forward_iterator_tag, int32_t>
80 UnicodeStringIterator(
const icu::UnicodeString*
string, int32_t pos) :
s(string), i(pos)
87 return s->char32At(i);
91 operator==(
const UnicodeStringIterator& rhs)
const
93 return s == rhs.s && i == rhs.i;
97 operator!=(
const UnicodeStringIterator& rhs)
const
99 return !(*
this == rhs);
102 UnicodeStringIterator&
109 UnicodeStringIterator
112 return UnicodeStringIterator(
s, i +
v);
116 const icu::UnicodeString*
s;
123 return s.append(std::move(
a));
129 for (
int i = 0; i != n; ++i)
137 template <
typename Iterator>
160 s.toUTF8String(result);
174 inline cxxopts::UnicodeStringIterator
175 begin(
const icu::UnicodeString&
s)
177 return cxxopts::UnicodeStringIterator(&
s, 0);
180 inline cxxopts::UnicodeStringIterator
181 end(
const icu::UnicodeString&
s)
183 return cxxopts::UnicodeStringIterator(&
s,
s.length());
194 template <
typename T>
198 return std::forward<T>(t);
210 return s.append(std::move(
a));
216 return s.append(n,
c);
219 template <
typename Iterator>
223 return s.append(begin, end);
226 template <
typename T>
230 return std::forward<T>(t);
248 const std::string LQUOTE(
"\'");
249 const std::string RQUOTE(
"\'");
251 const std::string LQUOTE(
"‘");
252 const std::string RQUOTE(
"’");
256 class Value :
public std::enable_shared_from_this<Value>
259 virtual ~Value() =
default;
261 virtual std::shared_ptr<Value>
clone()
const = 0;
263 virtual void parse(
const std::string& text)
const = 0;
265 virtual void parse()
const = 0;
294 return m_message.c_str();
298 std::string m_message;
340 " starts with a - but has incorrect syntax")
377 " does not take an argument, but argument " + LQUOTE + arg +
406 " is required but not present")
415 std::basic_regex<char> integer_pattern(
"(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
416 std::basic_regex<char> truthy_pattern(
"(t|T)(rue)?");
417 std::basic_regex<char> falsy_pattern(
"((f|F)(alse)?)?");
422 template <
typename T,
bool B>
425 template <
typename T>
428 template <
typename U>
449 template <
typename T>
452 template <
typename U>
459 template <
typename T,
typename U>
467 template <
typename R,
typename T>
474 return -
static_cast<R
>(t);
477 template <
typename R,
typename T>
484 template <
typename T>
489 std::regex_match(text, match, integer_pattern);
491 if (match.length() == 0)
496 if (match.length(4) > 0)
502 using US =
typename std::make_unsigned<T>::type;
505 constexpr
bool is_signed = std::numeric_limits<T>::is_signed;
506 const bool negative = match.length(1) > 0;
507 const uint8_t base = match.length(2) > 0 ? 16 : 10;
509 auto value_match = match[3];
513 for (
auto iter = value_match.first; iter != value_match.second; ++iter)
517 if (*iter >=
'0' && *iter <=
'9')
521 else if (base == 16 && *iter >=
'a' && *iter <=
'f')
523 digit = *iter -
'a' + 10;
525 else if (base == 16 && *iter >=
'A' && *iter <=
'F')
527 digit = *iter -
'A' + 10;
534 if (umax - digit < result * base)
539 result = result * base + digit;
542 detail::check_signed_range<T>(negative, result, text);
546 value = checked_negate<T>(result, text, std::integral_constant<bool, is_signed>());
554 template <
typename T>
558 std::stringstream in(text);
618 std::regex_match(text, result, truthy_pattern);
626 std::regex_match(text, result, falsy_pattern);
645 template <
typename T>
652 template <
typename T>
661 #ifdef CXXOPTS_HAS_OPTIONAL
662 template <
typename T>
668 value = std::move(result);
672 template <
typename T>
675 static constexpr
bool value =
false;
678 template <
typename T>
684 template <
typename T>
719 parse(
const std::string& text)
const
748 std::shared_ptr<Value>
753 return shared_from_this();
756 std::shared_ptr<Value>
761 return shared_from_this();
806 template <
typename T>
812 std::shared_ptr<Value>
815 return std::make_shared<standard_value<T>>(*this);
827 set_default_and_implicit();
832 set_default_and_implicit();
835 std::shared_ptr<Value>
838 return std::make_shared<standard_value<bool>>(*this);
843 set_default_and_implicit()
853 template <
typename T>
854 std::shared_ptr<Value>
857 return std::make_shared<values::standard_value<T>>();
860 template <
typename T>
861 std::shared_ptr<Value>
864 return std::make_shared<values::standard_value<T>>(&t);
873 const std::string& long_,
875 std::shared_ptr<const Value> val) :
876 m_short(short_), m_long(long_), m_desc(desc), m_value(val), m_count(0)
882 m_value = rhs.m_value->clone();
899 std::shared_ptr<Value>
902 return m_value->clone();
921 std::shared_ptr<const Value> m_value;
950 parse(std::shared_ptr<const OptionDetails> details,
const std::string& text)
952 ensure_value(details);
954 m_value->parse(text);
960 ensure_value(details);
970 template <
typename T>
974 if (m_value ==
nullptr)
976 throw std::domain_error(
"No value");
979 #ifdef CXXOPTS_NO_RTTI
988 ensure_value(std::shared_ptr<const OptionDetails> details)
990 if (m_value ==
nullptr)
992 m_value = details->make_storage();
996 std::shared_ptr<Value> m_value;
1004 m_key(
std::move(key_)), m_value(
std::move(value_))
1020 template <
typename T>
1031 std::string m_value;
1038 const std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<OptionDetails>>>,
1039 std::vector<std::string>,
1040 bool allow_unrecognised,
1047 auto iter = m_options->find(o);
1048 if (iter == m_options->end())
1053 auto riter = m_results.find(iter->second);
1055 return riter->second.count();
1061 auto iter = m_options->find(
option);
1063 if (iter == m_options->end())
1068 auto riter = m_results.find(iter->second);
1070 return riter->second;
1073 const std::vector<KeyValue>&
1076 return m_sequential;
1080 void parse(
int& argc,
char**& argv);
1082 void add_to_option(
const std::string&
option,
const std::string& arg);
1084 bool consume_positional(std::string
a);
1086 void parse_option(std::shared_ptr<OptionDetails>
value,
1087 const std::string& name,
1088 const std::string& arg =
"");
1090 void parse_default(std::shared_ptr<OptionDetails> details);
1092 void checked_parse_arg(
int argc,
1095 std::shared_ptr<OptionDetails>
value,
1096 const std::string& name);
1098 const std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<OptionDetails>>>
1100 std::vector<std::string> m_positional;
1101 std::vector<std::string>::iterator m_next_positional;
1102 std::unordered_set<std::string> m_positional_set;
1103 std::unordered_map<std::shared_ptr<OptionDetails>,
OptionValue> m_results;
1105 bool m_allow_unrecognised;
1107 std::vector<KeyValue> m_sequential;
1112 typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>> OptionMap;
1115 Options(std::string program, std::string help_string =
"") :
1116 m_program(
std::move(program)),
1118 m_custom_help(
"[OPTION...]"),
1119 m_positional_help(
"positional parameters"),
1120 m_show_positional(false),
1121 m_allow_unrecognised(false),
1123 m_next_positional(m_positional.end())
1130 m_positional_help = std::move(help_text);
1137 m_custom_help = std::move(help_text);
1144 m_show_positional =
true;
1151 m_allow_unrecognised =
true;
1160 const std::string&
s,
1161 const std::string& l,
1163 std::shared_ptr<const Value>
value,
1164 std::string arg_help);
1173 template <
typename Iterator>
1180 std::string
help(
const std::vector<std::string>&
groups = {})
const;
1182 const std::vector<std::string>
groups()
const;
1184 const HelpGroupDetails&
group_help(
const std::string& group)
const;
1187 void add_one_option(
const std::string&
option, std::shared_ptr<OptionDetails> details);
1189 String help_one_group(
const std::string& group)
const;
1191 void generate_group_help(
String& result,
const std::vector<std::string>&
groups)
const;
1193 void generate_all_groups_help(
String& result)
const;
1195 std::string m_program;
1197 std::string m_custom_help;
1198 std::string m_positional_help;
1199 bool m_show_positional;
1200 bool m_allow_unrecognised;
1202 std::shared_ptr<OptionMap> m_options;
1203 std::vector<std::string> m_positional;
1204 std::vector<std::string>::iterator m_next_positional;
1205 std::unordered_set<std::string> m_positional_set;
1208 std::map<std::string, HelpGroupDetails> m_help;
1215 m_options(options), m_group(
std::move(group))
1220 const std::string& desc,
1221 std::shared_ptr<const Value>
value = ::cxxopts::value<bool>(),
1222 std::string arg_help =
"");
1226 std::string m_group;
1231 constexpr
int OPTION_LONGEST = 30;
1232 constexpr
int OPTION_DESC_GAP = 2;
1234 std::basic_regex<char>
1235 option_matcher(
"--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
1237 std::basic_regex<char>
1238 option_specifier(
"(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
1241 format_option(
const HelpOptionDetails& o)
1262 auto arg = o.arg_help.size() > 0 ?
toLocalString(o.arg_help) :
"arg";
1268 result +=
" [=" + arg +
"(=" +
toLocalString(o.implicit_value) +
")]";
1272 result +=
" " + arg;
1280 format_description(
const HelpOptionDetails& o,
size_t start,
size_t width)
1284 if (o.has_default && (!o.is_boolean || o.default_value !=
"false"))
1286 desc +=
toLocalString(
" (default: " + o.default_value +
")");
1291 auto current = std::begin(desc);
1292 auto startLine = current;
1293 auto lastSpace = current;
1295 auto size =
size_t{};
1297 while (current != std::end(desc))
1299 if (*current ==
' ')
1301 lastSpace = current;
1304 if (*current ==
'\n')
1306 startLine = current + 1;
1307 lastSpace = startLine;
1309 else if (size > width)
1311 if (lastSpace == startLine)
1316 startLine = current + 1;
1317 lastSpace = startLine;
1324 startLine = lastSpace + 1;
1344 const std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<OptionDetails>>>
1346 std::vector<std::string> positional,
1347 bool allow_unrecognised,
1351 m_positional(
std::move(positional)),
1352 m_next_positional(m_positional.begin()),
1353 m_allow_unrecognised(allow_unrecognised)
1366 const std::string& desc,
1367 std::shared_ptr<const Value>
value,
1368 std::string arg_help)
1370 std::match_results<const char*> result;
1371 std::regex_match(opts.c_str(), result, option_specifier);
1378 const auto& short_match = result[2];
1379 const auto& long_match = result[3];
1381 if (!short_match.length() && !long_match.length())
1385 else if (long_match.length() == 1 && short_match.length())
1391 [](
const std::sub_match<const char*>& short_,
const std::sub_match<const char*>& long_)
1393 if (long_.length() == 1)
1395 return std::make_tuple(long_.str(), short_.str());
1399 return std::make_tuple(short_.str(), long_.str());
1401 }(short_match, long_match);
1404 std::get<0>(option_names),
1405 std::get<1>(option_names),
1408 std::move(arg_help));
1414 ParseResult::parse_default(std::shared_ptr<OptionDetails> details)
1416 m_results[details].parse_default(details);
1420 ParseResult::parse_option(std::shared_ptr<OptionDetails>
value,
1421 const std::string& ,
1422 const std::string& arg)
1424 auto& result = m_results[
value];
1425 result.parse(
value, arg);
1427 m_sequential.emplace_back(
value->long_name(), arg);
1431 ParseResult::checked_parse_arg(
int argc,
1434 std::shared_ptr<OptionDetails>
value,
1435 const std::string& name)
1437 if (current + 1 >= argc)
1439 if (
value->value().has_implicit())
1441 parse_option(
value, name,
value->value().get_implicit_value());
1445 throw missing_argument_exception(name);
1450 if (
value->value().has_implicit())
1452 parse_option(
value, name,
value->value().get_implicit_value());
1456 parse_option(
value, name, argv[current + 1]);
1463 ParseResult::add_to_option(
const std::string&
option,
const std::string& arg)
1465 auto iter = m_options->find(
option);
1467 if (iter == m_options->end())
1469 throw option_not_exists_exception(
option);
1472 parse_option(iter->second,
option, arg);
1476 ParseResult::consume_positional(std::string
a)
1478 while (m_next_positional != m_positional.end())
1480 auto iter = m_options->find(*m_next_positional);
1481 if (iter != m_options->end())
1483 auto& result = m_results[iter->second];
1484 if (!iter->second->value().is_container())
1486 if (result.count() == 0)
1488 add_to_option(*m_next_positional,
a);
1489 ++m_next_positional;
1494 ++m_next_positional;
1500 add_to_option(*m_next_positional,
a);
1504 ++m_next_positional;
1519 m_positional = std::move(options);
1520 m_next_positional = m_positional.begin();
1522 m_positional_set.insert(m_positional.begin(), m_positional.end());
1534 ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
1539 ParseResult::parse(
int& argc,
char**& argv)
1545 bool consume_remaining =
false;
1547 while (current != argc)
1549 if (strcmp(argv[current],
"--") == 0)
1551 consume_remaining =
true;
1556 std::match_results<const char*> result;
1557 std::regex_match(argv[current], result, option_matcher);
1564 if (argv[current][0] ==
'-' && argv[current][1] !=
'\0')
1566 throw option_syntax_exception(argv[current]);
1571 if (consume_positional(argv[current]))
1576 argv[nextKeep] = argv[current];
1584 if (result[4].length() != 0)
1586 const std::string&
s = result[4];
1588 for (std::size_t i = 0; i !=
s.size(); ++i)
1590 std::string name(1,
s[i]);
1591 auto iter = m_options->find(name);
1593 if (iter == m_options->end())
1595 if (m_allow_unrecognised)
1602 throw option_not_exists_exception(name);
1606 auto value = iter->second;
1608 if (i + 1 ==
s.size())
1611 checked_parse_arg(argc, argv, current,
value, name);
1613 else if (
value->value().has_implicit())
1615 parse_option(
value, name,
value->value().get_implicit_value());
1620 throw option_requires_argument_exception(name);
1624 else if (result[1].length() != 0)
1626 const std::string& name = result[1];
1628 auto iter = m_options->find(name);
1630 if (iter == m_options->end())
1632 if (m_allow_unrecognised)
1635 argv[nextKeep] = argv[current];
1643 throw option_not_exists_exception(name);
1647 auto opt = iter->second;
1650 if (result[2].length() != 0)
1654 parse_option(opt, name, result[3]);
1659 checked_parse_arg(argc, argv, current, opt, name);
1667 for (
auto& opt : *m_options)
1669 auto&
detail = opt.second;
1680 if (consume_remaining)
1682 while (current < argc)
1684 if (!consume_positional(argv[current]))
1692 while (current != argc)
1694 argv[nextKeep] = argv[current];
1705 const std::string&
s,
1706 const std::string& l,
1708 std::shared_ptr<const Value>
value,
1709 std::string arg_help)
1712 auto option = std::make_shared<OptionDetails>(
s, l, stringDesc,
value);
1721 add_one_option(l,
option);
1725 auto& options = m_help[group];
1730 value->has_default(),
1731 value->get_default_value(),
1732 value->has_implicit(),
1733 value->get_implicit_value(),
1734 std::move(arg_help),
1735 value->is_container(),
1736 value->is_boolean()});
1740 Options::add_one_option(
const std::string&
option, std::shared_ptr<OptionDetails> details)
1742 auto in = m_options->emplace(
option, details);
1751 Options::help_one_group(
const std::string& g)
const
1753 typedef std::vector<std::pair<String, String>> OptionHelp;
1755 auto group = m_help.find(g);
1756 if (group == m_help.end())
1772 for (
const auto& o : group->second.options)
1774 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end() &&
1780 auto s = format_option(o);
1782 format.push_back(std::make_pair(
s,
String()));
1785 longest = (
std::min)(longest,
static_cast<size_t>(OPTION_LONGEST));
1788 auto allowed =
size_t{76} - longest - OPTION_DESC_GAP;
1790 auto fiter = format.begin();
1791 for (
const auto& o : group->second.options)
1793 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end() &&
1799 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
1801 result += fiter->first;
1805 result +=
toLocalString(std::string(longest + OPTION_DESC_GAP,
' '));
1810 std::string(longest + OPTION_DESC_GAP -
stringLength(fiter->first),
' '));
1822 Options::generate_group_help(
String& result,
const std::vector<std::string>& print_groups)
const
1824 for (
size_t i = 0; i != print_groups.size(); ++i)
1826 const String& group_help_text = help_one_group(print_groups[i]);
1827 if (
empty(group_help_text))
1831 result += group_help_text;
1832 if (i < print_groups.size() - 1)
1840 Options::generate_all_groups_help(
String& result)
const
1842 std::vector<std::string> all_groups;
1843 all_groups.reserve(m_help.size());
1845 for (
auto& group : m_help)
1847 all_groups.push_back(group.first);
1850 generate_group_help(result, all_groups);
1859 if (m_positional.size() > 0 && m_positional_help.size() > 0)
1866 if (help_groups.size() == 0)
1868 generate_all_groups_help(result);
1872 generate_group_help(result, help_groups);
1878 inline const std::vector<std::string>
1881 std::vector<std::string> g;
1885 std::back_inserter(g),
1886 [](
const std::map<std::string, HelpGroupDetails>::value_type& pair)
1887 { return pair.first; });
1895 return m_help.at(group);
1900 #endif //CXXOPTS_HPP_INCLUDED