diff options
| author | Denis Evsyukov <[email protected]> | 2020-02-22 10:10:25 +0300 |
|---|---|---|
| committer | Denis Evsyukov <[email protected]> | 2020-02-22 10:10:25 +0300 |
| commit | 2180154b127b1f171bef690377c312eb11a6357a (patch) | |
| tree | e47969103050963136a3bc11a82a8cec8cd12a2f /src/opts | |
| parent | Change sha256 to sha1, and move parsing to main module (diff) | |
| download | t-2180154b127b1f171bef690377c312eb11a6357a.tar.xz t-2180154b127b1f171bef690377c312eb11a6357a.zip | |
[~] update cxxopts
Diffstat (limited to 'src/opts')
| -rw-r--r-- | src/opts/cxxopts.hpp | 3209 |
1 files changed, 1533 insertions, 1676 deletions
diff --git a/src/opts/cxxopts.hpp b/src/opts/cxxopts.hpp index 236ecbe..99e0aab 100644 --- a/src/opts/cxxopts.hpp +++ b/src/opts/cxxopts.hpp @@ -54,16 +54,16 @@ THE SOFTWARE. namespace cxxopts { - static constexpr struct { - uint8_t major, minor, patch; - } version = { - CXXOPTS__VERSION_MAJOR, - CXXOPTS__VERSION_MINOR, - CXXOPTS__VERSION_PATCH - }; -} - -//when we ask opts to use Unicode, help strings are processed using ICU, +static constexpr struct +{ + uint8_t major, minor, patch; +} version = { + CXXOPTS__VERSION_MAJOR, + CXXOPTS__VERSION_MINOR, + CXXOPTS__VERSION_PATCH}; +} // namespace cxxopts + +//when we ask cxxopts to use Unicode, help strings are processed using ICU, //which results in the correct lengths being computed for strings when they //are formatted for the help output //it is necessary to make sure that <unicode/unistr.h> can be found by the @@ -72,1855 +72,1688 @@ namespace cxxopts #ifdef CXXOPTS_USE_UNICODE #include <unicode/unistr.h> -namespace opts +namespace cxxopts { - typedef icu::UnicodeString String; +typedef icu::UnicodeString String; - inline - String - toLocalString(std::string s) - { +inline String +toLocalString(std::string s) +{ return icu::UnicodeString::fromUTF8(std::move(s)); - } - - class UnicodeStringIterator : public - std::iterator<std::forward_iterator_tag, int32_t> - { - public: +} - UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) - : s(string) - , i(pos) +class UnicodeStringIterator : public std::iterator<std::forward_iterator_tag, int32_t> +{ +public: + UnicodeStringIterator(const icu::UnicodeString *string, int32_t pos) + : s(string), i(pos) { } value_type operator*() const { - return s->char32At(i); + return s->char32At(i); } bool - operator==(const UnicodeStringIterator& rhs) const + operator==(const UnicodeStringIterator &rhs) const { - return s == rhs.s && i == rhs.i; + return s == rhs.s && i == rhs.i; } bool - operator!=(const UnicodeStringIterator& rhs) const + operator!=(const UnicodeStringIterator &rhs) const { - return !(*this == rhs); + return !(*this == rhs); } - UnicodeStringIterator& + UnicodeStringIterator & operator++() { - ++i; - return *this; + ++i; + return *this; } UnicodeStringIterator operator+(int32_t v) { - return UnicodeStringIterator(s, i + v); + return UnicodeStringIterator(s, i + v); } - private: - const icu::UnicodeString* s; +private: + const icu::UnicodeString *s; int32_t i; - }; +}; - inline - String& - stringAppend(String&s, String a) - { +inline String & +stringAppend(String &s, String a) +{ return s.append(std::move(a)); - } +} - inline - String& - stringAppend(String& s, int n, UChar32 c) - { +inline String & +stringAppend(String &s, int n, UChar32 c) +{ for (int i = 0; i != n; ++i) { - s.append(c); + s.append(c); } return s; - } +} - template <typename Iterator> - String& - stringAppend(String& s, Iterator begin, Iterator end) - { +template <typename Iterator> +String & +stringAppend(String &s, Iterator begin, Iterator end) +{ while (begin != end) { - s.append(*begin); - ++begin; + s.append(*begin); + ++begin; } return s; - } +} - inline - size_t - stringLength(const String& s) - { +inline size_t +stringLength(const String &s) +{ return s.length(); - } +} - inline - std::string - toUTF8String(const String& s) - { +inline std::string +toUTF8String(const String &s) +{ std::string result; s.toUTF8String(result); return result; - } +} - inline - bool - empty(const String& s) - { +inline bool +empty(const String &s) +{ return s.isEmpty(); - } } +} // namespace cxxopts namespace std { - inline - opts::UnicodeStringIterator - begin(const icu::UnicodeString& s) - { - return opts::UnicodeStringIterator(&s, 0); - } - - inline - opts::UnicodeStringIterator - end(const icu::UnicodeString& s) - { - return opts::UnicodeStringIterator(&s, s.length()); - } +inline cxxopts::UnicodeStringIterator +begin(const icu::UnicodeString &s) +{ + return cxxopts::UnicodeStringIterator(&s, 0); } +inline cxxopts::UnicodeStringIterator +end(const icu::UnicodeString &s) +{ + return cxxopts::UnicodeStringIterator(&s, s.length()); +} +} // namespace std + //ifdef CXXOPTS_USE_UNICODE #else namespace cxxopts { - typedef std::string String; +typedef std::string String; - template <typename T> - T - toLocalString(T&& t) - { - return std::forward<T>(t); - } +template <typename T> +T toLocalString(T &&t) +{ + return std::forward<T>(t); +} - inline - size_t - stringLength(const String& s) - { - return s.length(); - } +inline size_t +stringLength(const String &s) +{ + return s.length(); +} - inline - String& - stringAppend(String&s, String a) - { - return s.append(std::move(a)); - } +inline String & +stringAppend(String &s, String a) +{ + return s.append(std::move(a)); +} - inline - String& - stringAppend(String& s, size_t n, char c) - { - return s.append(n, c); - } +inline String & +stringAppend(String &s, size_t n, char c) +{ + return s.append(n, c); +} - template <typename Iterator> - String& - stringAppend(String& s, Iterator begin, Iterator end) - { - return s.append(begin, end); - } +template <typename Iterator> +String & +stringAppend(String &s, Iterator begin, Iterator end) +{ + return s.append(begin, end); +} - template <typename T> - std::string - toUTF8String(T&& t) - { - return std::forward<T>(t); - } +template <typename T> +std::string +toUTF8String(T &&t) +{ + return std::forward<T>(t); +} - inline - bool - empty(const std::string& s) - { - return s.empty(); - } +inline bool +empty(const std::string &s) +{ + return s.empty(); } +} // namespace cxxopts //ifdef CXXOPTS_USE_UNICODE #endif namespace cxxopts { - namespace - { +namespace +{ #ifdef _WIN32 - const std::string LQUOTE("\'"); - const std::string RQUOTE("\'"); +const std::string LQUOTE("\'"); +const std::string RQUOTE("\'"); #else - const std::string LQUOTE("‘"); - const std::string RQUOTE("’"); +const std::string LQUOTE("‘"); +const std::string RQUOTE("’"); #endif - } - - class Value : public std::enable_shared_from_this<Value> - { - public: +} // namespace - virtual ~Value() = default; +class Value : public std::enable_shared_from_this<Value> +{ +public: + virtual ~Value() = default; - virtual - std::shared_ptr<Value> - clone() const = 0; + virtual std::shared_ptr<Value> + clone() const = 0; - virtual void - parse(const std::string& text) const = 0; + virtual void + parse(const std::string &text) const = 0; - virtual void - parse() const = 0; + virtual void + parse() const = 0; - virtual bool - has_default() const = 0; + virtual bool + has_default() const = 0; - virtual bool - is_container() const = 0; + virtual bool + is_container() const = 0; - virtual bool - has_implicit() const = 0; + virtual bool + has_implicit() const = 0; - virtual std::string - get_default_value() const = 0; + virtual std::string + get_default_value() const = 0; - virtual std::string - get_implicit_value() const = 0; + virtual std::string + get_implicit_value() const = 0; - virtual std::shared_ptr<Value> - default_value(const std::string& value) = 0; + virtual std::shared_ptr<Value> + default_value(const std::string &value) = 0; - virtual std::shared_ptr<Value> - implicit_value(const std::string& value) = 0; + virtual std::shared_ptr<Value> + implicit_value(const std::string &value) = 0; - virtual std::shared_ptr<Value> - no_implicit_value() = 0; + virtual std::shared_ptr<Value> + no_implicit_value() = 0; - virtual bool - is_boolean() const = 0; - }; + virtual bool + is_boolean() const = 0; +}; - class OptionException : public std::exception +class OptionException : public std::exception +{ +public: + OptionException(const std::string &message) + : m_message(message) { - public: - OptionException(const std::string& message) - : m_message(message) - { - } + } - virtual const char* - what() const noexcept - { - return m_message.c_str(); - } + virtual const char * + what() const noexcept + { + return m_message.c_str(); + } - private: - std::string m_message; - }; +private: + std::string m_message; +}; - class OptionSpecException : public OptionException +class OptionSpecException : public OptionException +{ +public: + OptionSpecException(const std::string &message) + : OptionException(message) { - public: + } +}; - OptionSpecException(const std::string& message) - : OptionException(message) - { - } - }; +class OptionParseException : public OptionException +{ +public: + OptionParseException(const std::string &message) + : OptionException(message) + { + } +}; - class OptionParseException : public OptionException +class option_exists_error : public OptionSpecException +{ +public: + option_exists_error(const std::string &option) + : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") { - public: - OptionParseException(const std::string& message) - : OptionException(message) - { - } - }; + } +}; - class option_exists_error : public OptionSpecException +class invalid_option_format_error : public OptionSpecException +{ +public: + invalid_option_format_error(const std::string &format) + : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) { - public: - option_exists_error(const std::string& option) - : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") - { - } - }; + } +}; - class invalid_option_format_error : public OptionSpecException +class option_syntax_exception : public OptionParseException +{ +public: + option_syntax_exception(const std::string &text) + : OptionParseException("Argument " + LQUOTE + text + RQUOTE + + " starts with a - but has incorrect syntax") { - public: - invalid_option_format_error(const std::string& format) - : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) - { - } - }; + } +}; - class option_syntax_exception : public OptionParseException { - public: - option_syntax_exception(const std::string& text) - : OptionParseException("Argument " + LQUOTE + text + RQUOTE + - " starts with a - but has incorrect syntax") - { - } - }; +class option_not_exists_exception : public OptionParseException +{ +public: + option_not_exists_exception(const std::string &option) + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") + { + } +}; - class option_not_exists_exception : public OptionParseException +class missing_argument_exception : public OptionParseException +{ +public: + missing_argument_exception(const std::string &option) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + " is missing an argument") { - public: - option_not_exists_exception(const std::string& option) - : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") - { - } - }; + } +}; - class missing_argument_exception : public OptionParseException +class option_requires_argument_exception : public OptionParseException +{ +public: + option_requires_argument_exception(const std::string &option) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + " requires an argument") { - public: - missing_argument_exception(const std::string& option) - : OptionParseException( - "Option " + LQUOTE + option + RQUOTE + " is missing an argument" - ) - { - } - }; + } +}; - class option_requires_argument_exception : public OptionParseException +class option_not_has_argument_exception : public OptionParseException +{ +public: + option_not_has_argument_exception( + const std::string &option, + const std::string &arg) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + + " does not take an argument, but argument " + + LQUOTE + arg + RQUOTE + " given") { - public: - option_requires_argument_exception(const std::string& option) - : OptionParseException( - "Option " + LQUOTE + option + RQUOTE + " requires an argument" - ) - { - } - }; - - class option_not_has_argument_exception : public OptionParseException - { - public: - option_not_has_argument_exception - ( - const std::string& option, - const std::string& arg - ) - : OptionParseException( - "Option " + LQUOTE + option + RQUOTE + - " does not take an argument, but argument " + - LQUOTE + arg + RQUOTE + " given" - ) - { - } - }; + } +}; - class option_not_present_exception : public OptionParseException +class option_not_present_exception : public OptionParseException +{ +public: + option_not_present_exception(const std::string &option) + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") { - public: - option_not_present_exception(const std::string& option) - : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") - { - } - }; - - class argument_incorrect_type : public OptionParseException - { - public: - argument_incorrect_type - ( - const std::string& arg - ) - : OptionParseException( - "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" - ) - { - } - }; + } +}; - class option_required_exception : public OptionParseException +class argument_incorrect_type : public OptionParseException +{ +public: + argument_incorrect_type( + const std::string &arg) + : OptionParseException( + "Argument " + LQUOTE + arg + RQUOTE + " failed to parse") { - public: - option_required_exception(const std::string& option) - : OptionParseException( - "Option " + LQUOTE + option + RQUOTE + " is required but not present" - ) - { - } - }; + } +}; - template <typename T> - void throw_or_mimic(const std::string& text) +class option_required_exception : public OptionParseException +{ +public: + option_required_exception(const std::string &option) + : OptionParseException( + "Option " + LQUOTE + option + RQUOTE + " is required but not present") { - static_assert(std::is_base_of<std::exception, T>::value, - "throw_or_mimic only works on std::exception and " - "deriving classes"); + } +}; + +template <typename T> +void throw_or_mimic(const std::string &text) +{ + static_assert(std::is_base_of<std::exception, T>::value, + "throw_or_mimic only works on std::exception and " + "deriving classes"); #ifndef CXXOPTS_NO_EXCEPTIONS - // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw - throw T{text}; + // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw + throw T{text}; #else - // Otherwise manually instantiate the exception, print what() to stderr, + // Otherwise manually instantiate the exception, print what() to stderr, // and abort T exception{text}; std::cerr << exception.what() << std::endl; std::cerr << "Aborting (exceptions disabled)..." << std::endl; std::abort(); #endif - } - - namespace values - { - namespace - { - std::basic_regex<char> integer_pattern - ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); - std::basic_regex<char> truthy_pattern - ("(t|T)(rue)?|1"); - std::basic_regex<char> falsy_pattern - ("(f|F)(alse)?|0"); - } +} - namespace detail - { - template <typename T, bool B> - struct SignedCheck; +namespace values +{ +namespace +{ +std::basic_regex<char> integer_pattern("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); +std::basic_regex<char> truthy_pattern("(t|T)(rue)?|1"); +std::basic_regex<char> falsy_pattern("(f|F)(alse)?|0"); +} // namespace - template <typename T> - struct SignedCheck<T, true> - { - template <typename U> - void - operator()(bool negative, U u, const std::string& text) - { - if (negative) - { - if (u > static_cast<U>((std::numeric_limits<T>::min)())) - { - throw_or_mimic<argument_incorrect_type>(text); - } - } - else - { - if (u > static_cast<U>((std::numeric_limits<T>::max)())) - { - throw_or_mimic<argument_incorrect_type>(text); - } - } - } - }; +namespace detail +{ +template <typename T, bool B> +struct SignedCheck; - template <typename T> - struct SignedCheck<T, false> - { - template <typename U> - void - operator()(bool, U, const std::string&) {} - }; - - template <typename T, typename U> - void - check_signed_range(bool negative, U value, const std::string& text) +template <typename T> +struct SignedCheck<T, true> +{ + template <typename U> + void + operator()(bool negative, U u, const std::string &text) + { + if (negative) + { + if (u > static_cast<U>((std::numeric_limits<T>::min)())) { - SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text); + throw_or_mimic<argument_incorrect_type>(text); } } - - template <typename R, typename T> - R - checked_negate(T&& t, const std::string&, std::true_type) - { - // if we got to here, then `t` is a positive number that fits into - // `R`. So to avoid MSVC C4146, we first cast it to `R`. - // See https://github.com/jarro2783/cxxopts/issues/62 for more details. - return static_cast<R>(-static_cast<R>(t-1)-1); - } - - template <typename R, typename T> - T - checked_negate(T&& t, const std::string& text, std::false_type) - { - throw_or_mimic<argument_incorrect_type>(text); - return t; - } - - template <typename T> - void - integer_parser(const std::string& text, T& value) + else { - std::smatch match; - std::regex_match(text, match, integer_pattern); - - if (match.length() == 0) + if (u > static_cast<U>((std::numeric_limits<T>::max)())) { throw_or_mimic<argument_incorrect_type>(text); } + } + } +}; - if (match.length(4) > 0) - { - value = 0; - return; - } - - using US = typename std::make_unsigned<T>::type; - - constexpr bool is_signed = std::numeric_limits<T>::is_signed; - const bool negative = match.length(1) > 0; - const uint8_t base = match.length(2) > 0 ? 16 : 10; - - auto value_match = match[3]; - - US result = 0; - - for (auto iter = value_match.first; iter != value_match.second; ++iter) - { - US digit = 0; +template <typename T> +struct SignedCheck<T, false> +{ + template <typename U> + void + operator()(bool, U, const std::string &) {} +}; - if (*iter >= '0' && *iter <= '9') - { - digit = static_cast<US>(*iter - '0'); - } - else if (base == 16 && *iter >= 'a' && *iter <= 'f') - { - digit = static_cast<US>(*iter - 'a' + 10); - } - else if (base == 16 && *iter >= 'A' && *iter <= 'F') - { - digit = static_cast<US>(*iter - 'A' + 10); - } - else - { - throw_or_mimic<argument_incorrect_type>(text); - } +template <typename T, typename U> +void check_signed_range(bool negative, U value, const std::string &text) +{ + SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text); +} +} // namespace detail - const US next = static_cast<US>(result * base + digit); - if (result > next) - { - throw_or_mimic<argument_incorrect_type>(text); - } +template <typename R, typename T> +R checked_negate(T &&t, const std::string &, std::true_type) +{ + // if we got to here, then `t` is a positive number that fits into + // `R`. So to avoid MSVC C4146, we first cast it to `R`. + // See https://github.com/jarro2783/cxxopts/issues/62 for more details. + return static_cast<R>(-static_cast<R>(t - 1) - 1); +} - result = next; - } +template <typename R, typename T> +T checked_negate(T &&t, const std::string &text, std::false_type) +{ + throw_or_mimic<argument_incorrect_type>(text); + return t; +} - detail::check_signed_range<T>(negative, result, text); +template <typename T> +void integer_parser(const std::string &text, T &value) +{ + std::smatch match; + std::regex_match(text, match, integer_pattern); - if (negative) - { - value = checked_negate<T>(result, - text, - std::integral_constant<bool, is_signed>()); - } - else - { - value = static_cast<T>(result); - } - } + if (match.length() == 0) + { + throw_or_mimic<argument_incorrect_type>(text); + } - template <typename T> - void stringstream_parser(const std::string& text, T& value) - { - std::stringstream in(text); - in >> value; - if (!in) { - throw_or_mimic<argument_incorrect_type>(text); - } - } + if (match.length(4) > 0) + { + value = 0; + return; + } - inline - void - parse_value(const std::string& text, uint8_t& value) - { - integer_parser(text, value); - } + using US = typename std::make_unsigned<T>::type; - inline - void - parse_value(const std::string& text, int8_t& value) - { - integer_parser(text, value); - } + constexpr bool is_signed = std::numeric_limits<T>::is_signed; + const bool negative = match.length(1) > 0; + const uint8_t base = match.length(2) > 0 ? 16 : 10; - inline - void - parse_value(const std::string& text, uint16_t& value) - { - integer_parser(text, value); - } + auto value_match = match[3]; - inline - void - parse_value(const std::string& text, int16_t& value) - { - integer_parser(text, value); - } + US result = 0; - inline - void - parse_value(const std::string& text, uint32_t& value) - { - integer_parser(text, value); - } + for (auto iter = value_match.first; iter != value_match.second; ++iter) + { + US digit = 0; - inline - void - parse_value(const std::string& text, int32_t& value) + if (*iter >= '0' && *iter <= '9') { - integer_parser(text, value); + digit = static_cast<US>(*iter - '0'); } - - inline - void - parse_value(const std::string& text, uint64_t& value) + else if (base == 16 && *iter >= 'a' && *iter <= 'f') { - integer_parser(text, value); + digit = static_cast<US>(*iter - 'a' + 10); } - - inline - void - parse_value(const std::string& text, int64_t& value) + else if (base == 16 && *iter >= 'A' && *iter <= 'F') { - integer_parser(text, value); + digit = static_cast<US>(*iter - 'A' + 10); } - - inline - void - parse_value(const std::string& text, bool& value) + else { - std::smatch result; - std::regex_match(text, result, truthy_pattern); - - if (!result.empty()) - { - value = true; - return; - } - - std::regex_match(text, result, falsy_pattern); - if (!result.empty()) - { - value = false; - return; - } - throw_or_mimic<argument_incorrect_type>(text); } - inline - void - parse_value(const std::string& text, std::string& value) + const US next = static_cast<US>(result * base + digit); + if (result > next) { - value = text; + throw_or_mimic<argument_incorrect_type>(text); } - // The fallback parser. It uses the stringstream parser to parse all types - // that have not been overloaded explicitly. It has to be placed in the - // source code before all other more specialized templates. - template <typename T> - void - parse_value(const std::string& text, T& value) { - stringstream_parser(text, value); - } + result = next; + } - template <typename T> - void - parse_value(const std::string& text, std::vector<T>& value) - { - std::stringstream in(text); - std::string token; - while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { - T v; - parse_value(token, v); - value.emplace_back(std::move(v)); - } - } + detail::check_signed_range<T>(negative, result, text); -#ifdef CXXOPTS_HAS_OPTIONAL - template <typename T> - void - parse_value(const std::string& text, std::optional<T>& value) + if (negative) { - T result; - parse_value(text, result); - value = std::move(result); + value = checked_negate<T>(result, + text, + std::integral_constant<bool, is_signed>()); } -#endif + else + { + value = static_cast<T>(result); + } +} - inline - void parse_value(const std::string& text, char& c) - { - if (text.length() != 1) - { - throw_or_mimic<argument_incorrect_type>(text); - } +template <typename T> +void stringstream_parser(const std::string &text, T &value) +{ + std::stringstream in(text); + in >> value; + if (!in) + { + throw_or_mimic<argument_incorrect_type>(text); + } +} - c = text[0]; - } +inline void +parse_value(const std::string &text, uint8_t &value) +{ + integer_parser(text, value); +} - template <typename T> - struct type_is_container - { - static constexpr bool value = false; - }; +inline void +parse_value(const std::string &text, int8_t &value) +{ + integer_parser(text, value); +} - template <typename T> - struct type_is_container<std::vector<T>> - { - static constexpr bool value = true; - }; +inline void +parse_value(const std::string &text, uint16_t &value) +{ + integer_parser(text, value); +} - template <typename T> - class abstract_value : public Value - { - using Self = abstract_value<T>; +inline void +parse_value(const std::string &text, int16_t &value) +{ + integer_parser(text, value); +} - public: - abstract_value() - : m_result(std::make_shared<T>()) - , m_store(m_result.get()) - { - } +inline void +parse_value(const std::string &text, uint32_t &value) +{ + integer_parser(text, value); +} - abstract_value(T* t) - : m_store(t) - { - } +inline void +parse_value(const std::string &text, int32_t &value) +{ + integer_parser(text, value); +} - virtual ~abstract_value() = default; +inline void +parse_value(const std::string &text, uint64_t &value) +{ + integer_parser(text, value); +} - abstract_value(const abstract_value& rhs) - { - if (rhs.m_result) - { - m_result = std::make_shared<T>(); - m_store = m_result.get(); - } - else - { - m_store = rhs.m_store; - } +inline void +parse_value(const std::string &text, int64_t &value) +{ + integer_parser(text, value); +} - m_default = rhs.m_default; - m_implicit = rhs.m_implicit; - m_default_value = rhs.m_default_value; - m_implicit_value = rhs.m_implicit_value; - } +inline void +parse_value(const std::string &text, bool &value) +{ + std::smatch result; + std::regex_match(text, result, truthy_pattern); - void - parse(const std::string& text) const - { - parse_value(text, *m_store); - } + if (!result.empty()) + { + value = true; + return; + } - bool - is_container() const - { - return type_is_container<T>::value; - } + std::regex_match(text, result, falsy_pattern); + if (!result.empty()) + { + value = false; + return; + } - void - parse() const - { - parse_value(m_default_value, *m_store); - } + throw_or_mimic<argument_incorrect_type>(text); +} - bool - has_default() const - { - return m_default; - } +inline void +parse_value(const std::string &text, std::string &value) +{ + value = text; +} - bool - has_implicit() const - { - return m_implicit; - } +// The fallback parser. It uses the stringstream parser to parse all types +// that have not been overloaded explicitly. It has to be placed in the +// source code before all other more specialized templates. +template <typename T> +void parse_value(const std::string &text, T &value) +{ + stringstream_parser(text, value); +} - std::shared_ptr<Value> - default_value(const std::string& value) - { - m_default = true; - m_default_value = value; - return shared_from_this(); - } +template <typename T> +void parse_value(const std::string &text, std::vector<T> &value) +{ + std::stringstream in(text); + std::string token; + while (in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) + { + T v; + parse_value(token, v); + value.emplace_back(std::move(v)); + } +} - std::shared_ptr<Value> - implicit_value(const std::string& value) - { - m_implicit = true; - m_implicit_value = value; - return shared_from_this(); - } +#ifdef CXXOPTS_HAS_OPTIONAL +template <typename T> +void parse_value(const std::string &text, std::optional<T> &value) +{ + T result; + parse_value(text, result); + value = std::move(result); +} +#endif - std::shared_ptr<Value> - no_implicit_value() - { - m_implicit = false; - return shared_from_this(); - } +inline void parse_value(const std::string &text, char &c) +{ + if (text.length() != 1) + { + throw_or_mimic<argument_incorrect_type>(text); + } - std::string - get_default_value() const - { - return m_default_value; - } + c = text[0]; +} - std::string - get_implicit_value() const - { - return m_implicit_value; - } +template <typename T> +struct type_is_container +{ + static constexpr bool value = false; +}; - bool - is_boolean() const - { - return std::is_same<T, bool>::value; - } +template <typename T> +struct type_is_container<std::vector<T>> +{ + static constexpr bool value = true; +}; - const T& - get() const - { - if (m_store == nullptr) - { - return *m_result; - } - else - { - return *m_store; - } - } +template <typename T> +class abstract_value : public Value +{ + using Self = abstract_value<T>; - protected: - std::shared_ptr<T> m_result; - T* m_store; +public: + abstract_value() + : m_result(std::make_shared<T>()), m_store(m_result.get()) + { + } - bool m_default = false; - bool m_implicit = false; + abstract_value(T *t) + : m_store(t) + { + } - std::string m_default_value; - std::string m_implicit_value; - }; + virtual ~abstract_value() = default; - template <typename T> - class standard_value : public abstract_value<T> + abstract_value(const abstract_value &rhs) + { + if (rhs.m_result) { - public: - using abstract_value<T>::abstract_value; + m_result = std::make_shared<T>(); + m_store = m_result.get(); + } + else + { + m_store = rhs.m_store; + } - std::shared_ptr<Value> - clone() const - { - return std::make_shared<standard_value<T>>(*this); - } - }; + m_default = rhs.m_default; + m_implicit = rhs.m_implicit; + m_default_value = rhs.m_default_value; + m_implicit_value = rhs.m_implicit_value; + } - template <> - class standard_value<bool> : public abstract_value<bool> - { - public: - ~standard_value() = default; + void + parse(const std::string &text) const + { + parse_value(text, *m_store); + } - standard_value() - { - set_default_and_implicit(); - } + bool + is_container() const + { + return type_is_container<T>::value; + } - standard_value(bool* b) - : abstract_value(b) - { - set_default_and_implicit(); - } + void + parse() const + { + parse_value(m_default_value, *m_store); + } - std::shared_ptr<Value> - clone() const - { - return std::make_shared<standard_value<bool>>(*this); - } + bool + has_default() const + { + return m_default; + } - private: + bool + has_implicit() const + { + return m_implicit; + } - void - set_default_and_implicit() - { - m_default = true; - m_default_value = "false"; - m_implicit = true; - m_implicit_value = "true"; - } - }; + std::shared_ptr<Value> + default_value(const std::string &value) + { + m_default = true; + m_default_value = value; + return shared_from_this(); } - template <typename T> std::shared_ptr<Value> - value() + implicit_value(const std::string &value) { - return std::make_shared<values::standard_value<T>>(); + m_implicit = true; + m_implicit_value = value; + return shared_from_this(); } - template <typename T> std::shared_ptr<Value> - value(T& t) + no_implicit_value() { - return std::make_shared<values::standard_value<T>>(&t); + m_implicit = false; + return shared_from_this(); } - class OptionAdder; + std::string + get_default_value() const + { + return m_default_value; + } - class OptionDetails + std::string + get_implicit_value() const { - public: - OptionDetails - ( - const std::string& short_, - const std::string& long_, - const String& desc, - std::shared_ptr<const Value> val - ) - : m_short(short_) - , m_long(long_) - , m_desc(desc) - , m_value(val) - , m_count(0) + return m_implicit_value; + } + + bool + is_boolean() const + { + return std::is_same<T, bool>::value; + } + + const T & + get() const + { + if (m_store == nullptr) { + return *m_result; } - - OptionDetails(const OptionDetails& rhs) - : m_desc(rhs.m_desc) - , m_count(rhs.m_count) + else { - m_value = rhs.m_value->clone(); + return *m_store; } + } - OptionDetails(OptionDetails&& rhs) = default; +protected: + std::shared_ptr<T> m_result; + T *m_store; - const String& - description() const - { - return m_desc; - } + bool m_default = false; + bool m_implicit = false; - const Value& value() const { - return *m_value; - } + std::string m_default_value; + std::string m_implicit_value; +}; - std::shared_ptr<Value> - make_storage() const - { - return m_value->clone(); - } +template <typename T> +class standard_value : public abstract_value<T> +{ +public: + using abstract_value<T>::abstract_value; - const std::string& - short_name() const - { - return m_short; - } + std::shared_ptr<Value> + clone() const + { + return std::make_shared<standard_value<T>>(*this); + } +}; - const std::string& - long_name() const - { - return m_long; - } +template <> +class standard_value<bool> : public abstract_value<bool> +{ +public: + ~standard_value() = default; - private: - std::string m_short; - std::string m_long; - String m_desc; - std::shared_ptr<const Value> m_value; - int m_count; - }; - - struct HelpOptionDetails - { - std::string s; - std::string l; - String desc; - bool has_default; - std::string default_value; - bool has_implicit; - std::string implicit_value; - std::string arg_help; - bool is_container; - bool is_boolean; - }; - - struct HelpGroupDetails - { - std::string name; - std::string description; - std::vector<HelpOptionDetails> options; - }; - - class OptionValue - { - public: - void - parse - ( - std::shared_ptr<const OptionDetails> details, - const std::string& text - ) - { - ensure_value(details); - ++m_count; - m_value->parse(text); - } + standard_value() + { + set_default_and_implicit(); + } - void - parse_default(std::shared_ptr<const OptionDetails> details) - { - ensure_value(details); - m_default = true; - m_value->parse(); - } + standard_value(bool *b) + : abstract_value(b) + { + set_default_and_implicit(); + } - size_t - count() const noexcept - { - return m_count; - } + std::shared_ptr<Value> + clone() const + { + return std::make_shared<standard_value<bool>>(*this); + } - // TODO: maybe default options should count towards the number of arguments - bool - has_default() const noexcept - { - return m_default; - } +private: + void + set_default_and_implicit() + { + m_default = true; + m_default_value = "false"; + m_implicit = true; + m_implicit_value = "true"; + } +}; +} // namespace values - template <typename T> - const T& - as() const - { - if (m_value == nullptr) { - throw_or_mimic<std::domain_error>("No value"); - } +template <typename T> +std::shared_ptr<Value> +value() +{ + return std::make_shared<values::standard_value<T>>(); +} -#ifdef CXXOPTS_NO_RTTI - return static_cast<const values::standard_value<T>&>(*m_value).get(); -#else - return dynamic_cast<const values::standard_value<T>&>(*m_value).get(); -#endif - } +template <typename T> +std::shared_ptr<Value> +value(T &t) +{ + return std::make_shared<values::standard_value<T>>(&t); +} - private: - void - ensure_value(std::shared_ptr<const OptionDetails> details) - { - if (m_value == nullptr) - { - m_value = details->make_storage(); - } - } +class OptionAdder; - std::shared_ptr<Value> m_value; - size_t m_count = 0; - bool m_default = false; - }; +class OptionDetails +{ +public: + OptionDetails( + const std::string &short_, + const std::string &long_, + const String &desc, + std::shared_ptr<const Value> val) + : m_short(short_), m_long(long_), m_desc(desc), m_value(val), m_count(0) + { + } - class KeyValue + OptionDetails(const OptionDetails &rhs) + : m_desc(rhs.m_desc), m_count(rhs.m_count) { - public: - KeyValue(std::string key_, std::string value_) - : m_key(std::move(key_)) - , m_value(std::move(value_)) - { - } + m_value = rhs.m_value->clone(); + } - const - std::string& - key() const - { - return m_key; - } + OptionDetails(OptionDetails &&rhs) = default; - const - std::string& - value() const - { - return m_value; - } + const String & + description() const + { + return m_desc; + } - template <typename T> - T - as() const - { - T result; - values::parse_value(m_value, result); - return result; - } + const Value &value() const + { + return *m_value; + } - private: - std::string m_key; - std::string m_value; - }; + std::shared_ptr<Value> + make_storage() const + { + return m_value->clone(); + } - class ParseResult + const std::string & + short_name() const { - public: + return m_short; + } - ParseResult( - const std::shared_ptr< - std::unordered_map<std::string, std::shared_ptr<OptionDetails>> - >, - std::vector<std::string>, - bool allow_unrecognised, - int&, char**&); + const std::string & + long_name() const + { + return m_long; + } - size_t - count(const std::string& o) const - { - auto iter = m_options->find(o); - if (iter == m_options->end()) - { - return 0; - } +private: + std::string m_short; + std::string m_long; + String m_desc; + std::shared_ptr<const Value> m_value; + int m_count; +}; - auto riter = m_results.find(iter->second); +struct HelpOptionDetails +{ + std::string s; + std::string l; + String desc; + bool has_default; + std::string default_value; + bool has_implicit; + std::string implicit_value; + std::string arg_help; + bool is_container; + bool is_boolean; +}; + +struct HelpGroupDetails +{ + std::string name; + std::string description; + std::vector<HelpOptionDetails> options; +}; - return riter->second.count(); - } +class OptionValue +{ +public: + void + parse( + std::shared_ptr<const OptionDetails> details, + const std::string &text) + { + ensure_value(details); + ++m_count; + m_value->parse(text); + } - const OptionValue& - operator[](const std::string& option) const - { - auto iter = m_options->find(option); + void + parse_default(std::shared_ptr<const OptionDetails> details) + { + ensure_value(details); + m_default = true; + m_value->parse(); + } - if (iter == m_options->end()) - { - throw_or_mimic<option_not_present_exception>(option); - } + size_t + count() const noexcept + { + return m_count; + } - auto riter = m_results.find(iter->second); + // TODO: maybe default options should count towards the number of arguments + bool + has_default() const noexcept + { + return m_default; + } - return riter->second; + template <typename T> + const T & + as() const + { + if (m_value == nullptr) + { + throw_or_mimic<std::domain_error>("No value"); } - const std::vector<KeyValue>& - arguments() const +#ifdef CXXOPTS_NO_RTTI + return static_cast<const values::standard_value<T> &>(*m_value).get(); +#else + return dynamic_cast<const values::standard_value<T> &>(*m_value).get(); +#endif + } + +private: + void + ensure_value(std::shared_ptr<const OptionDetails> details) + { + if (m_value == nullptr) { - return m_sequential; + m_value = details->make_storage(); } + } - private: - - void - parse(int& argc, char**& argv); + std::shared_ptr<Value> m_value; + size_t m_count = 0; + bool m_default = false; +}; - void - add_to_option(const std::string& option, const std::string& arg); +class KeyValue +{ +public: + KeyValue(std::string key_, std::string value_) + : m_key(std::move(key_)), m_value(std::move(value_)) + { + } - bool - consume_positional(std::string a); + const std::string & + key() const + { + return m_key; + } - void - parse_option - ( - std::shared_ptr<OptionDetails> value, - const std::string& name, - const std::string& arg = "" - ); + const std::string & + value() const + { + return m_value; + } - void - parse_default(std::shared_ptr<OptionDetails> details); + template <typename T> + T as() const + { + T result; + values::parse_value(m_value, result); + return result; + } - void - checked_parse_arg - ( - int argc, - char* argv[], - int& current, - std::shared_ptr<OptionDetails> value, - const std::string& name - ); +private: + std::string m_key; + std::string m_value; +}; +class ParseResult +{ +public: + ParseResult( const std::shared_ptr< - std::unordered_map<std::string, std::shared_ptr<OptionDetails>> - > m_options; - std::vector<std::string> m_positional; - std::vector<std::string>::iterator m_next_positional; - std::unordered_set<std::string> m_positional_set; - std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results; - - bool m_allow_unrecognised; - - std::vector<KeyValue> m_sequential; - }; - - struct Option - { - Option - ( - const std::string& opts, - const std::string& desc, - const std::shared_ptr<const Value>& value = ::cxxopts::value<bool>(), - const std::string& arg_help = "" - ) - : opts_(opts) - , desc_(desc) - , value_(value) - , arg_help_(arg_help) - { - } + std::unordered_map<std::string, std::shared_ptr<OptionDetails>>>, + std::vector<std::string>, + bool allow_unrecognised, + int &, char **&); - std::string opts_; - std::string desc_; - std::shared_ptr<const Value> value_; - std::string arg_help_; - }; - - class Options - { - typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>> - OptionMap; - public: - - Options(std::string program, std::string help_string = "") - : m_program(std::move(program)) - , m_help_string(toLocalString(std::move(help_string))) - , m_custom_help("[OPTION...]") - , m_positional_help("positional parameters") - , m_show_positional(false) - , m_allow_unrecognised(false) - , m_options(std::make_shared<OptionMap>()) - , m_next_positional(m_positional.end()) + size_t + count(const std::string &o) const + { + auto iter = m_options->find(o); + if (iter == m_options->end()) { + return 0; } - Options& - positional_help(std::string help_text) - { - m_positional_help = std::move(help_text); - return *this; - } + auto riter = m_results.find(iter->second); - Options& - custom_help(std::string help_text) - { - m_custom_help = std::move(help_text); - return *this; - } + return riter->second.count(); + } - Options& - show_positional_help() - { - m_show_positional = true; - return *this; - } + const OptionValue & + operator[](const std::string &option) const + { + auto iter = m_options->find(option); - Options& - allow_unrecognised_options() + if (iter == m_options->end()) { - m_allow_unrecognised = true; - return *this; + throw_or_mimic<option_not_present_exception>(option); } - ParseResult - parse(int& argc, char**& argv); - - OptionAdder - add_options(std::string group = ""); - - void - add_options - ( - const std::string& group, - std::initializer_list<Option> options - ); - - void - add_option - ( - const std::string& group, - const Option& option - ); - - void - add_option - ( - const std::string& group, - const std::string& s, - const std::string& l, - std::string desc, - std::shared_ptr<const Value> value, - std::string arg_help - ); - - //parse positional arguments into the given option - void - parse_positional(std::string option); - - void - parse_positional(std::vector<std::string> options); - - void - parse_positional(std::initializer_list<std::string> options); - - template <typename Iterator> - void - parse_positional(Iterator begin, Iterator end) { - parse_positional(std::vector<std::string>{begin, end}); - } + auto riter = m_results.find(iter->second); - std::string - help(const std::vector<std::string>& groups = {}) const; + return riter->second; + } - const std::vector<std::string> - groups() const; + const std::vector<KeyValue> & + arguments() const + { + return m_sequential; + } - const HelpGroupDetails& - group_help(const std::string& group) const; +private: + void + parse(int &argc, char **&argv); - private: + void + add_to_option(const std::string &option, const std::string &arg); - void - add_one_option - ( - const std::string& option, - std::shared_ptr<OptionDetails> details - ); + bool + consume_positional(std::string a); - String - help_one_group(const std::string& group) const; + void + parse_option( + std::shared_ptr<OptionDetails> value, + const std::string &name, + const std::string &arg = ""); - void - generate_group_help - ( - String& result, - const std::vector<std::string>& groups - ) const; + void + parse_default(std::shared_ptr<OptionDetails> details); - void - generate_all_groups_help(String& result) const; + void + checked_parse_arg( + int argc, + char *argv[], + int ¤t, + std::shared_ptr<OptionDetails> value, + const std::string &name); + + const std::shared_ptr< + std::unordered_map<std::string, std::shared_ptr<OptionDetails>>> + m_options; + std::vector<std::string> m_positional; + std::vector<std::string>::iterator m_next_positional; + std::unordered_set<std::string> m_positional_set; + std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results; + + bool m_allow_unrecognised; + + std::vector<KeyValue> m_sequential; +}; + +struct Option +{ + Option( + const std::string &opts, + const std::string &desc, + const std::shared_ptr<const Value> &value = ::cxxopts::value<bool>(), + const std::string &arg_help = "") + : opts_(opts), desc_(desc), value_(value), arg_help_(arg_help) + { + } - std::string m_program; - String m_help_string; - std::string m_custom_help; - std::string m_positional_help; - bool m_show_positional; - bool m_allow_unrecognised; + std::string opts_; + std::string desc_; + std::shared_ptr<const Value> value_; + std::string arg_help_; +}; - std::shared_ptr<OptionMap> m_options; - std::vector<std::string> m_positional; - std::vector<std::string>::iterator m_next_positional; - std::unordered_set<std::string> m_positional_set; +class Options +{ + typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>> + OptionMap; - //mapping from groups to help options - std::map<std::string, HelpGroupDetails> m_help; - }; +public: + Options(std::string program, std::string help_string = "") + : m_program(std::move(program)), m_help_string(toLocalString(std::move(help_string))), m_custom_help("[OPTION...]"), m_positional_help("positional parameters"), m_show_positional(false), m_allow_unrecognised(false), m_options(std::make_shared<OptionMap>()), m_next_positional(m_positional.end()) + { + } - class OptionAdder + Options & + positional_help(std::string help_text) { - public: + m_positional_help = std::move(help_text); + return *this; + } - OptionAdder(Options& options, std::string group) - : m_options(options), m_group(std::move(group)) - { - } + Options & + custom_help(std::string help_text) + { + m_custom_help = std::move(help_text); + return *this; + } - OptionAdder& - operator() - ( - const std::string& opts, - const std::string& desc, - std::shared_ptr<const Value> value - = ::cxxopts::value<bool>(), - std::string arg_help = "" - ); - - private: - Options& m_options; - std::string m_group; - }; - - namespace - { - constexpr int OPTION_LONGEST = 30; - constexpr int OPTION_DESC_GAP = 2; - - std::basic_regex<char> option_matcher - ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); - - std::basic_regex<char> option_specifier - ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); - - String - format_option - ( - const HelpOptionDetails& o - ) - { - auto& s = o.s; - auto& l = o.l; + Options & + show_positional_help() + { + m_show_positional = true; + return *this; + } - String result = " "; + Options & + allow_unrecognised_options() + { + m_allow_unrecognised = true; + return *this; + } - if (s.size() > 0) - { - result += "-" + toLocalString(s) + ","; - } - else - { - result += " "; - } + ParseResult + parse(int &argc, char **&argv); - if (l.size() > 0) - { - result += " --" + toLocalString(l); - } + OptionAdder + add_options(std::string group = ""); - auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; + void + add_options( + const std::string &group, + std::initializer_list<Option> options); - if (!o.is_boolean) - { - if (o.has_implicit) - { - result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; - } - else - { - result += " " + arg; - } - } + void + add_option( + const std::string &group, + const Option &option); - return result; - } + void + add_option( + const std::string &group, + const std::string &s, + const std::string &l, + std::string desc, + std::shared_ptr<const Value> value, + std::string arg_help); + + //parse positional arguments into the given option + void + parse_positional(std::string option); - String - format_description - ( - const HelpOptionDetails& o, - size_t start, - size_t width - ) - { - auto desc = o.desc; + void + parse_positional(std::vector<std::string> options); - if (o.has_default && (!o.is_boolean || o.default_value != "false")) - { - if(o.default_value != "") - { - desc += toLocalString(" (default: " + o.default_value + ")"); - } - else - { - desc += toLocalString(" (default: \"\")"); - } - } + void + parse_positional(std::initializer_list<std::string> options); - String result; + template <typename Iterator> + void + parse_positional(Iterator begin, Iterator end) + { + parse_positional(std::vector<std::string>{begin, end}); + } - auto current = std::begin(desc); - auto startLine = current; - auto lastSpace = current; + std::string + help(const std::vector<std::string> &groups = {}) const; - auto size = size_t{}; + const std::vector<std::string> + groups() const; - while (current != std::end(desc)) - { - if (*current == ' ') - { - lastSpace = current; - } + const HelpGroupDetails & + group_help(const std::string &group) const; - if (*current == '\n') - { - startLine = current + 1; - lastSpace = startLine; - } - else if (size > width) - { - if (lastSpace == startLine) - { - stringAppend(result, startLine, current + 1); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = current + 1; - lastSpace = startLine; - } - else - { - stringAppend(result, startLine, lastSpace); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = lastSpace + 1; - lastSpace = startLine; - } - size = 0; - } - else - { - ++size; - } +private: + void + add_one_option( + const std::string &option, + std::shared_ptr<OptionDetails> details); - ++current; - } + String + help_one_group(const std::string &group) const; - //append whatever is left - stringAppend(result, startLine, current); + void + generate_group_help( + String &result, + const std::vector<std::string> &groups) const; - return result; - } - } + void + generate_all_groups_help(String &result) const; + + std::string m_program; + String m_help_string; + std::string m_custom_help; + std::string m_positional_help; + bool m_show_positional; + bool m_allow_unrecognised; + + std::shared_ptr<OptionMap> m_options; + std::vector<std::string> m_positional; + std::vector<std::string>::iterator m_next_positional; + std::unordered_set<std::string> m_positional_set; + + //mapping from groups to help options + std::map<std::string, HelpGroupDetails> m_help; +}; - inline - ParseResult::ParseResult - ( - const std::shared_ptr< - std::unordered_map<std::string, std::shared_ptr<OptionDetails>> - > options, - std::vector<std::string> positional, - bool allow_unrecognised, - int& argc, char**& argv - ) - : m_options(options) - , m_positional(std::move(positional)) - , m_next_positional(m_positional.begin()) - , m_allow_unrecognised(allow_unrecognised) +class OptionAdder +{ +public: + OptionAdder(Options &options, std::string group) + : m_options(options), m_group(std::move(group)) { - parse(argc, argv); } - inline - void - Options::add_options - ( - const std::string &group, - std::initializer_list<Option> options - ) - { - OptionAdder option_adder(*this, group); - for (const auto &option: options) - { - option_adder(option.opts_, option.desc_, option.value_, option.arg_help_); - } - } + OptionAdder & + operator()( + const std::string &opts, + const std::string &desc, + std::shared_ptr<const Value> value = ::cxxopts::value<bool>(), + std::string arg_help = ""); - inline - OptionAdder - Options::add_options(std::string group) +private: + Options &m_options; + std::string m_group; +}; + +namespace +{ +constexpr int OPTION_LONGEST = 30; +constexpr int OPTION_DESC_GAP = 2; + +std::basic_regex<char> option_matcher("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); + +std::basic_regex<char> option_specifier("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); + +String +format_option( + const HelpOptionDetails &o) +{ + auto &s = o.s; + auto &l = o.l; + + String result = " "; + + if (s.size() > 0) + { + result += "-" + toLocalString(s) + ","; + } + else { - return OptionAdder(*this, std::move(group)); + result += " "; } - inline - OptionAdder& - OptionAdder::operator() - ( - const std::string& opts, - const std::string& desc, - std::shared_ptr<const Value> value, - std::string arg_help - ) + if (l.size() > 0) { - std::match_results<const char*> result; - std::regex_match(opts.c_str(), result, option_specifier); + result += " --" + toLocalString(l); + } - if (result.empty()) + auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; + + if (!o.is_boolean) + { + if (o.has_implicit) + { + result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; + } + else { - throw_or_mimic<invalid_option_format_error>(opts); + result += " " + arg; } + } - const auto& short_match = result[2]; - const auto& long_match = result[3]; + return result; +} - if (!short_match.length() && !long_match.length()) +String +format_description( + const HelpOptionDetails &o, + size_t start, + size_t width) +{ + auto desc = o.desc; + + if (o.has_default && (!o.is_boolean || o.default_value != "false")) + { + if (o.default_value != "") { - throw_or_mimic<invalid_option_format_error>(opts); - } else if (long_match.length() == 1 && short_match.length()) + desc += toLocalString(" (default: " + o.default_value + ")"); + } + else + { + desc += toLocalString(" (default: \"\")"); + } + } + + String result; + + auto current = std::begin(desc); + auto startLine = current; + auto lastSpace = current; + + auto size = size_t{}; + + while (current != std::end(desc)) + { + if (*current == ' ') { - throw_or_mimic<invalid_option_format_error>(opts); + lastSpace = current; } - auto option_names = [] - ( - const std::sub_match<const char*>& short_, - const std::sub_match<const char*>& long_ - ) + if (*current == '\n') + { + startLine = current + 1; + lastSpace = startLine; + } + else if (size > width) { - if (long_.length() == 1) + if (lastSpace == startLine) { - return std::make_tuple(long_.str(), short_.str()); + stringAppend(result, startLine, current + 1); + stringAppend(result, "\n"); + stringAppend(result, start, ' '); + startLine = current + 1; + lastSpace = startLine; } else { - return std::make_tuple(short_.str(), long_.str()); + stringAppend(result, startLine, lastSpace); + stringAppend(result, "\n"); + stringAppend(result, start, ' '); + startLine = lastSpace + 1; + lastSpace = startLine; } - }(short_match, long_match); - - m_options.add_option - ( - m_group, - std::get<0>(option_names), - std::get<1>(option_names), - desc, - value, - std::move(arg_help) - ); + size = 0; + } + else + { + ++size; + } - return *this; + ++current; } - inline - void - ParseResult::parse_default(std::shared_ptr<OptionDetails> details) + //append whatever is left + stringAppend(result, startLine, current); + + return result; +} +} // namespace + +inline ParseResult::ParseResult( + const std::shared_ptr< + std::unordered_map<std::string, std::shared_ptr<OptionDetails>>> + options, + std::vector<std::string> positional, + bool allow_unrecognised, + int &argc, char **&argv) + : m_options(options), m_positional(std::move(positional)), m_next_positional(m_positional.begin()), m_allow_unrecognised(allow_unrecognised) +{ + parse(argc, argv); +} + +inline void +Options::add_options( + const std::string &group, + std::initializer_list<Option> options) +{ + OptionAdder option_adder(*this, group); + for (const auto &option : options) { - m_results[details].parse_default(details); + option_adder(option.opts_, option.desc_, option.value_, option.arg_help_); } +} - inline - void - ParseResult::parse_option - ( - std::shared_ptr<OptionDetails> value, - const std::string& /*name*/, - const std::string& arg - ) +inline OptionAdder +Options::add_options(std::string group) +{ + return OptionAdder(*this, std::move(group)); +} + +inline OptionAdder & +OptionAdder::operator()( + const std::string &opts, + const std::string &desc, + std::shared_ptr<const Value> value, + std::string arg_help) +{ + std::match_results<const char *> result; + std::regex_match(opts.c_str(), result, option_specifier); + + if (result.empty()) { - auto& result = m_results[value]; - result.parse(value, arg); + throw_or_mimic<invalid_option_format_error>(opts); + } + + const auto &short_match = result[2]; + const auto &long_match = result[3]; - m_sequential.emplace_back(value->long_name(), arg); + if (!short_match.length() && !long_match.length()) + { + throw_or_mimic<invalid_option_format_error>(opts); + } + else if (long_match.length() == 1 && short_match.length()) + { + throw_or_mimic<invalid_option_format_error>(opts); } - inline - void - ParseResult::checked_parse_arg - ( - int argc, - char* argv[], - int& current, - std::shared_ptr<OptionDetails> value, - const std::string& name - ) - { - if (current + 1 >= argc) + auto option_names = []( + const std::sub_match<const char *> &short_, + const std::sub_match<const char *> &long_) { + if (long_.length() == 1) { - if (value->value().has_implicit()) - { - parse_option(value, name, value->value().get_implicit_value()); - } - else - { - throw_or_mimic<missing_argument_exception>(name); - } + return std::make_tuple(long_.str(), short_.str()); } else { - if (value->value().has_implicit()) - { - parse_option(value, name, value->value().get_implicit_value()); - } - else - { - parse_option(value, name, argv[current + 1]); - ++current; - } + return std::make_tuple(short_.str(), long_.str()); } - } + }(short_match, long_match); - inline - void - ParseResult::add_to_option(const std::string& option, const std::string& arg) - { - auto iter = m_options->find(option); + m_options.add_option( + m_group, + std::get<0>(option_names), + std::get<1>(option_names), + desc, + value, + std::move(arg_help)); - if (iter == m_options->end()) + return *this; +} + +inline void +ParseResult::parse_default(std::shared_ptr<OptionDetails> details) +{ + m_results[details].parse_default(details); +} + +inline void +ParseResult::parse_option( + std::shared_ptr<OptionDetails> value, + const std::string & /*name*/, + const std::string &arg) +{ + auto &result = m_results[value]; + result.parse(value, arg); + + m_sequential.emplace_back(value->long_name(), arg); +} + +inline void +ParseResult::checked_parse_arg( + int argc, + char *argv[], + int ¤t, + std::shared_ptr<OptionDetails> value, + const std::string &name) +{ + if (current + 1 >= argc) + { + if (value->value().has_implicit()) + { + parse_option(value, name, value->value().get_implicit_value()); + } + else { - throw_or_mimic<option_not_exists_exception>(option); + throw_or_mimic<missing_argument_exception>(name); } + } + else + { + if (value->value().has_implicit()) + { + parse_option(value, name, value->value().get_implicit_value()); + } + else + { + parse_option(value, name, argv[current + 1]); + ++current; + } + } +} - parse_option(iter->second, option, arg); +inline void +ParseResult::add_to_option(const std::string &option, const std::string &arg) +{ + auto iter = m_options->find(option); + + if (iter == m_options->end()) + { + throw_or_mimic<option_not_exists_exception>(option); } - inline - bool - ParseResult::consume_positional(std::string a) + parse_option(iter->second, option, arg); +} + +inline bool +ParseResult::consume_positional(std::string a) +{ + while (m_next_positional != m_positional.end()) { - while (m_next_positional != m_positional.end()) + auto iter = m_options->find(*m_next_positional); + if (iter != m_options->end()) { - auto iter = m_options->find(*m_next_positional); - if (iter != m_options->end()) + auto &result = m_results[iter->second]; + if (!iter->second->value().is_container()) { - auto& result = m_results[iter->second]; - if (!iter->second->value().is_container()) + if (result.count() == 0) { - if (result.count() == 0) - { - add_to_option(*m_next_positional, a); - ++m_next_positional; - return true; - } - else - { - ++m_next_positional; - continue; - } + add_to_option(*m_next_positional, a); + ++m_next_positional; + return true; } else { - add_to_option(*m_next_positional, a); - return true; + ++m_next_positional; + continue; } } else { - throw_or_mimic<option_not_exists_exception>(*m_next_positional); + add_to_option(*m_next_positional, a); + return true; } } - - return false; + else + { + throw_or_mimic<option_not_exists_exception>(*m_next_positional); + } } - inline - void - Options::parse_positional(std::string option) - { - parse_positional(std::vector<std::string>{std::move(option)}); - } + return false; +} - inline - void - Options::parse_positional(std::vector<std::string> options) - { - m_positional = std::move(options); - m_next_positional = m_positional.begin(); +inline void +Options::parse_positional(std::string option) +{ + parse_positional(std::vector<std::string>{std::move(option)}); +} - m_positional_set.insert(m_positional.begin(), m_positional.end()); - } +inline void +Options::parse_positional(std::vector<std::string> options) +{ + m_positional = std::move(options); + m_next_positional = m_positional.begin(); - inline - void - Options::parse_positional(std::initializer_list<std::string> options) - { - parse_positional(std::vector<std::string>(std::move(options))); - } + m_positional_set.insert(m_positional.begin(), m_positional.end()); +} - inline - ParseResult - Options::parse(int& argc, char**& argv) - { - ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); - return result; - } +inline void +Options::parse_positional(std::initializer_list<std::string> options) +{ + parse_positional(std::vector<std::string>(std::move(options))); +} - inline - void - ParseResult::parse(int& argc, char**& argv) - { - int current = 1; +inline ParseResult +Options::parse(int &argc, char **&argv) +{ + ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); + return result; +} - int nextKeep = 1; +inline void +ParseResult::parse(int &argc, char **&argv) +{ + int current = 1; - bool consume_remaining = false; + int nextKeep = 1; - while (current != argc) - { - if (strcmp(argv[current], "--") == 0) - { - consume_remaining = true; - ++current; - break; - } + bool consume_remaining = false; - std::match_results<const char*> result; - std::regex_match(argv[current], result, option_matcher); + while (current != argc) + { + if (strcmp(argv[current], "--") == 0) + { + consume_remaining = true; + ++current; + break; + } - if (result.empty()) - { - //not a flag + std::match_results<const char *> result; + std::regex_match(argv[current], result, option_matcher); - // but if it starts with a `-`, then it's an error - if (argv[current][0] == '-' && argv[current][1] != '\0') { - if (!m_allow_unrecognised) { - throw_or_mimic<option_syntax_exception>(argv[current]); - } - } + if (result.empty()) + { + //not a flag - //if true is returned here then it was consumed, otherwise it is - //ignored - if (consume_positional(argv[current])) - { - } - else + // but if it starts with a `-`, then it's an error + if (argv[current][0] == '-' && argv[current][1] != '\0') + { + if (!m_allow_unrecognised) { - argv[nextKeep] = argv[current]; - ++nextKeep; + throw_or_mimic<option_syntax_exception>(argv[current]); } - //if we return from here then it was parsed successfully, so continue + } + + //if true is returned here then it was consumed, otherwise it is + //ignored + if (consume_positional(argv[current])) + { } else { - //short or long option? - if (result[4].length() != 0) - { - const std::string& s = result[4]; - - for (std::size_t i = 0; i != s.size(); ++i) - { - std::string name(1, s[i]); - auto iter = m_options->find(name); - - if (iter == m_options->end()) - { - if (m_allow_unrecognised) - { - continue; - } - else - { - //error - throw_or_mimic<option_not_exists_exception>(name); - } - } - - auto value = iter->second; + argv[nextKeep] = argv[current]; + ++nextKeep; + } + //if we return from here then it was parsed successfully, so continue + } + else + { + //short or long option? + if (result[4].length() != 0) + { + const std::string &s = result[4]; - if (i + 1 == s.size()) - { - //it must be the last argument - checked_parse_arg(argc, argv, current, value, name); - } - else if (value->value().has_implicit()) - { - parse_option(value, name, value->value().get_implicit_value()); - } - else - { - //error - throw_or_mimic<option_requires_argument_exception>(name); - } - } - } - else if (result[1].length() != 0) + for (std::size_t i = 0; i != s.size(); ++i) { - const std::string& name = result[1]; - + std::string name(1, s[i]); auto iter = m_options->find(name); if (iter == m_options->end()) { if (m_allow_unrecognised) { - // keep unrecognised options in argument list, skip to next argument - argv[nextKeep] = argv[current]; - ++nextKeep; - ++current; continue; } else @@ -1930,286 +1763,310 @@ namespace cxxopts } } - auto opt = iter->second; + auto value = iter->second; - //equals provided for long option? - if (result[2].length() != 0) + if (i + 1 == s.size()) { - //parse the option given - - parse_option(opt, name, result[3]); + //it must be the last argument + checked_parse_arg(argc, argv, current, value, name); + } + else if (value->value().has_implicit()) + { + parse_option(value, name, value->value().get_implicit_value()); } else { - //parse the next argument - checked_parse_arg(argc, argv, current, opt, name); + //error + throw_or_mimic<option_requires_argument_exception>(name); } } - } + else if (result[1].length() != 0) + { + const std::string &name = result[1]; - ++current; - } + auto iter = m_options->find(name); - for (auto& opt : *m_options) - { - auto& detail = opt.second; - auto& value = detail->value(); + if (iter == m_options->end()) + { + if (m_allow_unrecognised) + { + // keep unrecognised options in argument list, skip to next argument + argv[nextKeep] = argv[current]; + ++nextKeep; + ++current; + continue; + } + else + { + //error + throw_or_mimic<option_not_exists_exception>(name); + } + } - auto& store = m_results[detail]; + auto opt = iter->second; - if(value.has_default() && !store.count() && !store.has_default()){ - parse_default(detail); - } - } + //equals provided for long option? + if (result[2].length() != 0) + { + //parse the option given - if (consume_remaining) - { - while (current < argc) - { - if (!consume_positional(argv[current])) { - break; + parse_option(opt, name, result[3]); + } + else + { + //parse the next argument + checked_parse_arg(argc, argv, current, opt, name); } - ++current; - } - - //adjust argv for any that couldn't be swallowed - while (current != argc) { - argv[nextKeep] = argv[current]; - ++nextKeep; - ++current; } } - argc = nextKeep; - + ++current; } - inline - void - Options::add_option - ( - const std::string& group, - const Option& option - ) + for (auto &opt : *m_options) { - add_options(group, {option}); - } + auto &detail = opt.second; + auto &value = detail->value(); - inline - void - Options::add_option - ( - const std::string& group, - const std::string& s, - const std::string& l, - std::string desc, - std::shared_ptr<const Value> value, - std::string arg_help - ) - { - auto stringDesc = toLocalString(std::move(desc)); - auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value); - - if (s.size() > 0) - { - add_one_option(s, option); - } + auto &store = m_results[detail]; - if (l.size() > 0) + if (value.has_default() && !store.count() && !store.has_default()) { - add_one_option(l, option); + parse_default(detail); } - - //add the help details - auto& options = m_help[group]; - - options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, - value->has_default(), value->get_default_value(), - value->has_implicit(), value->get_implicit_value(), - std::move(arg_help), - value->is_container(), - value->is_boolean()}); } - inline - void - Options::add_one_option - ( - const std::string& option, - std::shared_ptr<OptionDetails> details - ) + if (consume_remaining) { - auto in = m_options->emplace(option, details); - - if (!in.second) + while (current < argc) { - throw_or_mimic<option_exists_error>(option); + if (!consume_positional(argv[current])) + { + break; + } + ++current; } - } - inline - String - Options::help_one_group(const std::string& g) const - { - typedef std::vector<std::pair<String, String>> OptionHelp; - - auto group = m_help.find(g); - if (group == m_help.end()) + //adjust argv for any that couldn't be swallowed + while (current != argc) { - return ""; + argv[nextKeep] = argv[current]; + ++nextKeep; + ++current; } + } - OptionHelp format; + argc = nextKeep; +} - size_t longest = 0; +inline void +Options::add_option( + const std::string &group, + const Option &option) +{ + add_options(group, {option}); +} - String result; +inline void +Options::add_option( + const std::string &group, + const std::string &s, + const std::string &l, + std::string desc, + std::shared_ptr<const Value> value, + std::string arg_help) +{ + auto stringDesc = toLocalString(std::move(desc)); + auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value); - if (!g.empty()) - { - result += toLocalString(" " + g + " options:\n"); - } + if (s.size() > 0) + { + add_one_option(s, option); + } - for (const auto& o : group->second.options) - { - if (m_positional_set.find(o.l) != m_positional_set.end() && - !m_show_positional) - { - continue; - } + if (l.size() > 0) + { + add_one_option(l, option); + } - auto s = format_option(o); - longest = (std::max)(longest, stringLength(s)); - format.push_back(std::make_pair(s, String())); - } + //add the help details + auto &options = m_help[group]; - longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST)); + options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, + value->has_default(), value->get_default_value(), + value->has_implicit(), value->get_implicit_value(), + std::move(arg_help), + value->is_container(), + value->is_boolean()}); +} - //widest allowed description - auto allowed = size_t{76} - longest - OPTION_DESC_GAP; +inline void +Options::add_one_option( + const std::string &option, + std::shared_ptr<OptionDetails> details) +{ + auto in = m_options->emplace(option, details); - auto fiter = format.begin(); - for (const auto& o : group->second.options) - { - if (m_positional_set.find(o.l) != m_positional_set.end() && - !m_show_positional) - { - continue; - } + if (!in.second) + { + throw_or_mimic<option_exists_error>(option); + } +} - auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); +inline String +Options::help_one_group(const std::string &g) const +{ + typedef std::vector<std::pair<String, String>> OptionHelp; - result += fiter->first; - if (stringLength(fiter->first) > longest) - { - result += '\n'; - result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); - } - else - { - result += toLocalString(std::string(longest + OPTION_DESC_GAP - - stringLength(fiter->first), - ' ')); - } - result += d; - result += '\n'; + auto group = m_help.find(g); + if (group == m_help.end()) + { + return ""; + } - ++fiter; - } + OptionHelp format; - return result; - } + size_t longest = 0; - inline - void - Options::generate_group_help - ( - String& result, - const std::vector<std::string>& print_groups - ) const + String result; + + if (!g.empty()) { - for (size_t i = 0; i != print_groups.size(); ++i) - { - const String& group_help_text = help_one_group(print_groups[i]); - if (empty(group_help_text)) - { - continue; - } - result += group_help_text; - if (i < print_groups.size() - 1) - { - result += '\n'; - } - } + result += toLocalString(" " + g + " options:\n"); } - inline - void - Options::generate_all_groups_help(String& result) const + for (const auto &o : group->second.options) { - std::vector<std::string> all_groups; - all_groups.reserve(m_help.size()); - - for (auto& group : m_help) + if (m_positional_set.find(o.l) != m_positional_set.end() && + !m_show_positional) { - all_groups.push_back(group.first); + continue; } - generate_group_help(result, all_groups); + auto s = format_option(o); + longest = (std::max)(longest, stringLength(s)); + format.push_back(std::make_pair(s, String())); } - inline - std::string - Options::help(const std::vector<std::string>& help_groups) const - { - String result = m_help_string + "\nUsage:\n " + - toLocalString(m_program) + " " + toLocalString(m_custom_help); + longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST)); - if (m_positional.size() > 0 && m_positional_help.size() > 0) { - result += " " + toLocalString(m_positional_help); + //widest allowed description + auto allowed = size_t{76} - longest - OPTION_DESC_GAP; + + auto fiter = format.begin(); + for (const auto &o : group->second.options) + { + if (m_positional_set.find(o.l) != m_positional_set.end() && + !m_show_positional) + { + continue; } - result += "\n\n"; + auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); - if (help_groups.size() == 0) + result += fiter->first; + if (stringLength(fiter->first) > longest) { - generate_all_groups_help(result); + result += '\n'; + result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); } else { - generate_group_help(result, help_groups); + result += toLocalString(std::string(longest + OPTION_DESC_GAP - + stringLength(fiter->first), + ' ')); } + result += d; + result += '\n'; - return toUTF8String(result); + ++fiter; } - inline - const std::vector<std::string> - Options::groups() const + return result; +} + +inline void +Options::generate_group_help( + String &result, + const std::vector<std::string> &print_groups) const +{ + for (size_t i = 0; i != print_groups.size(); ++i) { - std::vector<std::string> g; + const String &group_help_text = help_one_group(print_groups[i]); + if (empty(group_help_text)) + { + continue; + } + result += group_help_text; + if (i < print_groups.size() - 1) + { + result += '\n'; + } + } +} - std::transform( - m_help.begin(), - m_help.end(), - std::back_inserter(g), - [] (const std::map<std::string, HelpGroupDetails>::value_type& pair) - { - return pair.first; - } - ); +inline void +Options::generate_all_groups_help(String &result) const +{ + std::vector<std::string> all_groups; + all_groups.reserve(m_help.size()); - return g; + for (auto &group : m_help) + { + all_groups.push_back(group.first); } - inline - const HelpGroupDetails& - Options::group_help(const std::string& group) const + generate_group_help(result, all_groups); +} + +inline std::string +Options::help(const std::vector<std::string> &help_groups) const +{ + String result = m_help_string + "\nUsage:\n " + + toLocalString(m_program) + " " + toLocalString(m_custom_help); + + if (m_positional.size() > 0 && m_positional_help.size() > 0) { - return m_help.at(group); + result += " " + toLocalString(m_positional_help); } + result += "\n\n"; + + if (help_groups.size() == 0) + { + generate_all_groups_help(result); + } + else + { + generate_group_help(result, help_groups); + } + + return toUTF8String(result); +} + +inline const std::vector<std::string> +Options::groups() const +{ + std::vector<std::string> g; + + std::transform( + m_help.begin(), + m_help.end(), + std::back_inserter(g), + [](const std::map<std::string, HelpGroupDetails>::value_type &pair) { + return pair.first; + }); + + return g; } +inline const HelpGroupDetails & +Options::group_help(const std::string &group) const +{ + return m_help.at(group); +} + +} // namespace cxxopts + #endif //CXXOPTS_HPP_INCLUDED
\ No newline at end of file |