From ca5b0572b6995b4c74051f040681265e0b0dbb31 Mon Sep 17 00:00:00 2001 From: Eyal Rozenberg Date: Thu, 7 Jul 2022 00:51:45 +0300 Subject: [PATCH] Fixes jarro2783/cxxopts#338: Split the library into multiple header files - mostly one for each class. --- CMakeLists.txt | 2 +- include/cxxopts.hpp | 2678 +----------------------------- include/cxxopts/KeyValue.hpp | 51 + include/cxxopts/Option.hpp | 41 + include/cxxopts/OptionParser.hpp | 381 +++++ include/cxxopts/OptionValue.hpp | 217 +++ include/cxxopts/Options.hpp | 735 ++++++++ include/cxxopts/ParseResult.hpp | 189 +++ include/cxxopts/String.hpp | 223 +++ include/cxxopts/common.hpp | 28 + include/cxxopts/exceptions.hpp | 204 +++ include/cxxopts/values.hpp | 776 +++++++++ 12 files changed, 2847 insertions(+), 2678 deletions(-) create mode 100644 include/cxxopts/KeyValue.hpp create mode 100644 include/cxxopts/Option.hpp create mode 100644 include/cxxopts/OptionParser.hpp create mode 100644 include/cxxopts/OptionValue.hpp create mode 100644 include/cxxopts/Options.hpp create mode 100644 include/cxxopts/ParseResult.hpp create mode 100644 include/cxxopts/String.hpp create mode 100644 include/cxxopts/common.hpp create mode 100644 include/cxxopts/exceptions.hpp create mode 100644 include/cxxopts/values.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c1a6860..7b69c1f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ if (CXXOPTS_ENABLE_WARNINGS) cxxopts_enable_warnings() endif() -add_library(cxxopts INTERFACE) +add_library(cxxopts INTERFACE include/cxxopts.hpp) add_library(cxxopts::cxxopts ALIAS cxxopts) add_subdirectory(include) diff --git a/include/cxxopts.hpp b/include/cxxopts.hpp index b957956b..53ed5039 100644 --- a/include/cxxopts.hpp +++ b/include/cxxopts.hpp @@ -25,2682 +25,6 @@ THE SOFTWARE. #ifndef CXXOPTS_HPP_INCLUDED #define CXXOPTS_HPP_INCLUDED -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__GNUC__) && !defined(__clang__) -# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49 -# define CXXOPTS_NO_REGEX true -# endif -#endif - -#ifndef CXXOPTS_NO_REGEX -# include -#endif // CXXOPTS_NO_REGEX - -// Nonstandard before C++17, which is coincidentally what we also need for -#ifdef __has_include -# if __has_include() -# include -# ifdef __cpp_lib_optional -# define CXXOPTS_HAS_OPTIONAL -# endif -# endif -#endif - -#if __cplusplus >= 201603L -#define CXXOPTS_NODISCARD [[nodiscard]] -#else -#define CXXOPTS_NODISCARD -#endif - -#ifndef CXXOPTS_VECTOR_DELIMITER -#define CXXOPTS_VECTOR_DELIMITER ',' -#endif - -#define CXXOPTS__VERSION_MAJOR 3 -#define CXXOPTS__VERSION_MINOR 0 -#define CXXOPTS__VERSION_PATCH 0 - -#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6 - #define CXXOPTS_NULL_DEREF_IGNORE -#endif - -namespace cxxopts -{ - 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 can be found by the -//compiler, and that icu-uc is linked in to the binary. - -#ifdef CXXOPTS_USE_UNICODE -#include - -namespace cxxopts -{ - using String = icu::UnicodeString; - - inline - String - toLocalString(std::string s) - { - return icu::UnicodeString::fromUTF8(std::move(s)); - } - -#if defined(__GNUC__) -// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it: -// warning: base class 'class std::enable_shared_from_this' has accessible non-virtual destructor -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#pragma GCC diagnostic ignored "-Weffc++" -// This will be ignored under other compilers like LLVM clang. -#endif - class UnicodeStringIterator : public - std::iterator - { - public: - - UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) - : s(string) - , i(pos) - { - } - - value_type - operator*() const - { - return s->char32At(i); - } - - bool - operator==(const UnicodeStringIterator& rhs) const - { - return s == rhs.s && i == rhs.i; - } - - bool - operator!=(const UnicodeStringIterator& rhs) const - { - return !(*this == rhs); - } - - UnicodeStringIterator& - operator++() - { - ++i; - return *this; - } - - UnicodeStringIterator - operator+(int32_t v) - { - return UnicodeStringIterator(s, i + v); - } - - private: - const icu::UnicodeString* s; - int32_t i; - }; -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - - inline - String& - stringAppend(String&s, String a) - { - return s.append(std::move(a)); - } - - inline - String& - stringAppend(String& s, size_t n, UChar32 c) - { - for (size_t i = 0; i != n; ++i) - { - s.append(c); - } - - return s; - } - - template - String& - stringAppend(String& s, Iterator begin, Iterator end) - { - while (begin != end) - { - s.append(*begin); - ++begin; - } - - return s; - } - - inline - size_t - stringLength(const String& s) - { - return s.length(); - } - - inline - std::string - toUTF8String(const String& s) - { - std::string result; - s.toUTF8String(result); - - return result; - } - - inline - bool - empty(const String& s) - { - return s.isEmpty(); - } -} - -namespace std -{ - 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()); - } -} - -//ifdef CXXOPTS_USE_UNICODE -#else - -namespace cxxopts -{ - using String = std::string; - - template - T - toLocalString(T&& t) - { - return std::forward(t); - } - - inline - size_t - stringLength(const String& s) - { - return s.length(); - } - - inline - String& - stringAppend(String&s, const String& a) - { - return s.append(a); - } - - inline - String& - stringAppend(String& s, size_t n, char c) - { - return s.append(n, c); - } - - template - String& - stringAppend(String& s, Iterator begin, Iterator end) - { - return s.append(begin, end); - } - - template - std::string - toUTF8String(T&& t) - { - return std::forward(t); - } - - inline - bool - empty(const std::string& s) - { - return s.empty(); - } -} // namespace cxxopts - -//ifdef CXXOPTS_USE_UNICODE -#endif - -namespace cxxopts -{ - namespace - { -#ifdef _WIN32 - const std::string LQUOTE("\'"); - const std::string RQUOTE("\'"); -#else - const std::string LQUOTE("‘"); - const std::string RQUOTE("’"); -#endif - } // namespace - -#if defined(__GNUC__) -// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it: -// warning: base class 'class std::enable_shared_from_this' has accessible non-virtual destructor -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#pragma GCC diagnostic ignored "-Weffc++" -// This will be ignored under other compilers like LLVM clang. -#endif - class Value : public std::enable_shared_from_this - { - public: - - virtual ~Value() = default; - - virtual - std::shared_ptr - clone() const = 0; - - virtual void - parse(const std::string& text) const = 0; - - virtual void - parse() const = 0; - - virtual bool - has_default() const = 0; - - virtual bool - is_container() const = 0; - - virtual bool - has_implicit() const = 0; - - virtual std::string - get_default_value() const = 0; - - virtual std::string - get_implicit_value() const = 0; - - virtual std::shared_ptr - default_value(const std::string& value) = 0; - - virtual std::shared_ptr - implicit_value(const std::string& value) = 0; - - virtual std::shared_ptr - no_implicit_value() = 0; - - virtual bool - is_boolean() const = 0; - }; -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - class OptionException : public std::exception - { - public: - explicit OptionException(std::string message) - : m_message(std::move(message)) - { - } - - CXXOPTS_NODISCARD - const char* - what() const noexcept override - { - return m_message.c_str(); - } - - private: - std::string m_message; - }; - - class OptionSpecException : public OptionException - { - public: - - explicit OptionSpecException(const std::string& message) - : OptionException(message) - { - } - }; - - class OptionParseException : public OptionException - { - public: - explicit OptionParseException(const std::string& message) - : OptionException(message) - { - } - }; - - class option_exists_error : public OptionSpecException - { - public: - explicit option_exists_error(const std::string& option) - : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") - { - } - }; - - class invalid_option_format_error : public OptionSpecException - { - public: - explicit invalid_option_format_error(const std::string& format) - : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) - { - } - }; - - class option_syntax_exception : public OptionParseException { - public: - explicit 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: - explicit option_not_exists_exception(const std::string& option) - : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") - { - } - }; - - class missing_argument_exception : public OptionParseException - { - public: - explicit missing_argument_exception(const std::string& option) - : OptionParseException( - "Option " + LQUOTE + option + RQUOTE + " is missing an argument" - ) - { - } - }; - - class option_requires_argument_exception : public OptionParseException - { - public: - explicit 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 - { - public: - explicit option_not_present_exception(const std::string& option) - : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") - { - } - }; - - class option_has_no_value_exception : public OptionException - { - public: - explicit option_has_no_value_exception(const std::string& option) - : OptionException( - !option.empty() ? - ("Option " + LQUOTE + option + RQUOTE + " has no value") : - "Option has no value") - { - } - }; - - class argument_incorrect_type : public OptionParseException - { - public: - explicit argument_incorrect_type - ( - const std::string& arg - ) - : OptionParseException( - "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" - ) - { - } - }; - - class option_required_exception : public OptionParseException - { - public: - explicit option_required_exception(const std::string& option) - : OptionParseException( - "Option " + LQUOTE + option + RQUOTE + " is required but not present" - ) - { - } - }; - - template - void throw_or_mimic(const std::string& text) - { - static_assert(std::is_base_of::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}; -#else - // Otherwise manually instantiate the exception, print what() to stderr, - // and exit - T exception{text}; - std::cerr << exception.what() << std::endl; - std::exit(EXIT_FAILURE); -#endif - } - - namespace values - { - namespace parser_tool - { - struct IntegerDesc - { - std::string negative = ""; - std::string base = ""; - std::string value = ""; - }; - struct ArguDesc { - std::string arg_name = ""; - bool grouping = false; - bool set_value = false; - std::string value = ""; - }; -#ifdef CXXOPTS_NO_REGEX - inline IntegerDesc SplitInteger(const std::string &text) - { - if (text.empty()) - { - throw_or_mimic(text); - } - IntegerDesc desc; - const char *pdata = text.c_str(); - if (*pdata == '-') - { - pdata += 1; - desc.negative = "-"; - } - if (strncmp(pdata, "0x", 2) == 0) - { - pdata += 2; - desc.base = "0x"; - } - if (*pdata != '\0') - { - desc.value = std::string(pdata); - } - else - { - throw_or_mimic(text); - } - return desc; - } - - inline bool IsTrueText(const std::string &text) - { - const char *pdata = text.c_str(); - if (*pdata == 't' || *pdata == 'T') - { - pdata += 1; - if (strncmp(pdata, "rue\0", 4) == 0) - { - return true; - } - } - else if (strncmp(pdata, "1\0", 2) == 0) - { - return true; - } - return false; - } - - inline bool IsFalseText(const std::string &text) - { - const char *pdata = text.c_str(); - if (*pdata == 'f' || *pdata == 'F') - { - pdata += 1; - if (strncmp(pdata, "alse\0", 5) == 0) - { - return true; - } - } - else if (strncmp(pdata, "0\0", 2) == 0) - { - return true; - } - return false; - } - - inline std::pair SplitSwitchDef(const std::string &text) - { - std::string short_sw, long_sw; - const char *pdata = text.c_str(); - if (isalnum(*pdata) && *(pdata + 1) == ',') { - short_sw = std::string(1, *pdata); - pdata += 2; - } - while (*pdata == ' ') { pdata += 1; } - if (isalnum(*pdata)) { - const char *store = pdata; - pdata += 1; - while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') { - pdata += 1; - } - if (*pdata == '\0') { - long_sw = std::string(store, pdata - store); - } else { - throw_or_mimic(text); - } - } - return std::pair(short_sw, long_sw); - } - - inline ArguDesc ParseArgument(const char *arg, bool &matched) - { - ArguDesc argu_desc; - const char *pdata = arg; - matched = false; - if (strncmp(pdata, "--", 2) == 0) - { - pdata += 2; - if (isalnum(*pdata)) - { - argu_desc.arg_name.push_back(*pdata); - pdata += 1; - while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') - { - argu_desc.arg_name.push_back(*pdata); - pdata += 1; - } - if (argu_desc.arg_name.length() > 1) - { - if (*pdata == '=') - { - argu_desc.set_value = true; - pdata += 1; - if (*pdata != '\0') - { - argu_desc.value = std::string(pdata); - } - matched = true; - } - else if (*pdata == '\0') - { - matched = true; - } - } - } - } - else if (strncmp(pdata, "-", 1) == 0) - { - pdata += 1; - argu_desc.grouping = true; - while (isalnum(*pdata)) - { - argu_desc.arg_name.push_back(*pdata); - pdata += 1; - } - matched = !argu_desc.arg_name.empty() && *pdata == '\0'; - } - return argu_desc; - } - -#else // CXXOPTS_NO_REGEX - - namespace - { - - std::basic_regex integer_pattern - ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); - std::basic_regex truthy_pattern - ("(t|T)(rue)?|1"); - std::basic_regex falsy_pattern - ("(f|F)(alse)?|0"); - - std::basic_regex option_matcher - ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); - std::basic_regex option_specifier - ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); - - } // namespace - - inline IntegerDesc SplitInteger(const std::string &text) - { - std::smatch match; - std::regex_match(text, match, integer_pattern); - - if (match.length() == 0) - { - throw_or_mimic(text); - } - - IntegerDesc desc; - desc.negative = match[1]; - desc.base = match[2]; - desc.value = match[3]; - - if (match.length(4) > 0) - { - desc.base = match[5]; - desc.value = "0"; - return desc; - } - - return desc; - } - - inline bool IsTrueText(const std::string &text) - { - std::smatch result; - std::regex_match(text, result, truthy_pattern); - return !result.empty(); - } - - inline bool IsFalseText(const std::string &text) - { - std::smatch result; - std::regex_match(text, result, falsy_pattern); - return !result.empty(); - } - - inline std::pair SplitSwitchDef(const std::string &text) - { - std::match_results result; - std::regex_match(text.c_str(), result, option_specifier); - if (result.empty()) - { - throw_or_mimic(text); - } - - const std::string& short_sw = result[2]; - const std::string& long_sw = result[3]; - - return std::pair(short_sw, long_sw); - } - - inline ArguDesc ParseArgument(const char *arg, bool &matched) - { - std::match_results result; - std::regex_match(arg, result, option_matcher); - matched = !result.empty(); - - ArguDesc argu_desc; - if (matched) { - argu_desc.arg_name = result[1].str(); - argu_desc.set_value = result[2].length() > 0; - argu_desc.value = result[3].str(); - if (result[4].length() > 0) - { - argu_desc.grouping = true; - argu_desc.arg_name = result[4].str(); - } - } - - return argu_desc; - } - -#endif // CXXOPTS_NO_REGEX -#undef CXXOPTS_NO_REGEX - } - - namespace detail - { - template - struct SignedCheck; - - template - struct SignedCheck - { - template - void - operator()(bool negative, U u, const std::string& text) - { - if (negative) - { - if (u > static_cast((std::numeric_limits::min)())) - { - throw_or_mimic(text); - } - } - else - { - if (u > static_cast((std::numeric_limits::max)())) - { - throw_or_mimic(text); - } - } - } - }; - - template - struct SignedCheck - { - template - void - operator()(bool, U, const std::string&) const {} - }; - - template - void - check_signed_range(bool negative, U value, const std::string& text) - { - SignedCheck::is_signed>()(negative, value, text); - } - } // namespace detail - - template - void - checked_negate(R& r, 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. - r = static_cast(-static_cast(t-1)-1); - } - - template - void - checked_negate(R&, T&&, const std::string& text, std::false_type) - { - throw_or_mimic(text); - } - - template - void - integer_parser(const std::string& text, T& value) - { - parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text); - - using US = typename std::make_unsigned::type; - constexpr bool is_signed = std::numeric_limits::is_signed; - - const bool negative = int_desc.negative.length() > 0; - const uint8_t base = int_desc.base.length() > 0 ? 16 : 10; - const std::string & value_match = int_desc.value; - - US result = 0; - - for (char ch : value_match) - { - US digit = 0; - - if (ch >= '0' && ch <= '9') - { - digit = static_cast(ch - '0'); - } - else if (base == 16 && ch >= 'a' && ch <= 'f') - { - digit = static_cast(ch - 'a' + 10); - } - else if (base == 16 && ch >= 'A' && ch <= 'F') - { - digit = static_cast(ch - 'A' + 10); - } - else - { - throw_or_mimic(text); - } - - const US next = static_cast(result * base + digit); - if (result > next) - { - throw_or_mimic(text); - } - - result = next; - } - - detail::check_signed_range(negative, result, text); - - if (negative) - { - checked_negate(value, result, text, std::integral_constant()); - } - else - { - value = static_cast(result); - } - } - - template - void stringstream_parser(const std::string& text, T& value) - { - std::stringstream in(text); - in >> value; - if (!in) { - throw_or_mimic(text); - } - } - - template ::value>::type* = nullptr - > - void parse_value(const std::string& text, T& value) - { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, bool& value) - { - if (parser_tool::IsTrueText(text)) - { - value = true; - return; - } - - if (parser_tool::IsFalseText(text)) - { - value = false; - return; - } - - throw_or_mimic(text); - } - - inline - void - parse_value(const std::string& text, std::string& value) - { - value = 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 ::value>::type* = nullptr - > - void - parse_value(const std::string& text, T& value) { - stringstream_parser(text, value); - } - - template - void - parse_value(const std::string& text, std::vector& value) - { - if (text.empty()) { - T v; - parse_value(text, v); - value.emplace_back(std::move(v)); - return; - } - std::stringstream in(text); - std::string token; - while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { - T v; - parse_value(token, v); - value.emplace_back(std::move(v)); - } - } - -#ifdef CXXOPTS_HAS_OPTIONAL - template - void - parse_value(const std::string& text, std::optional& value) - { - T result; - parse_value(text, result); - value = std::move(result); - } -#endif - - inline - void parse_value(const std::string& text, char& c) - { - if (text.length() != 1) - { - throw_or_mimic(text); - } - - c = text[0]; - } - - template - struct type_is_container - { - static constexpr bool value = false; - }; - - template - struct type_is_container> - { - static constexpr bool value = true; - }; - - template - class abstract_value : public Value - { - using Self = abstract_value; - - public: - abstract_value() - : m_result(std::make_shared()) - , m_store(m_result.get()) - { - } - - explicit abstract_value(T* t) - : m_store(t) - { - } - - ~abstract_value() override = default; - - abstract_value& operator=(const abstract_value&) = default; - - abstract_value(const abstract_value& rhs) - { - if (rhs.m_result) - { - m_result = std::make_shared(); - m_store = m_result.get(); - } - else - { - m_store = rhs.m_store; - } - - m_default = rhs.m_default; - m_implicit = rhs.m_implicit; - m_default_value = rhs.m_default_value; - m_implicit_value = rhs.m_implicit_value; - } - - void - parse(const std::string& text) const override - { - parse_value(text, *m_store); - } - - bool - is_container() const override - { - return type_is_container::value; - } - - void - parse() const override - { - parse_value(m_default_value, *m_store); - } - - bool - has_default() const override - { - return m_default; - } - - bool - has_implicit() const override - { - return m_implicit; - } - - std::shared_ptr - default_value(const std::string& value) override - { - m_default = true; - m_default_value = value; - return shared_from_this(); - } - - std::shared_ptr - implicit_value(const std::string& value) override - { - m_implicit = true; - m_implicit_value = value; - return shared_from_this(); - } - - std::shared_ptr - no_implicit_value() override - { - m_implicit = false; - return shared_from_this(); - } - - std::string - get_default_value() const override - { - return m_default_value; - } - - std::string - get_implicit_value() const override - { - return m_implicit_value; - } - - bool - is_boolean() const override - { - return std::is_same::value; - } - - const T& - get() const - { - if (m_store == nullptr) - { - return *m_result; - } - return *m_store; - } - - protected: - std::shared_ptr m_result{}; - T* m_store{}; - - bool m_default = false; - bool m_implicit = false; - - std::string m_default_value{}; - std::string m_implicit_value{}; - }; - - template - class standard_value : public abstract_value - { - public: - using abstract_value::abstract_value; - - CXXOPTS_NODISCARD - std::shared_ptr - clone() const override - { - return std::make_shared>(*this); - } - }; - - template <> - class standard_value : public abstract_value - { - public: - ~standard_value() override = default; - - standard_value() - { - set_default_and_implicit(); - } - - explicit standard_value(bool* b) - : abstract_value(b) - { - set_default_and_implicit(); - } - - std::shared_ptr - clone() const override - { - return std::make_shared>(*this); - } - - private: - - void - set_default_and_implicit() - { - m_default = true; - m_default_value = "false"; - m_implicit = true; - m_implicit_value = "true"; - } - }; - } // namespace values - - template - std::shared_ptr - value() - { - return std::make_shared>(); - } - - template - std::shared_ptr - value(T& t) - { - return std::make_shared>(&t); - } - - class OptionAdder; - - class OptionDetails - { - public: - OptionDetails - ( - std::string short_, - std::string long_, - String desc, - std::shared_ptr val - ) - : m_short(std::move(short_)) - , m_long(std::move(long_)) - , m_desc(std::move(desc)) - , m_value(std::move(val)) - , m_count(0) - { - m_hash = std::hash{}(m_long + m_short); - } - - OptionDetails(const OptionDetails& rhs) - : m_desc(rhs.m_desc) - , m_value(rhs.m_value->clone()) - , m_count(rhs.m_count) - { - } - - OptionDetails(OptionDetails&& rhs) = default; - - CXXOPTS_NODISCARD - const String& - description() const - { - return m_desc; - } - - CXXOPTS_NODISCARD - const Value& - value() const { - return *m_value; - } - - CXXOPTS_NODISCARD - std::shared_ptr - make_storage() const - { - return m_value->clone(); - } - - CXXOPTS_NODISCARD - const std::string& - short_name() const - { - return m_short; - } - - CXXOPTS_NODISCARD - const std::string& - long_name() const - { - return m_long; - } - - size_t - hash() const - { - return m_hash; - } - - private: - std::string m_short{}; - std::string m_long{}; - String m_desc{}; - std::shared_ptr m_value{}; - int m_count; - - size_t m_hash{}; - }; - - 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 options{}; - }; - - class OptionValue - { - public: - void - parse - ( - const std::shared_ptr& details, - const std::string& text - ) - { - ensure_value(details); - ++m_count; - m_value->parse(text); - m_long_name = &details->long_name(); - } - - void - parse_default(const std::shared_ptr& details) - { - ensure_value(details); - m_default = true; - m_long_name = &details->long_name(); - m_value->parse(); - } - - void - parse_no_value(const std::shared_ptr& details) - { - m_long_name = &details->long_name(); - } - -#if defined(CXXOPTS_NULL_DEREF_IGNORE) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnull-dereference" -#endif - - CXXOPTS_NODISCARD - size_t - count() const noexcept - { - return m_count; - } - -#if defined(CXXOPTS_NULL_DEREF_IGNORE) -#pragma GCC diagnostic pop -#endif - - // TODO: maybe default options should count towards the number of arguments - CXXOPTS_NODISCARD - bool - has_default() const noexcept - { - return m_default; - } - - template - const T& - as() const - { - if (m_value == nullptr) { - throw_or_mimic( - m_long_name == nullptr ? "" : *m_long_name); - } - -#ifdef CXXOPTS_NO_RTTI - return static_cast&>(*m_value).get(); -#else - return dynamic_cast&>(*m_value).get(); -#endif - } - - private: - void - ensure_value(const std::shared_ptr& details) - { - if (m_value == nullptr) - { - m_value = details->make_storage(); - } - } - - - const std::string* m_long_name = nullptr; - // Holding this pointer is safe, since OptionValue's only exist in key-value pairs, - // where the key has the string we point to. - std::shared_ptr m_value{}; - size_t m_count = 0; - bool m_default = false; - }; - - class KeyValue - { - public: - KeyValue(std::string key_, std::string value_) - : m_key(std::move(key_)) - , m_value(std::move(value_)) - { - } - - CXXOPTS_NODISCARD - const std::string& - key() const - { - return m_key; - } - - CXXOPTS_NODISCARD - const std::string& - value() const - { - return m_value; - } - - template - T - as() const - { - T result; - values::parse_value(m_value, result); - return result; - } - - private: - std::string m_key; - std::string m_value; - }; - - using ParsedHashMap = std::unordered_map; - using NameHashMap = std::unordered_map; - - class ParseResult - { - public: - class Iterator - { - public: - using iterator_category = std::forward_iterator_tag; - using value_type = KeyValue; - using difference_type = void; - using pointer = const KeyValue*; - using reference = const KeyValue&; - - Iterator() = default; - Iterator(const Iterator&) = default; - - Iterator(const ParseResult *pr, bool end=false) - : m_pr(pr) - , m_iter(end? pr->m_defaults.end(): pr->m_sequential.begin()) - { - } - - Iterator& operator++() - { - ++m_iter; - if(m_iter == m_pr->m_sequential.end()) - { - m_iter = m_pr->m_defaults.begin(); - return *this; - } - return *this; - } - - Iterator operator++(int) - { - Iterator retval = *this; - ++(*this); - return retval; - } - - bool operator==(const Iterator& other) const - { - return m_iter == other.m_iter; - } - - bool operator!=(const Iterator& other) const - { - return !(*this == other); - } - - const KeyValue& operator*() - { - return *m_iter; - } - - const KeyValue* operator->() - { - return m_iter.operator->(); - } - - private: - const ParseResult* m_pr; - std::vector::const_iterator m_iter; - }; - - ParseResult() = default; - ParseResult(const ParseResult&) = default; - - ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector sequential, - std::vector default_opts, std::vector&& unmatched_args) - : m_keys(std::move(keys)) - , m_values(std::move(values)) - , m_sequential(std::move(sequential)) - , m_defaults(std::move(default_opts)) - , m_unmatched(std::move(unmatched_args)) - { - } - - ParseResult& operator=(ParseResult&&) = default; - ParseResult& operator=(const ParseResult&) = default; - - Iterator - begin() const - { - return Iterator(this); - } - - Iterator - end() const - { - return Iterator(this, true); - } - - size_t - count(const std::string& o) const - { - auto iter = m_keys.find(o); - if (iter == m_keys.end()) - { - return 0; - } - - auto viter = m_values.find(iter->second); - - if (viter == m_values.end()) - { - return 0; - } - - return viter->second.count(); - } - - const OptionValue& - operator[](const std::string& option) const - { - auto iter = m_keys.find(option); - - if (iter == m_keys.end()) - { - throw_or_mimic(option); - } - - auto viter = m_values.find(iter->second); - - if (viter == m_values.end()) - { - throw_or_mimic(option); - } - - return viter->second; - } - - const std::vector& - arguments() const - { - return m_sequential; - } - - const std::vector& - unmatched() const - { - return m_unmatched; - } - - const std::vector& - defaults() const - { - return m_defaults; - } - - const std::string - arguments_string() const - { - std::string result; - for(const auto& kv: m_sequential) - { - result += kv.key() + " = " + kv.value() + "\n"; - } - for(const auto& kv: m_defaults) - { - result += kv.key() + " = " + kv.value() + " " + "(default)" + "\n"; - } - return result; - } - - private: - NameHashMap m_keys{}; - ParsedHashMap m_values{}; - std::vector m_sequential{}; - std::vector m_defaults{}; - std::vector m_unmatched{}; - }; - - struct Option - { - Option - ( - std::string opts, - std::string desc, - std::shared_ptr value = ::cxxopts::value(), - std::string arg_help = "" - ) - : opts_(std::move(opts)) - , desc_(std::move(desc)) - , value_(std::move(value)) - , arg_help_(std::move(arg_help)) - { - } - - std::string opts_; - std::string desc_; - std::shared_ptr value_; - std::string arg_help_; - }; - - using OptionMap = std::unordered_map>; - using PositionalList = std::vector; - using PositionalListIterator = PositionalList::const_iterator; - - class OptionParser - { - public: - OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised) - : m_options(options) - , m_positional(positional) - , m_allow_unrecognised(allow_unrecognised) - { - } - - ParseResult - parse(int argc, const char* const* argv); - - bool - consume_positional(const std::string& a, PositionalListIterator& next); - - void - checked_parse_arg - ( - int argc, - const char* const* argv, - int& current, - const std::shared_ptr& value, - const std::string& name - ); - - void - add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg); - - void - parse_option - ( - const std::shared_ptr& value, - const std::string& name, - const std::string& arg = "" - ); - - void - parse_default(const std::shared_ptr& details); - - void - parse_no_value(const std::shared_ptr& details); - - private: - - void finalise_aliases(); - - const OptionMap& m_options; - const PositionalList& m_positional; - - std::vector m_sequential{}; - std::vector m_defaults{}; - bool m_allow_unrecognised; - - ParsedHashMap m_parsed{}; - NameHashMap m_keys{}; - }; - - class Options - { - public: - - explicit 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_width(76) - , m_tab_expansion(false) - , m_options(std::make_shared()) - { - } - - Options& - positional_help(std::string help_text) - { - m_positional_help = std::move(help_text); - return *this; - } - - Options& - custom_help(std::string help_text) - { - m_custom_help = std::move(help_text); - return *this; - } - - Options& - show_positional_help() - { - m_show_positional = true; - return *this; - } - - Options& - allow_unrecognised_options() - { - m_allow_unrecognised = true; - return *this; - } - - Options& - set_width(size_t width) - { - m_width = width; - return *this; - } - - Options& - set_tab_expansion(bool expansion=true) - { - m_tab_expansion = expansion; - return *this; - } - - ParseResult - parse(int argc, const char* const* argv); - - OptionAdder - add_options(std::string group = ""); - - void - add_options - ( - const std::string& group, - std::initializer_list