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
69 #ifdef CXXOPTS_USE_UNICODE
70 #include <unicode/unistr.h>
74 typedef icu::UnicodeString
String;
80 return icu::UnicodeString::fromUTF8(std::move(
s));
83 class UnicodeStringIterator :
public
84 std::iterator<std::forward_iterator_tag, int32_t>
88 UnicodeStringIterator(
const icu::UnicodeString*
string, int32_t pos)
97 return s->char32At(i);
101 operator==(
const UnicodeStringIterator& rhs)
const
103 return s == rhs.s && i == rhs.i;
107 operator!=(
const UnicodeStringIterator& rhs)
const
109 return !(*
this == rhs);
112 UnicodeStringIterator&
119 UnicodeStringIterator
122 return UnicodeStringIterator(
s, i +
v);
126 const icu::UnicodeString*
s;
134 return s.append(std::move(
a));
141 for (
int i = 0; i != n; ++i)
149 template <
typename Iterator>
174 s.toUTF8String(result);
190 cxxopts::UnicodeStringIterator
191 begin(
const icu::UnicodeString&
s)
193 return cxxopts::UnicodeStringIterator(&
s, 0);
197 cxxopts::UnicodeStringIterator
198 end(
const icu::UnicodeString&
s)
200 return cxxopts::UnicodeStringIterator(&
s,
s.length());
211 template <
typename T>
215 return std::forward<T>(t);
229 return s.append(std::move(
a));
236 return s.append(n,
c);
239 template <
typename Iterator>
243 return s.append(begin, end);
246 template <
typename T>
250 return std::forward<T>(t);
269 const std::string LQUOTE(
"\'");
270 const std::string RQUOTE(
"\'");
272 const std::string LQUOTE(
"‘");
273 const std::string RQUOTE(
"’");
277 class Value :
public std::enable_shared_from_this<Value>
281 virtual ~Value() =
default;
284 std::shared_ptr<Value>
288 parse(
const std::string& text)
const = 0;
308 virtual std::shared_ptr<Value>
311 virtual std::shared_ptr<Value>
329 return m_message.c_str();
333 std::string m_message;
378 " starts with a - but has incorrect syntax")
397 "Option " + LQUOTE +
option + RQUOTE +
" is missing an argument"
408 "Option " + LQUOTE +
option + RQUOTE +
" requires an argument"
419 const std::string&
option,
420 const std::string& arg
423 "Option " + LQUOTE +
option + RQUOTE +
424 " does not take an argument, but argument " +
425 LQUOTE + arg + RQUOTE +
" given"
445 const std::string& arg
448 "Argument " + LQUOTE + arg + RQUOTE +
" failed to parse"
459 "Option " + LQUOTE +
option + RQUOTE +
" is required but not present"
469 std::basic_regex<char> integer_pattern
470 (
"(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
471 std::basic_regex<char> truthy_pattern
473 std::basic_regex<char> falsy_pattern
479 template <
typename T,
bool B>
482 template <
typename T>
485 template <
typename U>
506 template <
typename T>
509 template <
typename U>
514 template <
typename T,
typename U>
522 template <
typename R,
typename T>
529 return -
static_cast<R
>(t);
532 template <
typename R,
typename T>
539 template <
typename T>
544 std::regex_match(text, match, integer_pattern);
546 if (match.length() == 0)
551 if (match.length(4) > 0)
557 using US =
typename std::make_unsigned<T>::type;
560 constexpr
bool is_signed = std::numeric_limits<T>::is_signed;
561 const bool negative = match.length(1) > 0;
562 const uint8_t base = match.length(2) > 0 ? 16 : 10;
564 auto value_match = match[3];
568 for (
auto iter = value_match.first; iter != value_match.second; ++iter)
572 if (*iter >=
'0' && *iter <=
'9')
576 else if (base == 16 && *iter >=
'a' && *iter <=
'f')
578 digit = *iter -
'a' + 10;
580 else if (base == 16 && *iter >=
'A' && *iter <=
'F')
582 digit = *iter -
'A' + 10;
589 if (umax - digit < result * base)
594 result = result * base + digit;
597 detail::check_signed_range<T>(negative, result, text);
601 value = checked_negate<T>(result,
603 std::integral_constant<bool, is_signed>());
611 template <
typename T>
614 std::stringstream in(text);
683 std::regex_match(text, result, truthy_pattern);
691 std::regex_match(text, result, falsy_pattern);
711 template <
typename T>
718 template <
typename T>
727 #ifdef CXXOPTS_HAS_OPTIONAL
728 template <
typename T>
734 value = std::move(result);
738 template <
typename T>
741 static constexpr
bool value =
false;
744 template <
typename T>
750 template <
typename T>
788 parse(
const std::string& text)
const
817 std::shared_ptr<Value>
822 return shared_from_this();
825 std::shared_ptr<Value>
830 return shared_from_this();
875 template <
typename T>
881 std::shared_ptr<Value>
884 return std::make_shared<standard_value<T>>(*this);
896 set_default_and_implicit();
902 set_default_and_implicit();
905 std::shared_ptr<Value>
908 return std::make_shared<standard_value<bool>>(*this);
914 set_default_and_implicit()
924 template <
typename T>
925 std::shared_ptr<Value>
928 return std::make_shared<values::standard_value<T>>();
931 template <
typename T>
932 std::shared_ptr<Value>
935 return std::make_shared<values::standard_value<T>>(&t);
945 const std::string& short_,
946 const std::string& long_,
948 std::shared_ptr<const Value> val
960 , m_count(rhs.m_count)
962 m_value = rhs.m_value->clone();
978 std::shared_ptr<Value>
981 return m_value->clone();
1000 std::shared_ptr<const Value> m_value;
1031 std::shared_ptr<const OptionDetails> details,
1032 const std::string& text
1035 ensure_value(details);
1037 m_value->parse(text);
1043 ensure_value(details);
1053 template <
typename T>
1057 if (m_value ==
nullptr)
1059 throw std::domain_error(
"No value");
1062 #ifdef CXXOPTS_NO_RTTI
1071 ensure_value(std::shared_ptr<const OptionDetails> details)
1073 if (m_value ==
nullptr)
1075 m_value = details->make_storage();
1079 std::shared_ptr<Value> m_value;
1087 : m_key(
std::move(key_))
1088 , m_value(
std::move(value_))
1106 template <
typename T>
1117 std::string m_value;
1125 const std::shared_ptr <
1126 std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1128 std::vector<std::string>,
1129 bool allow_unrecognised,
1135 auto iter = m_options->find(o);
1136 if (iter == m_options->end())
1141 auto riter = m_results.find(iter->second);
1143 return riter->second.count();
1149 auto iter = m_options->find(
option);
1151 if (iter == m_options->end())
1156 auto riter = m_results.find(iter->second);
1158 return riter->second;
1161 const std::vector<KeyValue>&
1164 return m_sequential;
1170 parse(
int& argc,
char**& argv);
1173 add_to_option(
const std::string&
option,
const std::string& arg);
1176 consume_positional(std::string
a);
1181 std::shared_ptr<OptionDetails>
value,
1182 const std::string& name,
1183 const std::string& arg =
""
1187 parse_default(std::shared_ptr<OptionDetails> details);
1195 std::shared_ptr<OptionDetails>
value,
1196 const std::string& name
1199 const std::shared_ptr <
1200 std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1202 std::vector<std::string> m_positional;
1203 std::vector<std::string>::iterator m_next_positional;
1204 std::unordered_set<std::string> m_positional_set;
1205 std::unordered_map<std::shared_ptr<OptionDetails>,
OptionValue> m_results;
1207 bool m_allow_unrecognised;
1209 std::vector<KeyValue> m_sequential;
1214 typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1218 Options(std::string program, std::string help_string =
"")
1219 : m_program(
std::move(program))
1221 , m_custom_help(
"[OPTION...]")
1222 , m_positional_help(
"positional parameters")
1223 , m_show_positional(false)
1224 , m_allow_unrecognised(false)
1226 , m_next_positional(m_positional.end())
1233 m_positional_help = std::move(help_text);
1240 m_custom_help = std::move(help_text);
1247 m_show_positional =
true;
1254 m_allow_unrecognised =
true;
1259 parse(
int& argc,
char**& argv);
1267 const std::string& group,
1268 const std::string&
s,
1269 const std::string& l,
1271 std::shared_ptr<const Value>
value,
1272 std::string arg_help
1285 template <
typename Iterator>
1293 help(
const std::vector<std::string>&
groups = {})
const;
1295 const std::vector<std::string>
1298 const HelpGroupDetails&
1306 const std::string&
option,
1307 std::shared_ptr<OptionDetails> details
1311 help_one_group(
const std::string& group)
const;
1317 const std::vector<std::string>&
groups
1321 generate_all_groups_help(
String& result)
const;
1323 std::string m_program;
1325 std::string m_custom_help;
1326 std::string m_positional_help;
1327 bool m_show_positional;
1328 bool m_allow_unrecognised;
1330 std::shared_ptr<OptionMap> m_options;
1331 std::vector<std::string> m_positional;
1332 std::vector<std::string>::iterator m_next_positional;
1333 std::unordered_set<std::string> m_positional_set;
1336 std::map<std::string, HelpGroupDetails> m_help;
1344 : m_options(options), m_group(
std::move(group))
1351 const std::string& opts,
1352 const std::string& desc,
1353 std::shared_ptr<const Value>
value
1354 = ::cxxopts::value<bool>(),
1355 std::string arg_help =
""
1360 std::string m_group;
1365 constexpr
int OPTION_LONGEST = 30;
1366 constexpr
int OPTION_DESC_GAP = 2;
1368 std::basic_regex<char> option_matcher
1369 (
"--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
1371 std::basic_regex<char> option_specifier
1372 (
"(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
1377 const HelpOptionDetails& o
1399 auto arg = o.arg_help.size() > 0 ?
toLocalString(o.arg_help) :
"arg";
1405 result +=
" [=" + arg +
"(=" +
toLocalString(o.implicit_value) +
")]";
1409 result +=
" " + arg;
1419 const HelpOptionDetails& o,
1426 if (o.has_default && (!o.is_boolean || o.default_value !=
"false"))
1428 desc +=
toLocalString(
" (default: " + o.default_value +
")");
1433 auto current = std::begin(desc);
1434 auto startLine = current;
1435 auto lastSpace = current;
1437 auto size =
size_t{};
1439 while (current != std::end(desc))
1441 if (*current ==
' ')
1443 lastSpace = current;
1446 if (*current ==
'\n')
1448 startLine = current + 1;
1449 lastSpace = startLine;
1451 else if (size > width)
1453 if (lastSpace == startLine)
1458 startLine = current + 1;
1459 lastSpace = startLine;
1466 startLine = lastSpace + 1;
1488 const std::shared_ptr <
1489 std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1491 std::vector<std::string> positional,
1492 bool allow_unrecognised,
1493 int& argc,
char**& argv
1495 : m_options(options)
1496 , m_positional(std::move(positional))
1497 , m_next_positional(m_positional.begin())
1498 , m_allow_unrecognised(allow_unrecognised)
1512 OptionAdder::operator()
1514 const std::string& opts,
1515 const std::string& desc,
1516 std::shared_ptr<const Value>
value,
1517 std::string arg_help
1520 std::match_results<const char*> result;
1521 std::regex_match(opts.c_str(), result, option_specifier);
1528 const auto& short_match = result[2];
1529 const auto& long_match = result[3];
1531 if (!short_match.length() && !long_match.length())
1535 else if (long_match.length() == 1 && short_match.length())
1540 auto option_names = []
1542 const std::sub_match<const char*>& short_,
1543 const std::sub_match<const char*>& long_
1546 if (long_.length() == 1)
1548 return std::make_tuple(long_.str(), short_.str());
1552 return std::make_tuple(short_.str(), long_.str());
1555 (short_match, long_match);
1557 m_options.add_option
1560 std::get<0>(option_names),
1561 std::get<1>(option_names),
1572 ParseResult::parse_default(std::shared_ptr<OptionDetails> details)
1574 m_results[details].parse_default(details);
1579 ParseResult::parse_option
1581 std::shared_ptr<OptionDetails>
value,
1582 const std::string& ,
1583 const std::string& arg
1586 auto& result = m_results[
value];
1587 result.parse(
value, arg);
1589 m_sequential.emplace_back(
value->long_name(), arg);
1594 ParseResult::checked_parse_arg
1599 std::shared_ptr<OptionDetails>
value,
1600 const std::string& name
1603 if (current + 1 >= argc)
1605 if (
value->value().has_implicit())
1607 parse_option(
value, name,
value->value().get_implicit_value());
1611 throw missing_argument_exception(name);
1616 if (
value->value().has_implicit())
1618 parse_option(
value, name,
value->value().get_implicit_value());
1622 parse_option(
value, name, argv[current + 1]);
1630 ParseResult::add_to_option(
const std::string&
option,
const std::string& arg)
1632 auto iter = m_options->find(
option);
1634 if (iter == m_options->end())
1636 throw option_not_exists_exception(
option);
1639 parse_option(iter->second,
option, arg);
1644 ParseResult::consume_positional(std::string
a)
1646 while (m_next_positional != m_positional.end())
1648 auto iter = m_options->find(*m_next_positional);
1649 if (iter != m_options->end())
1651 auto& result = m_results[iter->second];
1652 if (!iter->second->value().is_container())
1654 if (result.count() == 0)
1656 add_to_option(*m_next_positional,
a);
1657 ++m_next_positional;
1662 ++m_next_positional;
1668 add_to_option(*m_next_positional,
a);
1672 ++m_next_positional;
1689 m_positional = std::move(options);
1690 m_next_positional = m_positional.begin();
1692 m_positional_set.insert(m_positional.begin(), m_positional.end());
1706 ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
1712 ParseResult::parse(
int& argc,
char**& argv)
1718 bool consume_remaining =
false;
1720 while (current != argc)
1722 if (strcmp(argv[current],
"--") == 0)
1724 consume_remaining =
true;
1729 std::match_results<const char*> result;
1730 std::regex_match(argv[current], result, option_matcher);
1737 if (argv[current][0] ==
'-' && argv[current][1] !=
'\0')
1739 throw option_syntax_exception(argv[current]);
1744 if (consume_positional(argv[current]))
1749 argv[nextKeep] = argv[current];
1757 if (result[4].length() != 0)
1759 const std::string&
s = result[4];
1761 for (std::size_t i = 0; i !=
s.size(); ++i)
1763 std::string name(1,
s[i]);
1764 auto iter = m_options->find(name);
1766 if (iter == m_options->end())
1768 if (m_allow_unrecognised)
1775 throw option_not_exists_exception(name);
1779 auto value = iter->second;
1781 if (i + 1 ==
s.size())
1784 checked_parse_arg(argc, argv, current,
value, name);
1786 else if (
value->value().has_implicit())
1788 parse_option(
value, name,
value->value().get_implicit_value());
1793 throw option_requires_argument_exception(name);
1797 else if (result[1].length() != 0)
1799 const std::string& name = result[1];
1801 auto iter = m_options->find(name);
1803 if (iter == m_options->end())
1805 if (m_allow_unrecognised)
1808 argv[nextKeep] = argv[current];
1816 throw option_not_exists_exception(name);
1820 auto opt = iter->second;
1823 if (result[2].length() != 0)
1827 parse_option(opt, name, result[3]);
1832 checked_parse_arg(argc, argv, current, opt, name);
1841 for (
auto& opt : *m_options)
1843 auto&
detail = opt.second;
1854 if (consume_remaining)
1856 while (current < argc)
1858 if (!consume_positional(argv[current]))
1866 while (current != argc)
1868 argv[nextKeep] = argv[current];
1882 const std::string& group,
1883 const std::string&
s,
1884 const std::string& l,
1886 std::shared_ptr<const Value>
value,
1887 std::string arg_help
1891 auto option = std::make_shared<OptionDetails>(
s, l, stringDesc,
value);
1900 add_one_option(l,
option);
1904 auto& options = m_help[group];
1907 value->has_default(),
value->get_default_value(),
1908 value->has_implicit(),
value->get_implicit_value(),
1909 std::move(arg_help),
1910 value->is_container(),
1911 value->is_boolean()});
1916 Options::add_one_option
1918 const std::string&
option,
1919 std::shared_ptr<OptionDetails> details
1922 auto in = m_options->emplace(
option, details);
1932 Options::help_one_group(
const std::string& g)
const
1934 typedef std::vector<std::pair<String, String>> OptionHelp;
1936 auto group = m_help.find(g);
1937 if (group == m_help.end())
1953 for (
const auto& o : group->second.options)
1955 if (o.is_container &&
1956 m_positional_set.find(o.l) != m_positional_set.end() &&
1962 auto s = format_option(o);
1964 format.push_back(std::make_pair(
s,
String()));
1967 longest = (
std::min)(longest,
static_cast<size_t>(OPTION_LONGEST));
1970 auto allowed =
size_t{76} - longest - OPTION_DESC_GAP;
1972 auto fiter = format.begin();
1973 for (
const auto& o : group->second.options)
1975 if (o.is_container &&
1976 m_positional_set.find(o.l) != m_positional_set.end() &&
1982 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
1984 result += fiter->first;
1988 result +=
toLocalString(std::string(longest + OPTION_DESC_GAP,
' '));
1992 result +=
toLocalString(std::string(longest + OPTION_DESC_GAP -
2007 Options::generate_group_help
2010 const std::vector<std::string>& print_groups
2013 for (
size_t i = 0; i != print_groups.size(); ++i)
2015 const String& group_help_text = help_one_group(print_groups[i]);
2016 if (
empty(group_help_text))
2020 result += group_help_text;
2021 if (i < print_groups.size() - 1)
2030 Options::generate_all_groups_help(
String& result)
const
2032 std::vector<std::string> all_groups;
2033 all_groups.reserve(m_help.size());
2035 for (
auto& group : m_help)
2037 all_groups.push_back(group.first);
2040 generate_group_help(result, all_groups);
2047 String result = m_help_string +
"\nUsage:\n " +
2050 if (m_positional.size() > 0 && m_positional_help.size() > 0)
2057 if (help_groups.size() == 0)
2059 generate_all_groups_help(result);
2063 generate_group_help(result, help_groups);
2070 const std::vector<std::string>
2073 std::vector<std::string> g;
2078 std::back_inserter(g),
2079 [](
const std::map<std::string, HelpGroupDetails>::value_type & pair)
2092 return m_help.at(group);
2097 #endif //CXXOPTS_HPP_INCLUDED