Path: blob/master/examples/cppwin/TensorflowTTSCppInference/ext/cxxopts.hpp
1564 views
/*12Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck34Permission is hereby granted, free of charge, to any person obtaining a copy5of this software and associated documentation files (the "Software"), to deal6in the Software without restriction, including without limitation the rights7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell8copies of the Software, and to permit persons to whom the Software is9furnished to do so, subject to the following conditions:1011The above copyright notice and this permission notice shall be included in12all copies or substantial portions of the Software.1314THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN20THE SOFTWARE.2122*/2324#ifndef CXXOPTS_HPP_INCLUDED25#define CXXOPTS_HPP_INCLUDED2627#include <cstring>28#include <cctype>29#include <exception>30#include <iostream>31#include <limits>32#include <map>33#include <memory>34#include <regex>35#include <sstream>36#include <string>37#include <unordered_map>38#include <unordered_set>39#include <vector>4041#ifdef __cpp_lib_optional42#include <optional>43#define CXXOPTS_HAS_OPTIONAL44#endif4546#ifndef CXXOPTS_VECTOR_DELIMITER47#define CXXOPTS_VECTOR_DELIMITER ','48#endif4950#define CXXOPTS__VERSION_MAJOR 251#define CXXOPTS__VERSION_MINOR 252#define CXXOPTS__VERSION_PATCH 05354namespace cxxopts55{56static constexpr struct {57uint8_t major, minor, patch;58} version = {59CXXOPTS__VERSION_MAJOR,60CXXOPTS__VERSION_MINOR,61CXXOPTS__VERSION_PATCH62};63}6465//when we ask cxxopts to use Unicode, help strings are processed using ICU,66//which results in the correct lengths being computed for strings when they67//are formatted for the help output68//it is necessary to make sure that <unicode/unistr.h> can be found by the69//compiler, and that icu-uc is linked in to the binary.7071#ifdef CXXOPTS_USE_UNICODE72#include <unicode/unistr.h>7374namespace cxxopts75{76typedef icu::UnicodeString String;7778inline79String80toLocalString(std::string s)81{82return icu::UnicodeString::fromUTF8(std::move(s));83}8485class UnicodeStringIterator : public86std::iterator<std::forward_iterator_tag, int32_t>87{88public:8990UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)91: s(string)92, i(pos)93{94}9596value_type97operator*() const98{99return s->char32At(i);100}101102bool103operator==(const UnicodeStringIterator& rhs) const104{105return s == rhs.s && i == rhs.i;106}107108bool109operator!=(const UnicodeStringIterator& rhs) const110{111return !(*this == rhs);112}113114UnicodeStringIterator&115operator++()116{117++i;118return *this;119}120121UnicodeStringIterator122operator+(int32_t v)123{124return UnicodeStringIterator(s, i + v);125}126127private:128const icu::UnicodeString* s;129int32_t i;130};131132inline133String&134stringAppend(String&s, String a)135{136return s.append(std::move(a));137}138139inline140String&141stringAppend(String& s, int n, UChar32 c)142{143for (int i = 0; i != n; ++i)144{145s.append(c);146}147148return s;149}150151template <typename Iterator>152String&153stringAppend(String& s, Iterator begin, Iterator end)154{155while (begin != end)156{157s.append(*begin);158++begin;159}160161return s;162}163164inline165size_t166stringLength(const String& s)167{168return s.length();169}170171inline172std::string173toUTF8String(const String& s)174{175std::string result;176s.toUTF8String(result);177178return result;179}180181inline182bool183empty(const String& s)184{185return s.isEmpty();186}187}188189namespace std190{191inline192cxxopts::UnicodeStringIterator193begin(const icu::UnicodeString& s)194{195return cxxopts::UnicodeStringIterator(&s, 0);196}197198inline199cxxopts::UnicodeStringIterator200end(const icu::UnicodeString& s)201{202return cxxopts::UnicodeStringIterator(&s, s.length());203}204}205206//ifdef CXXOPTS_USE_UNICODE207#else208209namespace cxxopts210{211typedef std::string String;212213template <typename T>214T215toLocalString(T&& t)216{217return std::forward<T>(t);218}219220inline221size_t222stringLength(const String& s)223{224return s.length();225}226227inline228String&229stringAppend(String&s, String a)230{231return s.append(std::move(a));232}233234inline235String&236stringAppend(String& s, size_t n, char c)237{238return s.append(n, c);239}240241template <typename Iterator>242String&243stringAppend(String& s, Iterator begin, Iterator end)244{245return s.append(begin, end);246}247248template <typename T>249std::string250toUTF8String(T&& t)251{252return std::forward<T>(t);253}254255inline256bool257empty(const std::string& s)258{259return s.empty();260}261}262263//ifdef CXXOPTS_USE_UNICODE264#endif265266namespace cxxopts267{268namespace269{270#ifdef _WIN32271const std::string LQUOTE("\'");272const std::string RQUOTE("\'");273#else274const std::string LQUOTE("‘");275const std::string RQUOTE("’");276#endif277}278279class Value : public std::enable_shared_from_this<Value>280{281public:282283virtual ~Value() = default;284285virtual286std::shared_ptr<Value>287clone() const = 0;288289virtual void290parse(const std::string& text) const = 0;291292virtual void293parse() const = 0;294295virtual bool296has_default() const = 0;297298virtual bool299is_container() const = 0;300301virtual bool302has_implicit() const = 0;303304virtual std::string305get_default_value() const = 0;306307virtual std::string308get_implicit_value() const = 0;309310virtual std::shared_ptr<Value>311default_value(const std::string& value) = 0;312313virtual std::shared_ptr<Value>314implicit_value(const std::string& value) = 0;315316virtual std::shared_ptr<Value>317no_implicit_value() = 0;318319virtual bool320is_boolean() const = 0;321};322323class OptionException : public std::exception324{325public:326OptionException(const std::string& message)327: m_message(message)328{329}330331virtual const char*332what() const noexcept333{334return m_message.c_str();335}336337private:338std::string m_message;339};340341class OptionSpecException : public OptionException342{343public:344345OptionSpecException(const std::string& message)346: OptionException(message)347{348}349};350351class OptionParseException : public OptionException352{353public:354OptionParseException(const std::string& message)355: OptionException(message)356{357}358};359360class option_exists_error : public OptionSpecException361{362public:363option_exists_error(const std::string& option)364: OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")365{366}367};368369class invalid_option_format_error : public OptionSpecException370{371public:372invalid_option_format_error(const std::string& format)373: OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)374{375}376};377378class option_syntax_exception : public OptionParseException {379public:380option_syntax_exception(const std::string& text)381: OptionParseException("Argument " + LQUOTE + text + RQUOTE +382" starts with a - but has incorrect syntax")383{384}385};386387class option_not_exists_exception : public OptionParseException388{389public:390option_not_exists_exception(const std::string& option)391: OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")392{393}394};395396class missing_argument_exception : public OptionParseException397{398public:399missing_argument_exception(const std::string& option)400: OptionParseException(401"Option " + LQUOTE + option + RQUOTE + " is missing an argument"402)403{404}405};406407class option_requires_argument_exception : public OptionParseException408{409public:410option_requires_argument_exception(const std::string& option)411: OptionParseException(412"Option " + LQUOTE + option + RQUOTE + " requires an argument"413)414{415}416};417418class option_not_has_argument_exception : public OptionParseException419{420public:421option_not_has_argument_exception422(423const std::string& option,424const std::string& arg425)426: OptionParseException(427"Option " + LQUOTE + option + RQUOTE +428" does not take an argument, but argument " +429LQUOTE + arg + RQUOTE + " given"430)431{432}433};434435class option_not_present_exception : public OptionParseException436{437public:438option_not_present_exception(const std::string& option)439: OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")440{441}442};443444class argument_incorrect_type : public OptionParseException445{446public:447argument_incorrect_type448(449const std::string& arg450)451: OptionParseException(452"Argument " + LQUOTE + arg + RQUOTE + " failed to parse"453)454{455}456};457458class option_required_exception : public OptionParseException459{460public:461option_required_exception(const std::string& option)462: OptionParseException(463"Option " + LQUOTE + option + RQUOTE + " is required but not present"464)465{466}467};468469namespace values470{471namespace472{473std::basic_regex<char> integer_pattern474("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");475std::basic_regex<char> truthy_pattern476("(t|T)(rue)?|1");477std::basic_regex<char> falsy_pattern478("(f|F)(alse)?|0");479}480481namespace detail482{483template <typename T, bool B>484struct SignedCheck;485486template <typename T>487struct SignedCheck<T, true>488{489template <typename U>490void491operator()(bool negative, U u, const std::string& text)492{493if (negative)494{495if (u > static_cast<U>((std::numeric_limits<T>::min)()))496{497throw argument_incorrect_type(text);498}499}500else501{502if (u > static_cast<U>((std::numeric_limits<T>::max)()))503{504throw argument_incorrect_type(text);505}506}507}508};509510template <typename T>511struct SignedCheck<T, false>512{513template <typename U>514void515operator()(bool, U, const std::string&) {}516};517518template <typename T, typename U>519void520check_signed_range(bool negative, U value, const std::string& text)521{522SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);523}524}525526template <typename R, typename T>527R528checked_negate(T&& t, const std::string&, std::true_type)529{530// if we got to here, then `t` is a positive number that fits into531// `R`. So to avoid MSVC C4146, we first cast it to `R`.532// See https://github.com/jarro2783/cxxopts/issues/62 for more details.533return -static_cast<R>(t-1)-1;534}535536template <typename R, typename T>537T538checked_negate(T&&, const std::string& text, std::false_type)539{540throw argument_incorrect_type(text);541}542543template <typename T>544void545integer_parser(const std::string& text, T& value)546{547std::smatch match;548std::regex_match(text, match, integer_pattern);549550if (match.length() == 0)551{552throw argument_incorrect_type(text);553}554555if (match.length(4) > 0)556{557value = 0;558return;559}560561using US = typename std::make_unsigned<T>::type;562563constexpr bool is_signed = std::numeric_limits<T>::is_signed;564const bool negative = match.length(1) > 0;565const uint8_t base = match.length(2) > 0 ? 16 : 10;566567auto value_match = match[3];568569US result = 0;570571for (auto iter = value_match.first; iter != value_match.second; ++iter)572{573US digit = 0;574575if (*iter >= '0' && *iter <= '9')576{577digit = static_cast<US>(*iter - '0');578}579else if (base == 16 && *iter >= 'a' && *iter <= 'f')580{581digit = static_cast<US>(*iter - 'a' + 10);582}583else if (base == 16 && *iter >= 'A' && *iter <= 'F')584{585digit = static_cast<US>(*iter - 'A' + 10);586}587else588{589throw argument_incorrect_type(text);590}591592US next = result * base + digit;593if (result > next)594{595throw argument_incorrect_type(text);596}597598result = next;599}600601detail::check_signed_range<T>(negative, result, text);602603if (negative)604{605value = checked_negate<T>(result,606text,607std::integral_constant<bool, is_signed>());608}609else610{611value = static_cast<T>(result);612}613}614615template <typename T>616void stringstream_parser(const std::string& text, T& value)617{618std::stringstream in(text);619in >> value;620if (!in) {621throw argument_incorrect_type(text);622}623}624625inline626void627parse_value(const std::string& text, uint8_t& value)628{629integer_parser(text, value);630}631632inline633void634parse_value(const std::string& text, int8_t& value)635{636integer_parser(text, value);637}638639inline640void641parse_value(const std::string& text, uint16_t& value)642{643integer_parser(text, value);644}645646inline647void648parse_value(const std::string& text, int16_t& value)649{650integer_parser(text, value);651}652653inline654void655parse_value(const std::string& text, uint32_t& value)656{657integer_parser(text, value);658}659660inline661void662parse_value(const std::string& text, int32_t& value)663{664integer_parser(text, value);665}666667inline668void669parse_value(const std::string& text, uint64_t& value)670{671integer_parser(text, value);672}673674inline675void676parse_value(const std::string& text, int64_t& value)677{678integer_parser(text, value);679}680681inline682void683parse_value(const std::string& text, bool& value)684{685std::smatch result;686std::regex_match(text, result, truthy_pattern);687688if (!result.empty())689{690value = true;691return;692}693694std::regex_match(text, result, falsy_pattern);695if (!result.empty())696{697value = false;698return;699}700701throw argument_incorrect_type(text);702}703704inline705void706parse_value(const std::string& text, std::string& value)707{708value = text;709}710711// The fallback parser. It uses the stringstream parser to parse all types712// that have not been overloaded explicitly. It has to be placed in the713// source code before all other more specialized templates.714template <typename T>715void716parse_value(const std::string& text, T& value) {717stringstream_parser(text, value);718}719720template <typename T>721void722parse_value(const std::string& text, std::vector<T>& value)723{724std::stringstream in(text);725std::string token;726while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {727T v;728parse_value(token, v);729value.emplace_back(std::move(v));730}731}732733#ifdef CXXOPTS_HAS_OPTIONAL734template <typename T>735void736parse_value(const std::string& text, std::optional<T>& value)737{738T result;739parse_value(text, result);740value = std::move(result);741}742#endif743744template <typename T>745struct type_is_container746{747static constexpr bool value = false;748};749750template <typename T>751struct type_is_container<std::vector<T>>752{753static constexpr bool value = true;754};755756template <typename T>757class abstract_value : public Value758{759using Self = abstract_value<T>;760761public:762abstract_value()763: m_result(std::make_shared<T>())764, m_store(m_result.get())765{766}767768abstract_value(T* t)769: m_store(t)770{771}772773virtual ~abstract_value() = default;774775abstract_value(const abstract_value& rhs)776{777if (rhs.m_result)778{779m_result = std::make_shared<T>();780m_store = m_result.get();781}782else783{784m_store = rhs.m_store;785}786787m_default = rhs.m_default;788m_implicit = rhs.m_implicit;789m_default_value = rhs.m_default_value;790m_implicit_value = rhs.m_implicit_value;791}792793void794parse(const std::string& text) const795{796parse_value(text, *m_store);797}798799bool800is_container() const801{802return type_is_container<T>::value;803}804805void806parse() const807{808parse_value(m_default_value, *m_store);809}810811bool812has_default() const813{814return m_default;815}816817bool818has_implicit() const819{820return m_implicit;821}822823std::shared_ptr<Value>824default_value(const std::string& value)825{826m_default = true;827m_default_value = value;828return shared_from_this();829}830831std::shared_ptr<Value>832implicit_value(const std::string& value)833{834m_implicit = true;835m_implicit_value = value;836return shared_from_this();837}838839std::shared_ptr<Value>840no_implicit_value()841{842m_implicit = false;843return shared_from_this();844}845846std::string847get_default_value() const848{849return m_default_value;850}851852std::string853get_implicit_value() const854{855return m_implicit_value;856}857858bool859is_boolean() const860{861return std::is_same<T, bool>::value;862}863864const T&865get() const866{867if (m_store == nullptr)868{869return *m_result;870}871else872{873return *m_store;874}875}876877protected:878std::shared_ptr<T> m_result;879T* m_store;880881bool m_default = false;882bool m_implicit = false;883884std::string m_default_value;885std::string m_implicit_value;886};887888template <typename T>889class standard_value : public abstract_value<T>890{891public:892using abstract_value<T>::abstract_value;893894std::shared_ptr<Value>895clone() const896{897return std::make_shared<standard_value<T>>(*this);898}899};900901template <>902class standard_value<bool> : public abstract_value<bool>903{904public:905~standard_value() = default;906907standard_value()908{909set_default_and_implicit();910}911912standard_value(bool* b)913: abstract_value(b)914{915set_default_and_implicit();916}917918std::shared_ptr<Value>919clone() const920{921return std::make_shared<standard_value<bool>>(*this);922}923924private:925926void927set_default_and_implicit()928{929m_default = true;930m_default_value = "false";931m_implicit = true;932m_implicit_value = "true";933}934};935}936937template <typename T>938std::shared_ptr<Value>939value()940{941return std::make_shared<values::standard_value<T>>();942}943944template <typename T>945std::shared_ptr<Value>946value(T& t)947{948return std::make_shared<values::standard_value<T>>(&t);949}950951class OptionAdder;952953class OptionDetails954{955public:956OptionDetails957(958const std::string& short_,959const std::string& long_,960const String& desc,961std::shared_ptr<const Value> val962)963: m_short(short_)964, m_long(long_)965, m_desc(desc)966, m_value(val)967, m_count(0)968{969}970971OptionDetails(const OptionDetails& rhs)972: m_desc(rhs.m_desc)973, m_count(rhs.m_count)974{975m_value = rhs.m_value->clone();976}977978OptionDetails(OptionDetails&& rhs) = default;979980const String&981description() const982{983return m_desc;984}985986const Value& value() const {987return *m_value;988}989990std::shared_ptr<Value>991make_storage() const992{993return m_value->clone();994}995996const std::string&997short_name() const998{999return m_short;1000}10011002const std::string&1003long_name() const1004{1005return m_long;1006}10071008private:1009std::string m_short;1010std::string m_long;1011String m_desc;1012std::shared_ptr<const Value> m_value;1013int m_count;1014};10151016struct HelpOptionDetails1017{1018std::string s;1019std::string l;1020String desc;1021bool has_default;1022std::string default_value;1023bool has_implicit;1024std::string implicit_value;1025std::string arg_help;1026bool is_container;1027bool is_boolean;1028};10291030struct HelpGroupDetails1031{1032std::string name;1033std::string description;1034std::vector<HelpOptionDetails> options;1035};10361037class OptionValue1038{1039public:1040void1041parse1042(1043std::shared_ptr<const OptionDetails> details,1044const std::string& text1045)1046{1047ensure_value(details);1048++m_count;1049m_value->parse(text);1050}10511052void1053parse_default(std::shared_ptr<const OptionDetails> details)1054{1055ensure_value(details);1056m_default = true;1057m_value->parse();1058}10591060size_t1061count() const noexcept1062{1063return m_count;1064}10651066// TODO: maybe default options should count towards the number of arguments1067bool1068has_default() const noexcept1069{1070return m_default;1071}10721073template <typename T>1074const T&1075as() const1076{1077if (m_value == nullptr) {1078throw std::domain_error("No value");1079}10801081#ifdef CXXOPTS_NO_RTTI1082return static_cast<const values::standard_value<T>&>(*m_value).get();1083#else1084return dynamic_cast<const values::standard_value<T>&>(*m_value).get();1085#endif1086}10871088private:1089void1090ensure_value(std::shared_ptr<const OptionDetails> details)1091{1092if (m_value == nullptr)1093{1094m_value = details->make_storage();1095}1096}10971098std::shared_ptr<Value> m_value;1099size_t m_count = 0;1100bool m_default = false;1101};11021103class KeyValue1104{1105public:1106KeyValue(std::string key_, std::string value_)1107: m_key(std::move(key_))1108, m_value(std::move(value_))1109{1110}11111112const1113std::string&1114key() const1115{1116return m_key;1117}11181119const1120std::string&1121value() const1122{1123return m_value;1124}11251126template <typename T>1127T1128as() const1129{1130T result;1131values::parse_value(m_value, result);1132return result;1133}11341135private:1136std::string m_key;1137std::string m_value;1138};11391140class ParseResult1141{1142public:11431144ParseResult(1145const std::shared_ptr<1146std::unordered_map<std::string, std::shared_ptr<OptionDetails>>1147>,1148std::vector<std::string>,1149bool allow_unrecognised,1150int&, char**&);11511152size_t1153count(const std::string& o) const1154{1155auto iter = m_options->find(o);1156if (iter == m_options->end())1157{1158return 0;1159}11601161auto riter = m_results.find(iter->second);11621163return riter->second.count();1164}11651166const OptionValue&1167operator[](const std::string& option) const1168{1169auto iter = m_options->find(option);11701171if (iter == m_options->end())1172{1173throw option_not_present_exception(option);1174}11751176auto riter = m_results.find(iter->second);11771178return riter->second;1179}11801181const std::vector<KeyValue>&1182arguments() const1183{1184return m_sequential;1185}11861187private:11881189void1190parse(int& argc, char**& argv);11911192void1193add_to_option(const std::string& option, const std::string& arg);11941195bool1196consume_positional(std::string a);11971198void1199parse_option1200(1201std::shared_ptr<OptionDetails> value,1202const std::string& name,1203const std::string& arg = ""1204);12051206void1207parse_default(std::shared_ptr<OptionDetails> details);12081209void1210checked_parse_arg1211(1212int argc,1213char* argv[],1214int& current,1215std::shared_ptr<OptionDetails> value,1216const std::string& name1217);12181219const std::shared_ptr<1220std::unordered_map<std::string, std::shared_ptr<OptionDetails>>1221> m_options;1222std::vector<std::string> m_positional;1223std::vector<std::string>::iterator m_next_positional;1224std::unordered_set<std::string> m_positional_set;1225std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;12261227bool m_allow_unrecognised;12281229std::vector<KeyValue> m_sequential;1230};12311232class Options1233{1234typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>>1235OptionMap;1236public:12371238Options(std::string program, std::string help_string = "")1239: m_program(std::move(program))1240, m_help_string(toLocalString(std::move(help_string)))1241, m_custom_help("[OPTION...]")1242, m_positional_help("positional parameters")1243, m_show_positional(false)1244, m_allow_unrecognised(false)1245, m_options(std::make_shared<OptionMap>())1246, m_next_positional(m_positional.end())1247{1248}12491250Options&1251positional_help(std::string help_text)1252{1253m_positional_help = std::move(help_text);1254return *this;1255}12561257Options&1258custom_help(std::string help_text)1259{1260m_custom_help = std::move(help_text);1261return *this;1262}12631264Options&1265show_positional_help()1266{1267m_show_positional = true;1268return *this;1269}12701271Options&1272allow_unrecognised_options()1273{1274m_allow_unrecognised = true;1275return *this;1276}12771278ParseResult1279parse(int& argc, char**& argv);12801281OptionAdder1282add_options(std::string group = "");12831284void1285add_option1286(1287const std::string& group,1288const std::string& s,1289const std::string& l,1290std::string desc,1291std::shared_ptr<const Value> value,1292std::string arg_help1293);12941295//parse positional arguments into the given option1296void1297parse_positional(std::string option);12981299void1300parse_positional(std::vector<std::string> options);13011302void1303parse_positional(std::initializer_list<std::string> options);13041305template <typename Iterator>1306void1307parse_positional(Iterator begin, Iterator end) {1308parse_positional(std::vector<std::string>{begin, end});1309}13101311std::string1312help(const std::vector<std::string>& groups = {}) const;13131314const std::vector<std::string>1315groups() const;13161317const HelpGroupDetails&1318group_help(const std::string& group) const;13191320private:13211322void1323add_one_option1324(1325const std::string& option,1326std::shared_ptr<OptionDetails> details1327);13281329String1330help_one_group(const std::string& group) const;13311332void1333generate_group_help1334(1335String& result,1336const std::vector<std::string>& groups1337) const;13381339void1340generate_all_groups_help(String& result) const;13411342std::string m_program;1343String m_help_string;1344std::string m_custom_help;1345std::string m_positional_help;1346bool m_show_positional;1347bool m_allow_unrecognised;13481349std::shared_ptr<OptionMap> m_options;1350std::vector<std::string> m_positional;1351std::vector<std::string>::iterator m_next_positional;1352std::unordered_set<std::string> m_positional_set;13531354//mapping from groups to help options1355std::map<std::string, HelpGroupDetails> m_help;1356};13571358class OptionAdder1359{1360public:13611362OptionAdder(Options& options, std::string group)1363: m_options(options), m_group(std::move(group))1364{1365}13661367OptionAdder&1368operator()1369(1370const std::string& opts,1371const std::string& desc,1372std::shared_ptr<const Value> value1373= ::cxxopts::value<bool>(),1374std::string arg_help = ""1375);13761377private:1378Options& m_options;1379std::string m_group;1380};13811382namespace1383{1384constexpr int OPTION_LONGEST = 30;1385constexpr int OPTION_DESC_GAP = 2;13861387std::basic_regex<char> option_matcher1388("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");13891390std::basic_regex<char> option_specifier1391("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");13921393String1394format_option1395(1396const HelpOptionDetails& o1397)1398{1399auto& s = o.s;1400auto& l = o.l;14011402String result = " ";14031404if (s.size() > 0)1405{1406result += "-" + toLocalString(s) + ",";1407}1408else1409{1410result += " ";1411}14121413if (l.size() > 0)1414{1415result += " --" + toLocalString(l);1416}14171418auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";14191420if (!o.is_boolean)1421{1422if (o.has_implicit)1423{1424result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";1425}1426else1427{1428result += " " + arg;1429}1430}14311432return result;1433}14341435String1436format_description1437(1438const HelpOptionDetails& o,1439size_t start,1440size_t width1441)1442{1443auto desc = o.desc;14441445if (o.has_default && (!o.is_boolean || o.default_value != "false"))1446{1447desc += toLocalString(" (default: " + o.default_value + ")");1448}14491450String result;14511452auto current = std::begin(desc);1453auto startLine = current;1454auto lastSpace = current;14551456auto size = size_t{};14571458while (current != std::end(desc))1459{1460if (*current == ' ')1461{1462lastSpace = current;1463}14641465if (*current == '\n')1466{1467startLine = current + 1;1468lastSpace = startLine;1469}1470else if (size > width)1471{1472if (lastSpace == startLine)1473{1474stringAppend(result, startLine, current + 1);1475stringAppend(result, "\n");1476stringAppend(result, start, ' ');1477startLine = current + 1;1478lastSpace = startLine;1479}1480else1481{1482stringAppend(result, startLine, lastSpace);1483stringAppend(result, "\n");1484stringAppend(result, start, ' ');1485startLine = lastSpace + 1;1486lastSpace = startLine;1487}1488size = 0;1489}1490else1491{1492++size;1493}14941495++current;1496}14971498//append whatever is left1499stringAppend(result, startLine, current);15001501return result;1502}1503}15041505inline1506ParseResult::ParseResult1507(1508const std::shared_ptr<1509std::unordered_map<std::string, std::shared_ptr<OptionDetails>>1510> options,1511std::vector<std::string> positional,1512bool allow_unrecognised,1513int& argc, char**& argv1514)1515: m_options(options)1516, m_positional(std::move(positional))1517, m_next_positional(m_positional.begin())1518, m_allow_unrecognised(allow_unrecognised)1519{1520parse(argc, argv);1521}15221523inline1524OptionAdder1525Options::add_options(std::string group)1526{1527return OptionAdder(*this, std::move(group));1528}15291530inline1531OptionAdder&1532OptionAdder::operator()1533(1534const std::string& opts,1535const std::string& desc,1536std::shared_ptr<const Value> value,1537std::string arg_help1538)1539{1540std::match_results<const char*> result;1541std::regex_match(opts.c_str(), result, option_specifier);15421543if (result.empty())1544{1545throw invalid_option_format_error(opts);1546}15471548const auto& short_match = result[2];1549const auto& long_match = result[3];15501551if (!short_match.length() && !long_match.length())1552{1553throw invalid_option_format_error(opts);1554} else if (long_match.length() == 1 && short_match.length())1555{1556throw invalid_option_format_error(opts);1557}15581559auto option_names = []1560(1561const std::sub_match<const char*>& short_,1562const std::sub_match<const char*>& long_1563)1564{1565if (long_.length() == 1)1566{1567return std::make_tuple(long_.str(), short_.str());1568}1569else1570{1571return std::make_tuple(short_.str(), long_.str());1572}1573}(short_match, long_match);15741575m_options.add_option1576(1577m_group,1578std::get<0>(option_names),1579std::get<1>(option_names),1580desc,1581value,1582std::move(arg_help)1583);15841585return *this;1586}15871588inline1589void1590ParseResult::parse_default(std::shared_ptr<OptionDetails> details)1591{1592m_results[details].parse_default(details);1593}15941595inline1596void1597ParseResult::parse_option1598(1599std::shared_ptr<OptionDetails> value,1600const std::string& /*name*/,1601const std::string& arg1602)1603{1604auto& result = m_results[value];1605result.parse(value, arg);16061607m_sequential.emplace_back(value->long_name(), arg);1608}16091610inline1611void1612ParseResult::checked_parse_arg1613(1614int argc,1615char* argv[],1616int& current,1617std::shared_ptr<OptionDetails> value,1618const std::string& name1619)1620{1621if (current + 1 >= argc)1622{1623if (value->value().has_implicit())1624{1625parse_option(value, name, value->value().get_implicit_value());1626}1627else1628{1629throw missing_argument_exception(name);1630}1631}1632else1633{1634if (value->value().has_implicit())1635{1636parse_option(value, name, value->value().get_implicit_value());1637}1638else1639{1640parse_option(value, name, argv[current + 1]);1641++current;1642}1643}1644}16451646inline1647void1648ParseResult::add_to_option(const std::string& option, const std::string& arg)1649{1650auto iter = m_options->find(option);16511652if (iter == m_options->end())1653{1654throw option_not_exists_exception(option);1655}16561657parse_option(iter->second, option, arg);1658}16591660inline1661bool1662ParseResult::consume_positional(std::string a)1663{1664while (m_next_positional != m_positional.end())1665{1666auto iter = m_options->find(*m_next_positional);1667if (iter != m_options->end())1668{1669auto& result = m_results[iter->second];1670if (!iter->second->value().is_container())1671{1672if (result.count() == 0)1673{1674add_to_option(*m_next_positional, a);1675++m_next_positional;1676return true;1677}1678else1679{1680++m_next_positional;1681continue;1682}1683}1684else1685{1686add_to_option(*m_next_positional, a);1687return true;1688}1689}1690else1691{1692throw option_not_exists_exception(*m_next_positional);1693}1694}16951696return false;1697}16981699inline1700void1701Options::parse_positional(std::string option)1702{1703parse_positional(std::vector<std::string>{std::move(option)});1704}17051706inline1707void1708Options::parse_positional(std::vector<std::string> options)1709{1710m_positional = std::move(options);1711m_next_positional = m_positional.begin();17121713m_positional_set.insert(m_positional.begin(), m_positional.end());1714}17151716inline1717void1718Options::parse_positional(std::initializer_list<std::string> options)1719{1720parse_positional(std::vector<std::string>(std::move(options)));1721}17221723inline1724ParseResult1725Options::parse(int& argc, char**& argv)1726{1727ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);1728return result;1729}17301731inline1732void1733ParseResult::parse(int& argc, char**& argv)1734{1735int current = 1;17361737int nextKeep = 1;17381739bool consume_remaining = false;17401741while (current != argc)1742{1743if (strcmp(argv[current], "--") == 0)1744{1745consume_remaining = true;1746++current;1747break;1748}17491750std::match_results<const char*> result;1751std::regex_match(argv[current], result, option_matcher);17521753if (result.empty())1754{1755//not a flag17561757// but if it starts with a `-`, then it's an error1758if (argv[current][0] == '-' && argv[current][1] != '\0') {1759if (!m_allow_unrecognised) {1760throw option_syntax_exception(argv[current]);1761}1762}17631764//if true is returned here then it was consumed, otherwise it is1765//ignored1766if (consume_positional(argv[current]))1767{1768}1769else1770{1771argv[nextKeep] = argv[current];1772++nextKeep;1773}1774//if we return from here then it was parsed successfully, so continue1775}1776else1777{1778//short or long option?1779if (result[4].length() != 0)1780{1781const std::string& s = result[4];17821783for (std::size_t i = 0; i != s.size(); ++i)1784{1785std::string name(1, s[i]);1786auto iter = m_options->find(name);17871788if (iter == m_options->end())1789{1790if (m_allow_unrecognised)1791{1792continue;1793}1794else1795{1796//error1797throw option_not_exists_exception(name);1798}1799}18001801auto value = iter->second;18021803if (i + 1 == s.size())1804{1805//it must be the last argument1806checked_parse_arg(argc, argv, current, value, name);1807}1808else if (value->value().has_implicit())1809{1810parse_option(value, name, value->value().get_implicit_value());1811}1812else1813{1814//error1815throw option_requires_argument_exception(name);1816}1817}1818}1819else if (result[1].length() != 0)1820{1821const std::string& name = result[1];18221823auto iter = m_options->find(name);18241825if (iter == m_options->end())1826{1827if (m_allow_unrecognised)1828{1829// keep unrecognised options in argument list, skip to next argument1830argv[nextKeep] = argv[current];1831++nextKeep;1832++current;1833continue;1834}1835else1836{1837//error1838throw option_not_exists_exception(name);1839}1840}18411842auto opt = iter->second;18431844//equals provided for long option?1845if (result[2].length() != 0)1846{1847//parse the option given18481849parse_option(opt, name, result[3]);1850}1851else1852{1853//parse the next argument1854checked_parse_arg(argc, argv, current, opt, name);1855}1856}18571858}18591860++current;1861}18621863for (auto& opt : *m_options)1864{1865auto& detail = opt.second;1866auto& value = detail->value();18671868auto& store = m_results[detail];18691870if(value.has_default() && !store.count() && !store.has_default()){1871parse_default(detail);1872}1873}18741875if (consume_remaining)1876{1877while (current < argc)1878{1879if (!consume_positional(argv[current])) {1880break;1881}1882++current;1883}18841885//adjust argv for any that couldn't be swallowed1886while (current != argc) {1887argv[nextKeep] = argv[current];1888++nextKeep;1889++current;1890}1891}18921893argc = nextKeep;18941895}18961897inline1898void1899Options::add_option1900(1901const std::string& group,1902const std::string& s,1903const std::string& l,1904std::string desc,1905std::shared_ptr<const Value> value,1906std::string arg_help1907)1908{1909auto stringDesc = toLocalString(std::move(desc));1910auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);19111912if (s.size() > 0)1913{1914add_one_option(s, option);1915}19161917if (l.size() > 0)1918{1919add_one_option(l, option);1920}19211922//add the help details1923auto& options = m_help[group];19241925options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,1926value->has_default(), value->get_default_value(),1927value->has_implicit(), value->get_implicit_value(),1928std::move(arg_help),1929value->is_container(),1930value->is_boolean()});1931}19321933inline1934void1935Options::add_one_option1936(1937const std::string& option,1938std::shared_ptr<OptionDetails> details1939)1940{1941auto in = m_options->emplace(option, details);19421943if (!in.second)1944{1945throw option_exists_error(option);1946}1947}19481949inline1950String1951Options::help_one_group(const std::string& g) const1952{1953typedef std::vector<std::pair<String, String>> OptionHelp;19541955auto group = m_help.find(g);1956if (group == m_help.end())1957{1958return "";1959}19601961OptionHelp format;19621963size_t longest = 0;19641965String result;19661967if (!g.empty())1968{1969result += toLocalString(" " + g + " options:\n");1970}19711972for (const auto& o : group->second.options)1973{1974if (m_positional_set.find(o.l) != m_positional_set.end() &&1975!m_show_positional)1976{1977continue;1978}19791980auto s = format_option(o);1981longest = (std::max)(longest, stringLength(s));1982format.push_back(std::make_pair(s, String()));1983}19841985longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));19861987//widest allowed description1988auto allowed = size_t{76} - longest - OPTION_DESC_GAP;19891990auto fiter = format.begin();1991for (const auto& o : group->second.options)1992{1993if (m_positional_set.find(o.l) != m_positional_set.end() &&1994!m_show_positional)1995{1996continue;1997}19981999auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);20002001result += fiter->first;2002if (stringLength(fiter->first) > longest)2003{2004result += '\n';2005result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));2006}2007else2008{2009result += toLocalString(std::string(longest + OPTION_DESC_GAP -2010stringLength(fiter->first),2011' '));2012}2013result += d;2014result += '\n';20152016++fiter;2017}20182019return result;2020}20212022inline2023void2024Options::generate_group_help2025(2026String& result,2027const std::vector<std::string>& print_groups2028) const2029{2030for (size_t i = 0; i != print_groups.size(); ++i)2031{2032const String& group_help_text = help_one_group(print_groups[i]);2033if (empty(group_help_text))2034{2035continue;2036}2037result += group_help_text;2038if (i < print_groups.size() - 1)2039{2040result += '\n';2041}2042}2043}20442045inline2046void2047Options::generate_all_groups_help(String& result) const2048{2049std::vector<std::string> all_groups;2050all_groups.reserve(m_help.size());20512052for (auto& group : m_help)2053{2054all_groups.push_back(group.first);2055}20562057generate_group_help(result, all_groups);2058}20592060inline2061std::string2062Options::help(const std::vector<std::string>& help_groups) const2063{2064String result = m_help_string + "\nUsage:\n " +2065toLocalString(m_program) + " " + toLocalString(m_custom_help);20662067if (m_positional.size() > 0 && m_positional_help.size() > 0) {2068result += " " + toLocalString(m_positional_help);2069}20702071result += "\n\n";20722073if (help_groups.size() == 0)2074{2075generate_all_groups_help(result);2076}2077else2078{2079generate_group_help(result, help_groups);2080}20812082return toUTF8String(result);2083}20842085inline2086const std::vector<std::string>2087Options::groups() const2088{2089std::vector<std::string> g;20902091std::transform(2092m_help.begin(),2093m_help.end(),2094std::back_inserter(g),2095[] (const std::map<std::string, HelpGroupDetails>::value_type& pair)2096{2097return pair.first;2098}2099);21002101return g;2102}21032104inline2105const HelpGroupDetails&2106Options::group_help(const std::string& group) const2107{2108return m_help.at(group);2109}21102111}21122113#endif //CXXOPTS_HPP_INCLUDED211421152116