aboutsummaryrefslogtreecommitdiff
path: root/third-party
diff options
context:
space:
mode:
authorJim Flynn <jim.flynn@arm.com>2023-04-10 23:26:40 +0100
committerTeresaARM <teresa.charlinreyes@arm.com>2023-04-26 14:02:18 +0000
commit357add2c4685362afb188feaaa67b90e4d6d2361 (patch)
tree8016797bb16f9a6e2334c2f30eb1fecf8913c3f0 /third-party
parent2b04ec3b94da152281fbbc69f8539378589b1f56 (diff)
downloadarmnn-357add2c4685362afb188feaaa67b90e4d6d2361.tar.gz
Update cxxopts from 3.0 to 3.1.1
!android-nn-driver:9431 Signed-off-by: Jim Flynn <jim.flynn@arm.com> Change-Id: I58143445b5c5cf2aafd0838156c9543adce21e6a
Diffstat (limited to 'third-party')
-rw-r--r--third-party/cxxopts/CHANGELOG.md34
-rw-r--r--third-party/cxxopts/CHANGELOG.md.license2
-rw-r--r--third-party/cxxopts/LICENSE.spdx2
-rw-r--r--third-party/cxxopts/README.md46
-rw-r--r--third-party/cxxopts/README.md.license2
-rw-r--r--third-party/cxxopts/cxxopts.hpp3215
6 files changed, 1945 insertions, 1356 deletions
diff --git a/third-party/cxxopts/CHANGELOG.md b/third-party/cxxopts/CHANGELOG.md
index df0e110995..782f4d061f 100644
--- a/third-party/cxxopts/CHANGELOG.md
+++ b/third-party/cxxopts/CHANGELOG.md
@@ -3,6 +3,31 @@
This is the changelog for `cxxopts`, a C++11 library for parsing command line
options. The project adheres to semantic versioning.
+## 3.1.1
+
+### Bug Fixes
+
+* Fixed version number in header.
+* Fixed cast warning in Unicode function.
+
+## 3.1
+
+### Added
+
+* Support for multiple long names for the same option (= multiple long aliases)
+* Add a `program()` function to retrieve the program name.
+* Added a .clang-format file.
+* Added iterator and printing for a ParseResult.
+
+### Changed
+
+* Cleanup exception code, add cxxopts::exceptions namespace.
+* Renamed several exceptions to be more descriptive, and added to a nested namespace.
+
+### Bug Fixes
+
+* Fix `arguments()` having no key for options that only have a short name.
+
## 3.0
### Changed
@@ -15,10 +40,19 @@ options. The project adheres to semantic versioning.
* Change argument type in `Options::parse` from `char**` to `const char**`.
* Refactor parser to not change its arguments.
* `ParseResult` doesn't depend on a reference to the parser.
+* Fixed several warnings and code quality issues.
+* Improved formatting for help descriptions.
+* Improve integer parsing.
### Added
* A list of unmatched arguments is available in `ParseResult`.
+* Support single letter options with argument attached.
+* Use <optional> if it is present.
+
+### Bug Fixes
+
+* Fix missing option name in exception.
## 2.2
diff --git a/third-party/cxxopts/CHANGELOG.md.license b/third-party/cxxopts/CHANGELOG.md.license
index bf1b5b51c7..2a9007a4fe 100644
--- a/third-party/cxxopts/CHANGELOG.md.license
+++ b/third-party/cxxopts/CHANGELOG.md.license
@@ -1,4 +1,4 @@
#
-# Copyright (c) 2014 Jarryd Beck
+# Copyright (c) 2014-2022 Jarryd Beck
# SPDX-License-Identifier: MIT
#
diff --git a/third-party/cxxopts/LICENSE.spdx b/third-party/cxxopts/LICENSE.spdx
index e4139cb989..079b1a78e5 100644
--- a/third-party/cxxopts/LICENSE.spdx
+++ b/third-party/cxxopts/LICENSE.spdx
@@ -4,4 +4,4 @@ FilesAnalyzed: true
PackageLicenseConcluded: MIT
PackageLicenseInfoFromFiles: MIT
PackageLicenseDeclared: MIT
-PackageCopyrightText: <text>Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck</text>
+PackageCopyrightText: <text>Copyright (c) 2014-2022 Jarryd Beck</text>
diff --git a/third-party/cxxopts/README.md b/third-party/cxxopts/README.md
index 9517993d04..de69e5ab88 100644
--- a/third-party/cxxopts/README.md
+++ b/third-party/cxxopts/README.md
@@ -7,9 +7,9 @@ tagged release version.
## Version 3 breaking changes
-If you have used version 2, there are a couple of breaking changes in (the as
-yet unreleased, current master) version 3 that you should be aware of. If you are new to
-`cxxopts` you can skip this section.
+If you have used version 2, there are a couple of breaking changes in version 3
+that you should be aware of. If you are new to `cxxopts` you can skip this
+section.
The parser no longer modifies its arguments, so you can pass a const `argc` and
`argv` and expect them not to be changed.
@@ -109,9 +109,9 @@ result.unmatched()
Exceptional situations throw C++ exceptions. There are two types of
exceptions: errors defining the options, and errors when parsing a list of
-arguments. All exceptions derive from `cxxopts::OptionException`. Errors
-defining options derive from `cxxopts::OptionSpecException` and errors
-parsing arguments derive from `cxxopts::OptionParseException`.
+arguments. All exceptions derive from `cxxopts::exceptions::exception`. Errors
+defining options derive from `cxxopts::exceptions::specification` and errors
+parsing arguments derive from `cxxopts::exceptions::parsing`.
All exceptions define a `what()` function to get a printable string
explaining the error.
@@ -125,15 +125,37 @@ vector to the `help` function.
## Positional Arguments
-Positional arguments can be optionally parsed into one or more options.
-To set up positional arguments, call
+Positional arguments are those given without a preceding flag and can be used
+alongside non-positional arguments. There may be multiple positional arguments,
+and the final positional argument may be a container type to hold a list of all
+remaining positionals.
+
+To set up positional arguments, first declare the options, then configure a
+set of those arguments as positional like:
```cpp
-options.parse_positional({"first", "second", "last"})
+options.add_options()
+ ("script", "The script file to execute", cxxopts::value<std::string>())
+ ("server", "The server to execute on", cxxopts::value<std::string>())
+ ("filenames", "The filename(s) to process", cxxopts::value<std::vector<std::string>>());
+
+options.parse_positional({"script", "server", "filenames"});
+
+// Parse options the usual way
+options.parse(argc, argv);
```
-where "last" should be the name of an option with a container type, and the
-others should have a single value.
+For example, parsing the following arguments:
+~~~
+my_script.py my_server.com file1.txt file2.txt file3.txt
+~~~
+will result in parsed arguments like the following table:
+
+| Field | Value |
+| ------------- | ----------------------------------------- |
+| `"script"` | `"my_script.py"` |
+| `"server"` | `"my_server.com"` |
+| `"filenames"` | `{"file1.txt", "file2.txt", "file3.txt"}` |
## Default and implicit values
@@ -161,6 +183,8 @@ Note that the default and implicit value is always stored as a string,
regardless of the type that you want to store it in. It will be parsed as
though it was given on the command line.
+Default values are not counted by `Options::count`.
+
## Boolean values
Boolean options have a default implicit value of `"true"`, which can be
diff --git a/third-party/cxxopts/README.md.license b/third-party/cxxopts/README.md.license
index bf1b5b51c7..2a9007a4fe 100644
--- a/third-party/cxxopts/README.md.license
+++ b/third-party/cxxopts/README.md.license
@@ -1,4 +1,4 @@
#
-# Copyright (c) 2014 Jarryd Beck
+# Copyright (c) 2014-2022 Jarryd Beck
# SPDX-License-Identifier: MIT
#
diff --git a/third-party/cxxopts/cxxopts.hpp b/third-party/cxxopts/cxxopts.hpp
index 5cebc4bd17..8aa17bd63b 100644
--- a/third-party/cxxopts/cxxopts.hpp
+++ b/third-party/cxxopts/cxxopts.hpp
@@ -1,7 +1,6 @@
/*
-Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck
-
+Copyright (c) 2014-2022 Jarryd Beck
SPDX-License-Identifier: MIT
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -24,28 +23,55 @@ THE SOFTWARE.
*/
+// vim: ts=2:sw=2:expandtab
+
#ifndef CXXOPTS_HPP_INCLUDED
#define CXXOPTS_HPP_INCLUDED
-#include <cctype>
#include <cstring>
#include <exception>
-#include <iostream>
#include <limits>
-#include <list>
+#include <initializer_list>
#include <map>
#include <memory>
-#include <regex>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
+#include <algorithm>
+#include <locale>
+
+#ifdef CXXOPTS_NO_EXCEPTIONS
+#include <iostream>
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
+# define CXXOPTS_NO_REGEX true
+# endif
+#endif
+#if defined(_MSC_VER) && !defined(__clang__)
+#define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern
+#define CXXOPTS_LINKONCE __declspec(selectany) extern
+#else
+#define CXXOPTS_LINKONCE_CONST
+#define CXXOPTS_LINKONCE
+#endif
-#ifdef __cpp_lib_optional
-#include <optional>
-#define CXXOPTS_HAS_OPTIONAL
+#ifndef CXXOPTS_NO_REGEX
+# include <regex>
+#endif // CXXOPTS_NO_REGEX
+
+// Nonstandard before C++17, which is coincidentally what we also need for <optional>
+#ifdef __has_include
+# if __has_include(<optional>)
+# include <optional>
+# ifdef __cpp_lib_optional
+# define CXXOPTS_HAS_OPTIONAL
+# endif
+# endif
#endif
#if __cplusplus >= 201603L
@@ -59,18 +85,39 @@ THE SOFTWARE.
#endif
#define CXXOPTS__VERSION_MAJOR 3
-#define CXXOPTS__VERSION_MINOR 0
-#define CXXOPTS__VERSION_PATCH 0
+#define CXXOPTS__VERSION_MINOR 1
+#define CXXOPTS__VERSION_PATCH 1
-namespace cxxopts
-{
- static constexpr struct {
- uint8_t major, minor, patch;
- } version = {
- CXXOPTS__VERSION_MAJOR,
- CXXOPTS__VERSION_MINOR,
- CXXOPTS__VERSION_PATCH
- };
+#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
+ #define CXXOPTS_NULL_DEREF_IGNORE
+#endif
+
+#if defined(__GNUC__)
+#define DO_PRAGMA(x) _Pragma(#x)
+#define CXXOPTS_DIAGNOSTIC_PUSH DO_PRAGMA(GCC diagnostic push)
+#define CXXOPTS_DIAGNOSTIC_POP DO_PRAGMA(GCC diagnostic pop)
+#define CXXOPTS_IGNORE_WARNING(x) DO_PRAGMA(GCC diagnostic ignored x)
+#else
+// define other compilers here if needed
+#define CXXOPTS_DIAGNOSTIC_PUSH
+#define CXXOPTS_DIAGNOSTIC_POP
+#define CXXOPTS_IGNORE_WARNING(x)
+#endif
+
+#ifdef CXXOPTS_NO_RTTI
+#define CXXOPTS_RTTI_CAST static_cast
+#else
+#define CXXOPTS_RTTI_CAST dynamic_cast
+#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,
@@ -82,1602 +129,2064 @@ namespace cxxopts
#ifdef CXXOPTS_USE_UNICODE
#include <unicode/unistr.h>
-namespace cxxopts
-{
- typedef icu::UnicodeString String;
-
- 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)
- {
- }
-
- value_type
- operator*() const
- {
- return s->char32At(i);
- }
-
- bool
- operator==(const UnicodeStringIterator& rhs) const
- {
- return s == rhs.s && i == rhs.i;
- }
+namespace cxxopts {
- bool
- operator!=(const UnicodeStringIterator& rhs) const
- {
- return !(*this == rhs);
- }
+using String = icu::UnicodeString;
- UnicodeStringIterator&
- operator++()
- {
- ++i;
- return *this;
- }
+inline
+String
+toLocalString(std::string s)
+{
+ return icu::UnicodeString::fromUTF8(std::move(s));
+}
- UnicodeStringIterator
- operator+(int32_t v)
- {
- return UnicodeStringIterator(s, i + v);
- }
+// 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<cxxopts::Value>' has accessible non-virtual destructor
+CXXOPTS_DIAGNOSTIC_PUSH
+CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
+// This will be ignored under other compilers like LLVM clang.
+class UnicodeStringIterator
+{
+ public:
- private:
- const icu::UnicodeString* s;
- int32_t i;
- };
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = int32_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
- inline
- String&
- stringAppend(String&s, String a)
+ UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
+ : s(string)
+ , i(pos)
{
- return s.append(std::move(a));
}
- inline
- String&
- stringAppend(String& s, size_t n, UChar32 c)
+ value_type
+ operator*() const
{
- for (size_t i = 0; i != n; ++i)
- {
- s.append(c);
- }
-
- return s;
+ return s->char32At(i);
}
- template <typename Iterator>
- String&
- stringAppend(String& s, Iterator begin, Iterator end)
+ bool
+ operator==(const UnicodeStringIterator& rhs) const
{
- while (begin != end)
- {
- s.append(*begin);
- ++begin;
- }
-
- return s;
+ return s == rhs.s && i == rhs.i;
}
- inline
- size_t
- stringLength(const String& s)
+ bool
+ operator!=(const UnicodeStringIterator& rhs) const
{
- return s.length();
+ return !(*this == rhs);
}
- inline
- std::string
- toUTF8String(const String& s)
+ UnicodeStringIterator&
+ operator++()
{
- std::string result;
- s.toUTF8String(result);
-
- return result;
+ ++i;
+ return *this;
}
- inline
- bool
- empty(const String& s)
+ UnicodeStringIterator
+ operator+(int32_t v)
{
- return s.isEmpty();
+ return UnicodeStringIterator(s, i + v);
}
+
+ private:
+ const icu::UnicodeString* s;
+ int32_t i;
+};
+CXXOPTS_DIAGNOSTIC_POP
+
+inline
+String&
+stringAppend(String&s, String a)
+{
+ return s.append(std::move(a));
}
-namespace std
+inline
+String&
+stringAppend(String& s, std::size_t n, UChar32 c)
{
- inline
- cxxopts::UnicodeStringIterator
- begin(const icu::UnicodeString& s)
+ for (std::size_t i = 0; i != n; ++i)
{
- return cxxopts::UnicodeStringIterator(&s, 0);
+ s.append(c);
}
- inline
- cxxopts::UnicodeStringIterator
- end(const icu::UnicodeString& s)
+ return s;
+}
+
+template <typename Iterator>
+String&
+stringAppend(String& s, Iterator begin, Iterator end)
+{
+ while (begin != end)
{
- return cxxopts::UnicodeStringIterator(&s, s.length());
+ s.append(*begin);
+ ++begin;
}
+
+ return s;
+}
+
+inline
+size_t
+stringLength(const String& s)
+{
+ return static_cast<size_t>(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 cxxopts
+
+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());
}
+} // namespace std
+
//ifdef CXXOPTS_USE_UNICODE
#else
-namespace cxxopts
+namespace cxxopts {
+
+using String = std::string;
+
+template <typename T>
+T
+toLocalString(T&& t)
{
- typedef std::string String;
+ return std::forward<T>(t);
+}
- template <typename T>
- T
- toLocalString(T&& t)
+inline
+std::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, std::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 T>
+std::string
+toUTF8String(T&& t)
+{
+ return std::forward<T>(t);
+}
+
+inline
+bool
+empty(const std::string& s)
+{
+ return s.empty();
+}
+
+} // namespace cxxopts
+
+//ifdef CXXOPTS_USE_UNICODE
+#endif
+
+namespace cxxopts {
+
+namespace {
+CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'");
+CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'");
+} // namespace
+
+// 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<cxxopts::Value>' has accessible non-virtual
+// destructor This will be ignored under other compilers like LLVM clang.
+CXXOPTS_DIAGNOSTIC_PUSH
+CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor")
+
+// some older versions of GCC warn under this warning
+CXXOPTS_IGNORE_WARNING("-Weffc++")
+class Value : public std::enable_shared_from_this<Value>
+{
+ public:
+
+ virtual ~Value() = default;
+
+ virtual
+ std::shared_ptr<Value>
+ 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<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>
+ no_implicit_value() = 0;
+
+ virtual bool
+ is_boolean() const = 0;
+};
+
+CXXOPTS_DIAGNOSTIC_POP
+
+namespace exceptions {
+
+class exception : public std::exception
+{
+ public:
+ explicit exception(std::string message)
+ : m_message(std::move(message))
{
- return std::forward<T>(t);
}
- inline
- size_t
- stringLength(const String& s)
+ CXXOPTS_NODISCARD
+ const char*
+ what() const noexcept override
{
- return s.length();
+ return m_message.c_str();
}
- inline
- String&
- stringAppend(String&s, const String& a)
+ private:
+ std::string m_message;
+};
+
+class specification : public exception
+{
+ public:
+
+ explicit specification(const std::string& message)
+ : exception(message)
{
- return s.append(a);
}
+};
- inline
- String&
- stringAppend(String& s, size_t n, char c)
+class parsing : public exception
+{
+ public:
+ explicit parsing(const std::string& message)
+ : exception(message)
{
- return s.append(n, c);
}
+};
- template <typename Iterator>
- String&
- stringAppend(String& s, Iterator begin, Iterator end)
+class option_already_exists : public specification
+{
+ public:
+ explicit option_already_exists(const std::string& option)
+ : specification("Option " + LQUOTE + option + RQUOTE + " already exists")
{
- return s.append(begin, end);
}
+};
- template <typename T>
- std::string
- toUTF8String(T&& t)
+class invalid_option_format : public specification
+{
+ public:
+ explicit invalid_option_format(const std::string& format)
+ : specification("Invalid option format " + LQUOTE + format + RQUOTE)
{
- return std::forward<T>(t);
}
+};
- inline
- bool
- empty(const std::string& s)
+class invalid_option_syntax : public parsing {
+ public:
+ explicit invalid_option_syntax(const std::string& text)
+ : parsing("Argument " + LQUOTE + text + RQUOTE +
+ " starts with a - but has incorrect syntax")
{
- return s.empty();
}
-} // namespace cxxopts
+};
-//ifdef CXXOPTS_USE_UNICODE
-#endif
+class no_such_option : public parsing
+{
+ public:
+ explicit no_such_option(const std::string& option)
+ : parsing("Option " + LQUOTE + option + RQUOTE + " does not exist")
+ {
+ }
+};
-namespace cxxopts
+class missing_argument : public parsing
{
- namespace
+ public:
+ explicit missing_argument(const std::string& option)
+ : parsing(
+ "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
+ )
{
-#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<cxxopts::Value>' has accessible non-virtual destructor
-#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
-#pragma GCC diagnostic push
-// This will be ignored under other compilers like LLVM clang.
-#endif
- class Value : public std::enable_shared_from_this<Value>
+class option_requires_argument : public parsing
+{
+ public:
+ explicit option_requires_argument(const std::string& option)
+ : parsing(
+ "Option " + LQUOTE + option + RQUOTE + " requires an argument"
+ )
{
- public:
+ }
+};
- virtual ~Value() = default;
+class gratuitous_argument_for_option : public parsing
+{
+ public:
+ gratuitous_argument_for_option
+ (
+ const std::string& option,
+ const std::string& arg
+ )
+ : parsing(
+ "Option " + LQUOTE + option + RQUOTE +
+ " does not take an argument, but argument " +
+ LQUOTE + arg + RQUOTE + " given"
+ )
+ {
+ }
+};
- virtual
- std::shared_ptr<Value>
- clone() const = 0;
+class requested_option_not_present : public parsing
+{
+ public:
+ explicit requested_option_not_present(const std::string& option)
+ : parsing("Option " + LQUOTE + option + RQUOTE + " not present")
+ {
+ }
+};
- virtual void
- parse(const std::string& text) const = 0;
+class option_has_no_value : public exception
+{
+ public:
+ explicit option_has_no_value(const std::string& option)
+ : exception(
+ !option.empty() ?
+ ("Option " + LQUOTE + option + RQUOTE + " has no value") :
+ "Option has no value")
+ {
+ }
+};
- virtual void
- parse() const = 0;
+class incorrect_argument_type : public parsing
+{
+ public:
+ explicit incorrect_argument_type
+ (
+ const std::string& arg
+ )
+ : parsing(
+ "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
+ )
+ {
+ }
+};
- virtual bool
- has_default() const = 0;
+} // namespace exceptions
- virtual bool
- is_container() const = 0;
- virtual bool
- has_implicit() const = 0;
+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");
- virtual std::string
- get_default_value() const = 0;
+#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
+}
- virtual std::string
- get_implicit_value() const = 0;
+using OptionNames = std::vector<std::string>;
- virtual std::shared_ptr<Value>
- default_value(const std::string& value) = 0;
+namespace values {
- virtual std::shared_ptr<Value>
- implicit_value(const std::string& value) = 0;
+namespace parser_tool {
- virtual std::shared_ptr<Value>
- no_implicit_value() = 0;
+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<exceptions::incorrect_argument_type>(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<exceptions::incorrect_argument_type>(text);
+ }
+ return desc;
+}
- virtual bool
- is_boolean() const = 0;
- };
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#endif
- class OptionException : public std::exception
+inline bool IsTrueText(const std::string &text)
+{
+ const char *pdata = text.c_str();
+ if (*pdata == 't' || *pdata == 'T')
{
- public:
- explicit OptionException(std::string message)
- : m_message(std::move(message))
+ pdata += 1;
+ if (strncmp(pdata, "rue\0", 4) == 0)
{
+ return true;
}
+ }
+ else if (strncmp(pdata, "1\0", 2) == 0)
+ {
+ return true;
+ }
+ return false;
+}
- CXXOPTS_NODISCARD
- const char*
- what() const noexcept override
+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 m_message.c_str();
+ return true;
}
+ }
+ else if (strncmp(pdata, "0\0", 2) == 0)
+ {
+ return true;
+ }
+ return false;
+}
- private:
- std::string m_message;
- };
+inline OptionNames split_option_names(const std::string &text)
+{
+ OptionNames split_names;
+
+ std::string::size_type token_start_pos = 0;
+ auto length = text.length();
- class OptionSpecException : public OptionException
+ if (length == 0)
{
- public:
+ throw_or_mimic<exceptions::invalid_option_format>(text);
+ }
- explicit OptionSpecException(const std::string& message)
- : OptionException(message)
- {
+ while (token_start_pos < length) {
+ const auto &npos = std::string::npos;
+ auto next_non_space_pos = text.find_first_not_of(' ', token_start_pos);
+ if (next_non_space_pos == npos) {
+ throw_or_mimic<exceptions::invalid_option_format>(text);
}
- };
-
- class OptionParseException : public OptionException
- {
- public:
- explicit OptionParseException(const std::string& message)
- : OptionException(message)
- {
+ token_start_pos = next_non_space_pos;
+ auto next_delimiter_pos = text.find(',', token_start_pos);
+ if (next_delimiter_pos == token_start_pos) {
+ throw_or_mimic<exceptions::invalid_option_format>(text);
}
- };
-
- class option_exists_error : public OptionSpecException
- {
- public:
- explicit option_exists_error(const std::string& option)
- : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")
- {
+ if (next_delimiter_pos == npos) {
+ next_delimiter_pos = length;
}
- };
-
- class invalid_option_format_error : public OptionSpecException
- {
- public:
- explicit invalid_option_format_error(const std::string& format)
- : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)
+ auto token_length = next_delimiter_pos - token_start_pos;
+ // validate the token itself matches the regex /([:alnum:][-_[:alnum:]]*/
{
- }
- };
+ const char* option_name_valid_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "_-.?";
- 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")
- {
+ if (!std::isalnum(text[token_start_pos], std::locale::classic()) ||
+ text.find_first_not_of(option_name_valid_chars, token_start_pos) < next_delimiter_pos) {
+ throw_or_mimic<exceptions::invalid_option_format>(text);
+ }
}
- };
+ split_names.emplace_back(text.substr(token_start_pos, token_length));
+ token_start_pos = next_delimiter_pos + 1;
+ }
+ return split_names;
+}
- class option_not_exists_exception : public OptionParseException
+inline ArguDesc ParseArgument(const char *arg, bool &matched)
+{
+ ArguDesc argu_desc;
+ const char *pdata = arg;
+ matched = false;
+ if (strncmp(pdata, "--", 2) == 0)
{
- public:
- explicit option_not_exists_exception(const std::string& option)
- : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
+ pdata += 2;
+ if (isalnum(*pdata, std::locale::classic()))
{
+ argu_desc.arg_name.push_back(*pdata);
+ pdata += 1;
+ while (isalnum(*pdata, std::locale::classic()) || *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;
+ }
+ }
}
- };
-
- class missing_argument_exception : public OptionParseException
+ }
+ else if (strncmp(pdata, "-", 1) == 0)
{
- public:
- explicit missing_argument_exception(const std::string& option)
- : OptionParseException(
- "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
- )
+ pdata += 1;
+ argu_desc.grouping = true;
+ while (isalnum(*pdata, std::locale::classic()))
{
+ argu_desc.arg_name.push_back(*pdata);
+ pdata += 1;
}
- };
+ matched = !argu_desc.arg_name.empty() && *pdata == '\0';
+ }
+ return argu_desc;
+}
- 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"
- )
- {
- }
- };
+#else // CXXOPTS_NO_REGEX
+
+namespace {
+CXXOPTS_LINKONCE
+std::basic_regex<char> integer_pattern
+ ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
+CXXOPTS_LINKONCE
+std::basic_regex<char> truthy_pattern
+ ("(t|T)(rue)?|1");
+CXXOPTS_LINKONCE
+std::basic_regex<char> falsy_pattern
+ ("(f|F)(alse)?|0");
+CXXOPTS_LINKONCE
+std::basic_regex<char> option_matcher
+ ("--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)");
+CXXOPTS_LINKONCE
+std::basic_regex<char> option_specifier
+ ("([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*");
+CXXOPTS_LINKONCE
+std::basic_regex<char> option_specifier_separator(", *");
+
+} // namespace
+
+inline IntegerDesc SplitInteger(const std::string &text)
+{
+ std::smatch match;
+ std::regex_match(text, match, integer_pattern);
- class option_not_has_argument_exception : public OptionParseException
+ if (match.length() == 0)
{
- 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"
- )
- {
- }
- };
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
+ }
- class option_not_present_exception : public OptionParseException
- {
- public:
- explicit option_not_present_exception(const std::string& option)
- : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
- {
- }
- };
+ IntegerDesc desc;
+ desc.negative = match[1];
+ desc.base = match[2];
+ desc.value = match[3];
- class option_has_no_value_exception : public OptionException
+ if (match.length(4) > 0)
{
- 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")
- {
- }
- };
+ desc.base = match[5];
+ desc.value = "0";
+ return desc;
+ }
- class argument_incorrect_type : public OptionParseException
- {
- public:
- explicit argument_incorrect_type
- (
- const std::string& arg
- )
- : OptionParseException(
- "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
- )
- {
- }
- };
+ return desc;
+}
- 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"
- )
- {
- }
- };
+inline bool IsTrueText(const std::string &text)
+{
+ std::smatch result;
+ std::regex_match(text, result, truthy_pattern);
+ return !result.empty();
+}
- 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");
+inline bool IsFalseText(const std::string &text)
+{
+ std::smatch result;
+ std::regex_match(text, result, falsy_pattern);
+ return !result.empty();
+}
-#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
+// Gets the option names specified via a single, comma-separated string,
+// and returns the separate, space-discarded, non-empty names
+// (without considering which or how many are single-character)
+inline OptionNames split_option_names(const std::string &text)
+{
+ if (!std::regex_match(text.c_str(), option_specifier))
+ {
+ throw_or_mimic<exceptions::invalid_option_format>(text);
}
- 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
-
- namespace detail
- {
- template <typename T, bool B>
- struct SignedCheck;
+ OptionNames split_names;
- 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);
- }
- }
- }
- };
+ constexpr int use_non_matches { -1 };
+ auto token_iterator = std::sregex_token_iterator(
+ text.begin(), text.end(), option_specifier_separator, use_non_matches);
+ std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names));
+ return split_names;
+}
- 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)
- {
- SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
- }
- } // namespace detail
+inline ArguDesc ParseArgument(const char *arg, bool &matched)
+{
+ std::match_results<const char*> result;
+ std::regex_match(arg, result, option_matcher);
+ matched = !result.empty();
- template <typename R, typename T>
- void
- checked_negate(R& r, T&& t, const std::string&, std::true_type)
+ 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)
{
- // 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<R>(-static_cast<R>(t-1)-1);
+ argu_desc.grouping = true;
+ argu_desc.arg_name = result[4].str();
}
+ }
- template <typename R, typename T>
- void
- checked_negate(R&, T&&, const std::string& text, std::false_type)
- {
- throw_or_mimic<argument_incorrect_type>(text);
- }
+ return argu_desc;
+}
- template <typename T>
- void
- integer_parser(const std::string& text, T& value)
- {
- std::smatch match;
- std::regex_match(text, match, integer_pattern);
+#endif // CXXOPTS_NO_REGEX
+#undef CXXOPTS_NO_REGEX
+} // namespace parser_tool
+
+namespace detail {
+
+template <typename T, bool B>
+struct SignedCheck;
- if (match.length() == 0)
+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);
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
}
-
- if (match.length(4) > 0)
+ }
+ else
+ {
+ if (u > static_cast<U>((std::numeric_limits<T>::max)()))
{
- value = 0;
- return;
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
}
+ }
+ }
+};
- using US = typename std::make_unsigned<T>::type;
+template <typename T>
+struct SignedCheck<T, false>
+{
+ template <typename U>
+ void
+ operator()(bool, U, const std::string&) const {}
+};
- 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;
+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);
+}
- auto value_match = match[3];
+} // namespace detail
- US result = 0;
+template <typename R, typename T>
+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<R>(-static_cast<R>(t-1)-1);
+}
- for (auto iter = value_match.first; iter != value_match.second; ++iter)
- {
- US digit = 0;
+template <typename R, typename T>
+void
+checked_negate(R&, T&&, const std::string& text, std::false_type)
+{
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
+}
- 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>
+void
+integer_parser(const std::string& text, T& value)
+{
+ parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
- const US next = static_cast<US>(result * base + digit);
- if (result > next)
- {
- throw_or_mimic<argument_incorrect_type>(text);
- }
+ using US = typename std::make_unsigned<T>::type;
+ constexpr bool is_signed = std::numeric_limits<T>::is_signed;
- result = next;
- }
+ 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;
- detail::check_signed_range<T>(negative, result, text);
+ US result = 0;
- if (negative)
- {
- checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
- }
- else
- {
- value = static_cast<T>(result);
- }
- }
+ for (char ch : value_match)
+ {
+ US digit = 0;
- template <typename T>
- void stringstream_parser(const std::string& text, T& value)
+ if (ch >= '0' && ch <= '9')
{
- std::stringstream in(text);
- in >> value;
- if (!in) {
- throw_or_mimic<argument_incorrect_type>(text);
- }
+ digit = static_cast<US>(ch - '0');
}
-
- inline
- void
- parse_value(const std::string& text, uint8_t& value)
+ else if (base == 16 && ch >= 'a' && ch <= 'f')
{
- integer_parser(text, value);
+ digit = static_cast<US>(ch - 'a' + 10);
}
-
- inline
- void
- parse_value(const std::string& text, int8_t& value)
+ else if (base == 16 && ch >= 'A' && ch <= 'F')
{
- integer_parser(text, value);
+ digit = static_cast<US>(ch - 'A' + 10);
}
-
- inline
- void
- parse_value(const std::string& text, uint16_t& value)
+ else
{
- integer_parser(text, value);
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
}
- inline
- void
- parse_value(const std::string& text, int16_t& value)
+ const US next = static_cast<US>(result * base + digit);
+ if (result > next)
{
- integer_parser(text, value);
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
}
- inline
- void
- parse_value(const std::string& text, uint32_t& value)
- {
- integer_parser(text, value);
- }
+ result = next;
+ }
- inline
- void
- parse_value(const std::string& text, int32_t& value)
- {
- integer_parser(text, value);
- }
+ detail::check_signed_range<T>(negative, result, text);
- inline
- void
- parse_value(const std::string& text, uint64_t& value)
- {
- integer_parser(text, value);
- }
+ if (negative)
+ {
+ checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
+ }
+ else
+ {
+ value = static_cast<T>(result);
+ }
+}
- inline
- void
- parse_value(const std::string& text, int64_t& value)
- {
- integer_parser(text, value);
- }
+template <typename T>
+void stringstream_parser(const std::string& text, T& value)
+{
+ std::stringstream in(text);
+ in >> value;
+ if (!in) {
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
+ }
+}
- inline
- void
- parse_value(const std::string& text, bool& value)
- {
- std::smatch result;
- std::regex_match(text, result, truthy_pattern);
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr
+ >
+void parse_value(const std::string& text, T& value)
+{
+ integer_parser(text, value);
+}
- if (!result.empty())
- {
- value = true;
- return;
- }
+inline
+void
+parse_value(const std::string& text, bool& value)
+{
+ if (parser_tool::IsTrueText(text))
+ {
+ value = true;
+ return;
+ }
- std::regex_match(text, result, falsy_pattern);
- if (!result.empty())
- {
- value = false;
- return;
- }
+ if (parser_tool::IsFalseText(text))
+ {
+ value = false;
+ return;
+ }
- throw_or_mimic<argument_incorrect_type>(text);
- }
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
+}
- inline
- void
- parse_value(const std::string& text, std::string& value)
- {
- value = 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 <typename T>
- void
- parse_value(const std::string& text, T& value) {
- stringstream_parser(text, value);
- }
+// 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,
+ typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr
+ >
+void
+parse_value(const std::string& text, T& value) {
+ stringstream_parser(text, value);
+}
- template <typename T>
- void
- parse_value(const std::string& text, std::vector<T>& value)
- {
- 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));
- }
- }
+template <typename T>
+void
+parse_value(const std::string& text, std::vector<T>& 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 <typename T>
- void
- parse_value(const std::string& text, std::optional<T>& value)
- {
- T result;
- parse_value(text, result);
- value = std::move(result);
- }
+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
- inline
- void parse_value(const std::string& text, char& c)
- {
- if (text.length() != 1)
- {
- throw_or_mimic<argument_incorrect_type>(text);
- }
+inline
+void parse_value(const std::string& text, char& c)
+{
+ if (text.length() != 1)
+ {
+ throw_or_mimic<exceptions::incorrect_argument_type>(text);
+ }
- c = text[0];
- }
+ c = text[0];
+}
- template <typename T>
- struct type_is_container
- {
- static constexpr bool value = false;
- };
+template <typename T>
+struct type_is_container
+{
+ static constexpr bool value = false;
+};
- template <typename T>
- struct type_is_container<std::vector<T>>
- {
- static constexpr bool value = true;
- };
+template <typename T>
+struct type_is_container<std::vector<T>>
+{
+ static constexpr bool value = true;
+};
- template <typename T>
- class abstract_value : public Value
+template <typename T>
+class abstract_value : public Value
+{
+ using Self = abstract_value<T>;
+
+ public:
+ abstract_value()
+ : m_result(std::make_shared<T>())
+ , 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)
{
- using Self = abstract_value<T>;
+ m_result = std::make_shared<T>();
+ m_store = m_result.get();
+ }
+ else
+ {
+ m_store = rhs.m_store;
+ }
- public:
- abstract_value()
- : m_result(std::make_shared<T>())
- , m_store(m_result.get())
- {
- }
+ m_default = rhs.m_default;
+ m_implicit = rhs.m_implicit;
+ m_default_value = rhs.m_default_value;
+ m_implicit_value = rhs.m_implicit_value;
+ }
- explicit abstract_value(T* t)
- : m_store(t)
- {
- }
+ void
+ parse(const std::string& text) const override
+ {
+ parse_value(text, *m_store);
+ }
- ~abstract_value() override = default;
+ bool
+ is_container() const override
+ {
+ return type_is_container<T>::value;
+ }
- abstract_value& operator=(const abstract_value&) = default;
+ void
+ parse() const override
+ {
+ parse_value(m_default_value, *m_store);
+ }
- 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;
- }
+ bool
+ has_default() const override
+ {
+ return m_default;
+ }
- m_default = rhs.m_default;
- m_implicit = rhs.m_implicit;
- m_default_value = rhs.m_default_value;
- m_implicit_value = rhs.m_implicit_value;
- }
+ bool
+ has_implicit() const override
+ {
+ return m_implicit;
+ }
- void
- parse(const std::string& text) const override
- {
- parse_value(text, *m_store);
- }
+ std::shared_ptr<Value>
+ default_value(const std::string& value) override
+ {
+ m_default = true;
+ m_default_value = value;
+ return shared_from_this();
+ }
- bool
- is_container() const override
- {
- return type_is_container<T>::value;
- }
+ std::shared_ptr<Value>
+ implicit_value(const std::string& value) override
+ {
+ m_implicit = true;
+ m_implicit_value = value;
+ return shared_from_this();
+ }
- void
- parse() const override
- {
- parse_value(m_default_value, *m_store);
- }
+ std::shared_ptr<Value>
+ no_implicit_value() override
+ {
+ m_implicit = false;
+ return shared_from_this();
+ }
- bool
- has_default() const override
- {
- return m_default;
- }
+ std::string
+ get_default_value() const override
+ {
+ return m_default_value;
+ }
- bool
- has_implicit() const override
- {
- return m_implicit;
- }
+ std::string
+ get_implicit_value() const override
+ {
+ return m_implicit_value;
+ }
- std::shared_ptr<Value>
- default_value(const std::string& value) override
- {
- m_default = true;
- m_default_value = value;
- return shared_from_this();
- }
+ bool
+ is_boolean() const override
+ {
+ return std::is_same<T, bool>::value;
+ }
- std::shared_ptr<Value>
- implicit_value(const std::string& value) override
- {
- m_implicit = true;
- m_implicit_value = value;
- return shared_from_this();
- }
+ const T&
+ get() const
+ {
+ if (m_store == nullptr)
+ {
+ return *m_result;
+ }
+ return *m_store;
+ }
- std::shared_ptr<Value>
- no_implicit_value() override
- {
- m_implicit = false;
- return shared_from_this();
- }
+ protected:
+ std::shared_ptr<T> m_result{};
+ T* m_store{};
- std::string
- get_default_value() const override
- {
- return m_default_value;
- }
+ bool m_default = false;
+ bool m_implicit = false;
- std::string
- get_implicit_value() const override
- {
- return m_implicit_value;
- }
+ std::string m_default_value{};
+ std::string m_implicit_value{};
+};
- bool
- is_boolean() const override
- {
- return std::is_same<T, bool>::value;
- }
+template <typename T>
+class standard_value : public abstract_value<T>
+{
+ public:
+ using abstract_value<T>::abstract_value;
- const T&
- get() const
- {
- if (m_store == nullptr)
- {
- return *m_result;
- }
- return *m_store;
- }
+ CXXOPTS_NODISCARD
+ std::shared_ptr<Value>
+ clone() const override
+ {
+ return std::make_shared<standard_value<T>>(*this);
+ }
+};
- protected:
- std::shared_ptr<T> m_result{};
- T* m_store{};
+template <>
+class standard_value<bool> : public abstract_value<bool>
+{
+ public:
+ ~standard_value() override = default;
- bool m_default = false;
- bool m_implicit = false;
+ standard_value()
+ {
+ set_default_and_implicit();
+ }
- std::string m_default_value{};
- std::string m_implicit_value{};
- };
+ explicit standard_value(bool* b)
+ : abstract_value(b)
+ {
+ m_implicit = true;
+ m_implicit_value = "true";
+ }
- template <typename T>
- class standard_value : public abstract_value<T>
- {
- public:
- using abstract_value<T>::abstract_value;
+ std::shared_ptr<Value>
+ clone() const override
+ {
+ return std::make_shared<standard_value<bool>>(*this);
+ }
- CXXOPTS_NODISCARD
- std::shared_ptr<Value>
- clone() const
- {
- return std::make_shared<standard_value<T>>(*this);
- }
- };
+ private:
- template <>
- class standard_value<bool> : public abstract_value<bool>
- {
- public:
- ~standard_value() override = default;
+ void
+ set_default_and_implicit()
+ {
+ m_default = true;
+ m_default_value = "false";
+ m_implicit = true;
+ m_implicit_value = "true";
+ }
+};
- standard_value()
- {
- set_default_and_implicit();
- }
+} // namespace values
- explicit standard_value(bool* b)
- : abstract_value(b)
- {
- set_default_and_implicit();
- }
+template <typename T>
+std::shared_ptr<Value>
+value()
+{
+ return std::make_shared<values::standard_value<T>>();
+}
- std::shared_ptr<Value>
- clone() const override
- {
- return std::make_shared<standard_value<bool>>(*this);
- }
+template <typename T>
+std::shared_ptr<Value>
+value(T& t)
+{
+ return std::make_shared<values::standard_value<T>>(&t);
+}
- private:
+class OptionAdder;
- void
- set_default_and_implicit()
- {
- m_default = true;
- m_default_value = "false";
- m_implicit = true;
- m_implicit_value = "true";
- }
- };
- } // namespace values
+CXXOPTS_NODISCARD
+inline
+const std::string&
+first_or_empty(const OptionNames& long_names)
+{
+ static const std::string empty{""};
+ return long_names.empty() ? empty : long_names.front();
+}
- template <typename T>
- std::shared_ptr<Value>
- value()
+class OptionDetails
+{
+ public:
+ OptionDetails
+ (
+ std::string short_,
+ OptionNames long_,
+ String desc,
+ std::shared_ptr<const Value> val
+ )
+ : m_short(std::move(short_))
+ , m_long(std::move(long_))
+ , m_desc(std::move(desc))
+ , m_value(std::move(val))
+ , m_count(0)
{
- return std::make_shared<values::standard_value<T>>();
+ m_hash = std::hash<std::string>{}(first_long_name() + m_short);
}
- template <typename T>
+ 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<Value>
- value(T& t)
+ make_storage() const
{
- return std::make_shared<values::standard_value<T>>(&t);
+ return m_value->clone();
}
- class OptionAdder;
+ CXXOPTS_NODISCARD
+ const std::string&
+ short_name() const
+ {
+ return m_short;
+ }
- class OptionDetails
+ CXXOPTS_NODISCARD
+ const std::string&
+ first_long_name() const
{
- public:
- OptionDetails
- (
- std::string short_,
- std::string long_,
- String desc,
- std::shared_ptr<const Value> 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<std::string>{}(m_long + m_short);
- }
+ return first_or_empty(m_long);
+ }
- OptionDetails(const OptionDetails& rhs)
- : m_desc(rhs.m_desc)
- , m_count(rhs.m_count)
- {
- m_value = rhs.m_value->clone();
- }
+ CXXOPTS_NODISCARD
+ const std::string&
+ essential_name() const
+ {
+ return m_long.empty() ? m_short : m_long.front();
+ }
- OptionDetails(OptionDetails&& rhs) = default;
+ CXXOPTS_NODISCARD
+ const OptionNames &
+ long_names() const
+ {
+ return m_long;
+ }
- CXXOPTS_NODISCARD
- const String&
- description() const
- {
- return m_desc;
- }
+ std::size_t
+ hash() const
+ {
+ return m_hash;
+ }
- CXXOPTS_NODISCARD
- const Value&
- value() const {
- return *m_value;
- }
+ private:
+ std::string m_short{};
+ OptionNames m_long{};
+ String m_desc{};
+ std::shared_ptr<const Value> m_value{};
+ int m_count;
- CXXOPTS_NODISCARD
- std::shared_ptr<Value>
- make_storage() const
- {
- return m_value->clone();
- }
+ std::size_t m_hash{};
+};
- CXXOPTS_NODISCARD
- const std::string&
- short_name() const
- {
- return m_short;
- }
+struct HelpOptionDetails
+{
+ std::string s;
+ OptionNames 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{};
+};
- CXXOPTS_NODISCARD
- const std::string&
- long_name() const
- {
- return m_long;
- }
+class OptionValue
+{
+ public:
+ void
+ parse
+ (
+ const std::shared_ptr<const OptionDetails>& details,
+ const std::string& text
+ )
+ {
+ ensure_value(details);
+ ++m_count;
+ m_value->parse(text);
+ m_long_names = &details->long_names();
+ }
- size_t
- hash() const
- {
- return m_hash;
- }
+ void
+ parse_default(const std::shared_ptr<const OptionDetails>& details)
+ {
+ ensure_value(details);
+ m_default = true;
+ m_long_names = &details->long_names();
+ m_value->parse();
+ }
- private:
- std::string m_short{};
- std::string m_long{};
- String m_desc{};
- std::shared_ptr<const Value> m_value{};
- int m_count;
+ void
+ parse_no_value(const std::shared_ptr<const OptionDetails>& details)
+ {
+ m_long_names = &details->long_names();
+ }
- size_t m_hash{};
- };
+#if defined(CXXOPTS_NULL_DEREF_IGNORE)
+CXXOPTS_DIAGNOSTIC_PUSH
+CXXOPTS_IGNORE_WARNING("-Wnull-dereference")
+#endif
- 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;
- };
+ CXXOPTS_NODISCARD
+ std::size_t
+ count() const noexcept
+ {
+ return m_count;
+ }
+
+#if defined(CXXOPTS_NULL_DEREF_IGNORE)
+CXXOPTS_DIAGNOSTIC_POP
+#endif
- struct HelpGroupDetails
+ // TODO: maybe default options should count towards the number of arguments
+ CXXOPTS_NODISCARD
+ bool
+ has_default() const noexcept
{
- std::string name{};
- std::string description{};
- std::vector<HelpOptionDetails> options{};
- };
+ return m_default;
+ }
- class OptionValue
+ template <typename T>
+ const T&
+ as() const
{
- public:
- void
- parse
- (
- const std::shared_ptr<const OptionDetails>& details,
- const std::string& text
- )
- {
- ensure_value(details);
- ++m_count;
- m_value->parse(text);
- m_long_name = &details->long_name();
+ if (m_value == nullptr) {
+ throw_or_mimic<exceptions::option_has_no_value>(
+ m_long_names == nullptr ? "" : first_or_empty(*m_long_names));
}
- void
- parse_default(const std::shared_ptr<const OptionDetails>& details)
- {
- ensure_value(details);
- m_default = true;
- m_long_name = &details->long_name();
- m_value->parse();
- }
+ return CXXOPTS_RTTI_CAST<const values::standard_value<T>&>(*m_value).get();
+ }
- CXXOPTS_NODISCARD
- size_t
- count() const noexcept
+ private:
+ void
+ ensure_value(const std::shared_ptr<const OptionDetails>& details)
+ {
+ if (m_value == nullptr)
{
- return m_count;
+ m_value = details->make_storage();
}
+ }
- // TODO: maybe default options should count towards the number of arguments
- CXXOPTS_NODISCARD
- bool
- has_default() const noexcept
- {
- return m_default;
- }
- template <typename T>
- const T&
- as() const
- {
- if (m_value == nullptr) {
- throw_or_mimic<option_has_no_value_exception>(
- m_long_name == nullptr ? "" : *m_long_name);
+ const OptionNames * m_long_names = 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<Value> m_value{};
+ std::size_t m_count = 0;
+ bool m_default = false;
+};
+
+class KeyValue
+{
+ public:
+ KeyValue(std::string key_, std::string value_) noexcept
+ : 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 <typename T>
+ 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<std::size_t, OptionValue>;
+using NameHashMap = std::unordered_map<std::string, std::size_t>;
+
+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;
+
+// GCC complains about m_iter not being initialised in the member
+// initializer list
+CXXOPTS_DIAGNOSTIC_PUSH
+CXXOPTS_IGNORE_WARNING("-Weffc++")
+ Iterator(const ParseResult *pr, bool end=false)
+ : m_pr(pr)
+ {
+ if (end)
+ {
+ m_sequential = false;
+ m_iter = m_pr->m_defaults.end();
}
+ else
+ {
+ m_sequential = true;
+ m_iter = m_pr->m_sequential.begin();
-#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
+ if (m_iter == m_pr->m_sequential.end())
+ {
+ m_sequential = false;
+ m_iter = m_pr->m_defaults.begin();
+ }
+ }
}
+CXXOPTS_DIAGNOSTIC_POP
- private:
- void
- ensure_value(const std::shared_ptr<const OptionDetails>& details)
+ Iterator& operator++()
{
- if (m_value == nullptr)
+ ++m_iter;
+ if(m_sequential && m_iter == m_pr->m_sequential.end())
{
- m_value = details->make_storage();
+ m_sequential = false;
+ m_iter = m_pr->m_defaults.begin();
+ return *this;
}
+ return *this;
}
+ Iterator operator++(int)
+ {
+ Iterator retval = *this;
+ ++(*this);
+ return retval;
+ }
- 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<Value> 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_))
+ bool operator==(const Iterator& other) const
{
+ return (m_sequential == other.m_sequential) && (m_iter == other.m_iter);
}
- CXXOPTS_NODISCARD
- const std::string&
- key() const
+ bool operator!=(const Iterator& other) const
{
- return m_key;
+ return !(*this == other);
}
- CXXOPTS_NODISCARD
- const std::string&
- value() const
+ const KeyValue& operator*()
{
- return m_value;
+ return *m_iter;
}
- template <typename T>
- T
- as() const
+ const KeyValue* operator->()
{
- T result;
- values::parse_value(m_value, result);
- return result;
+ return m_iter.operator->();
}
private:
- std::string m_key;
- std::string m_value;
+ const ParseResult* m_pr;
+ std::vector<KeyValue>::const_iterator m_iter;
+ bool m_sequential = true;
};
- using ParsedHashMap = std::unordered_map<size_t, OptionValue>;
- using NameHashMap = std::unordered_map<std::string, size_t>;
+ ParseResult() = default;
+ ParseResult(const ParseResult&) = default;
- class ParseResult
+ ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential,
+ std::vector<KeyValue> default_opts, std::vector<std::string>&& 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))
{
- public:
+ }
+
+ ParseResult& operator=(ParseResult&&) = default;
+ ParseResult& operator=(const ParseResult&) = default;
- ParseResult() {}
+ Iterator
+ begin() const
+ {
+ return Iterator(this);
+ }
- ParseResult(const ParseResult&) = default;
+ Iterator
+ end() const
+ {
+ return Iterator(this, true);
+ }
- ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential, std::vector<std::string>&& unmatched_args)
- : m_keys(std::move(keys))
- , m_values(std::move(values))
- , m_sequential(std::move(sequential))
- , m_unmatched(std::move(unmatched_args))
+ std::size_t
+ count(const std::string& o) const
+ {
+ auto iter = m_keys.find(o);
+ if (iter == m_keys.end())
{
+ return 0;
}
- ParseResult& operator=(ParseResult&&) = default;
- ParseResult& operator=(const ParseResult&) = default;
+ auto viter = m_values.find(iter->second);
- size_t
- count(const std::string& o) const
+ if (viter == m_values.end())
{
- auto iter = m_keys.find(o);
- if (iter == m_keys.end())
- {
- return 0;
- }
+ return 0;
+ }
- auto viter = m_values.find(iter->second);
+ return viter->second.count();
+ }
- if (viter == m_values.end())
- {
- return 0;
- }
+ const OptionValue&
+ operator[](const std::string& option) const
+ {
+ auto iter = m_keys.find(option);
- return viter->second.count();
+ if (iter == m_keys.end())
+ {
+ throw_or_mimic<exceptions::requested_option_not_present>(option);
}
- const OptionValue&
- operator[](const std::string& option) const
+ auto viter = m_values.find(iter->second);
+
+ if (viter == m_values.end())
{
- auto iter = m_keys.find(option);
+ throw_or_mimic<exceptions::requested_option_not_present>(option);
+ }
- if (iter == m_keys.end())
- {
- throw_or_mimic<option_not_present_exception>(option);
- }
+ return viter->second;
+ }
- auto viter = m_values.find(iter->second);
+ const std::vector<KeyValue>&
+ arguments() const
+ {
+ return m_sequential;
+ }
- if (viter == m_values.end())
- {
- throw_or_mimic<option_not_present_exception>(option);
- }
+ const std::vector<std::string>&
+ unmatched() const
+ {
+ return m_unmatched;
+ }
- return viter->second;
- }
+ const std::vector<KeyValue>&
+ defaults() const
+ {
+ return m_defaults;
+ }
- const std::vector<KeyValue>&
- arguments() const
+ const std::string
+ arguments_string() const
+ {
+ std::string result;
+ for(const auto& kv: m_sequential)
{
- return m_sequential;
+ result += kv.key() + " = " + kv.value() + "\n";
}
-
- const std::vector<std::string>&
- unmatched() const
+ for(const auto& kv: m_defaults)
{
- return m_unmatched;
+ result += kv.key() + " = " + kv.value() + " " + "(default)" + "\n";
}
+ return result;
+ }
- private:
- NameHashMap m_keys{};
- ParsedHashMap m_values{};
- std::vector<KeyValue> m_sequential{};
- std::vector<std::string> m_unmatched{};
- };
+ private:
+ NameHashMap m_keys{};
+ ParsedHashMap m_values{};
+ std::vector<KeyValue> m_sequential{};
+ std::vector<KeyValue> m_defaults{};
+ std::vector<std::string> m_unmatched{};
+};
- struct Option
+struct Option
+{
+ Option
+ (
+ std::string opts,
+ std::string desc,
+ std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
+ std::string arg_help = ""
+ )
+ : opts_(std::move(opts))
+ , desc_(std::move(desc))
+ , value_(std::move(value))
+ , arg_help_(std::move(arg_help))
{
- Option
- (
- std::string opts,
- std::string desc,
- std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
- 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<const Value> value_;
- std::string arg_help_;
- };
+ std::string opts_;
+ std::string desc_;
+ std::shared_ptr<const Value> value_;
+ std::string arg_help_;
+};
- using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
- using PositionalList = std::vector<std::string>;
- using PositionalListIterator = PositionalList::const_iterator;
+using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
+using PositionalList = std::vector<std::string>;
+using PositionalListIterator = PositionalList::const_iterator;
- class OptionParser
+class OptionParser
+{
+ public:
+ OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
+ : m_options(options)
+ , m_positional(positional)
+ , m_allow_unrecognised(allow_unrecognised)
{
- 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);
+ ParseResult
+ parse(int argc, const char* const* argv);
- bool
- consume_positional(const std::string& a, PositionalListIterator& next);
+ 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<OptionDetails>& value,
- const std::string& name
- );
+ void
+ checked_parse_arg
+ (
+ int argc,
+ const char* const* argv,
+ int& current,
+ const std::shared_ptr<OptionDetails>& value,
+ const std::string& name
+ );
- void
- add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
+ void
+ add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
- void
- parse_option
- (
- const std::shared_ptr<OptionDetails>& value,
- const std::string& name,
- const std::string& arg = ""
- );
+ void
+ parse_option
+ (
+ const std::shared_ptr<OptionDetails>& value,
+ const std::string& name,
+ const std::string& arg = ""
+ );
- void
- parse_default(const std::shared_ptr<OptionDetails>& details);
+ void
+ parse_default(const std::shared_ptr<OptionDetails>& details);
- private:
+ void
+ parse_no_value(const std::shared_ptr<OptionDetails>& details);
- void finalise_aliases();
+ private:
- const OptionMap& m_options;
- const PositionalList& m_positional;
+ void finalise_aliases();
- std::vector<KeyValue> m_sequential{};
- bool m_allow_unrecognised;
+ const OptionMap& m_options;
+ const PositionalList& m_positional;
- ParsedHashMap m_parsed{};
- NameHashMap m_keys{};
- };
+ std::vector<KeyValue> m_sequential{};
+ std::vector<KeyValue> m_defaults{};
+ bool m_allow_unrecognised;
- class Options
+ ParsedHashMap m_parsed{};
+ NameHashMap m_keys{};
+};
+
+class Options
+{
+ public:
+
+ explicit Options(std::string program_name, std::string help_string = "")
+ : m_program(std::move(program_name))
+ , 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<OptionMap>())
{
- 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_options(std::make_shared<OptionMap>())
- {
- }
+ Options&
+ positional_help(std::string help_text)
+ {
+ m_positional_help = std::move(help_text);
+ return *this;
+ }
- 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&
- 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&
- show_positional_help()
- {
- m_show_positional = true;
- return *this;
- }
+ Options&
+ allow_unrecognised_options()
+ {
+ m_allow_unrecognised = true;
+ return *this;
+ }
- Options&
- allow_unrecognised_options()
- {
- m_allow_unrecognised = true;
- return *this;
- }
+ Options&
+ set_width(std::size_t width)
+ {
+ m_width = width;
+ return *this;
+ }
- ParseResult
- parse(int argc, const char* const* argv);
+ Options&
+ set_tab_expansion(bool expansion=true)
+ {
+ m_tab_expansion = expansion;
+ return *this;
+ }
- OptionAdder
- add_options(std::string group = "");
+ ParseResult
+ parse(int argc, const char* const* argv);
- void
- add_options
- (
- const std::string& group,
- std::initializer_list<Option> options
- );
+ OptionAdder
+ add_options(std::string group = "");
- void
- add_option
- (
- const std::string& group,
- const Option& option
- );
+ void
+ add_options
+ (
+ const std::string& group,
+ std::initializer_list<Option> options
+ );
- void
- add_option
- (
- const std::string& group,
- const std::string& s,
- const std::string& l,
- std::string desc,
- const std::shared_ptr<const Value>& value,
- std::string arg_help
- );
+ void
+ add_option
+ (
+ const std::string& group,
+ const Option& option
+ );
- //parse positional arguments into the given option
- void
- parse_positional(std::string option);
+ void
+ add_option
+ (
+ const std::string& group,
+ const std::string& s,
+ const OptionNames& l,
+ std::string desc,
+ const std::shared_ptr<const Value>& value,
+ std::string arg_help
+ );
- void
- parse_positional(std::vector<std::string> options);
+ void
+ add_option
+ (
+ const std::string& group,
+ const std::string& short_name,
+ const std::string& single_long_name,
+ std::string desc,
+ const std::shared_ptr<const Value>& value,
+ std::string arg_help
+ )
+ {
+ OptionNames long_names;
+ long_names.emplace_back(single_long_name);
+ add_option(group, short_name, long_names, desc, value, arg_help);
+ }
- void
- parse_positional(std::initializer_list<std::string> options);
+ //parse positional arguments into the given option
+ void
+ parse_positional(std::string option);
- template <typename Iterator>
- void
- parse_positional(Iterator begin, Iterator end) {
- parse_positional(std::vector<std::string>{begin, end});
- }
+ void
+ parse_positional(std::vector<std::string> options);
+
+ void
+ parse_positional(std::initializer_list<std::string> options);
- std::string
- help(const std::vector<std::string>& groups = {}) const;
+ template <typename Iterator>
+ void
+ parse_positional(Iterator begin, Iterator end) {
+ parse_positional(std::vector<std::string>{begin, end});
+ }
- std::vector<std::string>
- groups() const;
+ std::string
+ help(const std::vector<std::string>& groups = {}, bool print_usage=true) const;
- const HelpGroupDetails&
- group_help(const std::string& group) const;
+ std::vector<std::string>
+ groups() const;
- private:
+ const HelpGroupDetails&
+ group_help(const std::string& group) const;
- void
- add_one_option
- (
- const std::string& option,
- const std::shared_ptr<OptionDetails>& details
- );
+ const std::string& program() const
+ {
+ return m_program;
+ }
+
+ private:
+
+ void
+ add_one_option
+ (
+ const std::string& option,
+ const std::shared_ptr<OptionDetails>& details
+ );
+
+ String
+ help_one_group(const std::string& group) const;
- String
- help_one_group(const std::string& group) const;
+ void
+ generate_group_help
+ (
+ String& result,
+ const std::vector<std::string>& groups
+ ) const;
+
+ 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::size_t m_width;
+ bool m_tab_expansion;
+
+ std::shared_ptr<OptionMap> m_options;
+ std::vector<std::string> m_positional{};
+ std::unordered_set<std::string> m_positional_set{};
+
+ //mapping from groups to help options
+ std::map<std::string, HelpGroupDetails> m_help{};
+};
+
+class OptionAdder
+{
+ public:
- void
- generate_group_help
- (
- String& result,
- const std::vector<std::string>& groups
- ) const;
+ OptionAdder(Options& options, std::string group)
+ : m_options(options), m_group(std::move(group))
+ {
+ }
- void
- generate_all_groups_help(String& result) const;
+ OptionAdder&
+ operator()
+ (
+ const std::string& opts,
+ const std::string& desc,
+ const std::shared_ptr<const Value>& value
+ = ::cxxopts::value<bool>(),
+ std::string 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;
+ private:
+ Options& m_options;
+ std::string m_group;
+};
- std::shared_ptr<OptionMap> m_options;
- std::vector<std::string> m_positional{};
- std::unordered_set<std::string> m_positional_set{};
+namespace {
+constexpr std::size_t OPTION_LONGEST = 30;
+constexpr std::size_t OPTION_DESC_GAP = 2;
- //mapping from groups to help options
- std::map<std::string, HelpGroupDetails> m_help{};
+String
+format_option
+(
+ const HelpOptionDetails& o
+)
+{
+ const auto& s = o.s;
+ const auto& l = first_or_empty(o.l);
- std::list<OptionDetails> m_option_list{};
- std::unordered_map<std::string, decltype(m_option_list)::iterator> m_option_map{};
- };
+ String result = " ";
- class OptionAdder
+ if (!s.empty())
{
- public:
-
- OptionAdder(Options& options, std::string group)
- : m_options(options), m_group(std::move(group))
+ result += "-" + toLocalString(s);
+ if (!l.empty())
{
+ result += ",";
}
+ }
+ else
+ {
+ result += " ";
+ }
- OptionAdder&
- operator()
- (
- const std::string& opts,
- const std::string& desc,
- const std::shared_ptr<const Value>& value
- = ::cxxopts::value<bool>(),
- std::string arg_help = ""
- );
+ if (!l.empty())
+ {
+ result += " --" + toLocalString(l);
+ }
- private:
- Options& m_options;
- std::string m_group;
- };
+ auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
- namespace
+ if (!o.is_boolean)
{
- constexpr int OPTION_LONGEST = 30;
- constexpr int OPTION_DESC_GAP = 2;
+ if (o.has_implicit)
+ {
+ result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
+ }
+ else
+ {
+ result += " " + arg;
+ }
+ }
- std::basic_regex<char> option_matcher
- ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
+ return result;
+}
- std::basic_regex<char> option_specifier
- ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
+String
+format_description
+(
+ const HelpOptionDetails& o,
+ std::size_t start,
+ std::size_t allowed,
+ bool tab_expansion
+)
+{
+ auto desc = o.desc;
- String
- format_option
- (
- const HelpOptionDetails& o
- )
+ if (o.has_default && (!o.is_boolean || o.default_value != "false"))
+ {
+ if(!o.default_value.empty())
+ {
+ desc += toLocalString(" (default: " + o.default_value + ")");
+ }
+ else
{
- const auto& s = o.s;
- const auto& l = o.l;
+ desc += toLocalString(" (default: \"\")");
+ }
+ }
- String result = " ";
+ String result;
- if (!s.empty())
+ if (tab_expansion)
+ {
+ String desc2;
+ auto size = std::size_t{ 0 };
+ for (auto c = std::begin(desc); c != std::end(desc); ++c)
+ {
+ if (*c == '\n')
{
- result += "-" + toLocalString(s);
- if (!l.empty())
- {
- result += ",";
- }
+ desc2 += *c;
+ size = 0;
}
- else
+ else if (*c == '\t')
{
- result += " ";
+ auto skip = 8 - size % 8;
+ stringAppend(desc2, skip, ' ');
+ size += skip;
}
-
- if (!l.empty())
+ else
{
- result += " --" + toLocalString(l);
+ desc2 += *c;
+ ++size;
}
+ }
+ desc = desc2;
+ }
- auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
+ desc += " ";
- if (!o.is_boolean)
- {
- if (o.has_implicit)
- {
- result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
- }
- else
- {
- result += " " + arg;
- }
- }
+ auto current = std::begin(desc);
+ auto previous = current;
+ auto startLine = current;
+ auto lastSpace = current;
+
+ auto size = std::size_t{};
+
+ bool appendNewLine;
+ bool onlyWhiteSpace = true;
- return result;
+ while (current != std::end(desc))
+ {
+ appendNewLine = false;
+ if (*previous == ' ' || *previous == '\t')
+ {
+ lastSpace = current;
+ }
+ if (*current != ' ' && *current != '\t')
+ {
+ onlyWhiteSpace = false;
}
- String
- format_description
- (
- const HelpOptionDetails& o,
- size_t start,
- size_t width
- )
+ while (*current == '\n')
{
- auto desc = o.desc;
+ previous = current;
+ ++current;
+ appendNewLine = true;
+ }
- if (o.has_default && (!o.is_boolean || o.default_value != "false"))
+ if (!appendNewLine && size >= allowed)
+ {
+ if (lastSpace != startLine)
{
- if(!o.default_value.empty())
- {
- desc += toLocalString(" (default: " + o.default_value + ")");
- }
- else
- {
- desc += toLocalString(" (default: \"\")");
- }
+ current = lastSpace;
+ previous = current;
}
+ appendNewLine = true;
+ }
- String result;
+ if (appendNewLine)
+ {
+ stringAppend(result, startLine, current);
+ startLine = current;
+ lastSpace = current;
- auto current = std::begin(desc);
- auto startLine = current;
- auto lastSpace = current;
+ if (*previous != '\n')
+ {
+ stringAppend(result, "\n");
+ }
- auto size = size_t{};
+ stringAppend(result, start, ' ');
- while (current != std::end(desc))
+ if (*previous != '\n')
{
- if (*current == ' ')
- {
- lastSpace = current;
- }
+ stringAppend(result, lastSpace, current);
+ }
- 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;
- }
+ onlyWhiteSpace = true;
+ size = 0;
+ }
- ++current;
- }
+ previous = current;
+ ++current;
+ ++size;
+ }
- //append whatever is left
- stringAppend(result, startLine, current);
+ //append whatever is left but ignore whitespace
+ if (!onlyWhiteSpace)
+ {
+ stringAppend(result, startLine, previous);
+ }
- return result;
- }
- } // namespace
+ return result;
+}
+
+} // namespace
inline
void
@@ -1711,43 +2220,31 @@ OptionAdder::operator()
std::string arg_help
)
{
- std::match_results<const char*> result;
- std::regex_match(opts.c_str(), result, option_specifier);
-
- if (result.empty())
- {
- throw_or_mimic<invalid_option_format_error>(opts);
- }
-
- const auto& short_match = result[2];
- const auto& long_match = result[3];
-
- 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);
- }
-
- auto option_names = []
- (
- const std::sub_match<const char*>& short_,
- const std::sub_match<const char*>& long_
- )
- {
- if (long_.length() == 1)
- {
- return std::make_tuple(long_.str(), short_.str());
- }
- return std::make_tuple(short_.str(), long_.str());
- }(short_match, long_match);
+ OptionNames option_names = values::parser_tool::split_option_names(opts);
+ // Note: All names will be non-empty; but we must separate the short
+ // (length-1) and longer names
+ std::string short_name {""};
+ auto first_short_name_iter =
+ std::partition(option_names.begin(), option_names.end(),
+ [&](const std::string& name) { return name.length() > 1; }
+ );
+ auto num_length_1_names = (option_names.end() - first_short_name_iter);
+ switch(num_length_1_names) {
+ case 1:
+ short_name = *first_short_name_iter;
+ option_names.erase(first_short_name_iter);
+ break;
+ case 0:
+ break;
+ default:
+ throw_or_mimic<exceptions::invalid_option_format>(opts);
+ };
m_options.add_option
(
m_group,
- std::get<0>(option_names),
- std::get<1>(option_names),
+ short_name,
+ option_names,
desc,
value,
std::move(arg_help)
@@ -1763,6 +2260,15 @@ OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
// TODO: remove the duplicate code here
auto& store = m_parsed[details->hash()];
store.parse_default(details);
+ m_defaults.emplace_back(details->essential_name(), details->value().get_default_value());
+}
+
+inline
+void
+OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details)
+{
+ auto& store = m_parsed[details->hash()];
+ store.parse_no_value(details);
}
inline
@@ -1778,7 +2284,7 @@ OptionParser::parse_option
auto& result = m_parsed[hash];
result.parse(value, arg);
- m_sequential.emplace_back(value->long_name(), arg);
+ m_sequential.emplace_back(value->essential_name(), arg);
}
inline
@@ -1800,7 +2306,7 @@ OptionParser::checked_parse_arg
}
else
{
- throw_or_mimic<missing_argument_exception>(name);
+ throw_or_mimic<exceptions::missing_argument>(name);
}
}
else
@@ -1833,9 +2339,9 @@ OptionParser::consume_positional(const std::string& a, PositionalListIterator& n
auto iter = m_options.find(*next);
if (iter != m_options.end())
{
- auto& result = m_parsed[iter->second->hash()];
if (!iter->second->value().is_container())
{
+ auto& result = m_parsed[iter->second->hash()];
if (result.count() == 0)
{
add_to_option(iter, *next, a);
@@ -1848,7 +2354,7 @@ OptionParser::consume_positional(const std::string& a, PositionalListIterator& n
add_to_option(iter, *next, a);
return true;
}
- throw_or_mimic<option_not_exists_exception>(*next);
+ throw_or_mimic<exceptions::no_such_option>(*next);
}
return false;
@@ -1891,7 +2397,7 @@ OptionParser::parse(int argc, const char* const* argv)
{
int current = 1;
bool consume_remaining = false;
- PositionalListIterator next_positional = m_positional.begin();
+ auto next_positional = m_positional.begin();
std::vector<std::string> unmatched;
@@ -1903,18 +2409,18 @@ OptionParser::parse(int argc, const char* const* argv)
++current;
break;
}
+ bool matched = false;
+ values::parser_tool::ArguDesc argu_desc =
+ values::parser_tool::ParseArgument(argv[current], matched);
- std::match_results<const char*> result;
- std::regex_match(argv[current], result, option_matcher);
-
- if (result.empty())
+ if (!matched)
{
//not a flag
// 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]);
+ throw_or_mimic<exceptions::invalid_option_syntax>(argv[current]);
}
}
@@ -1925,16 +2431,16 @@ OptionParser::parse(int argc, const char* const* argv)
}
else
{
- unmatched.push_back(argv[current]);
+ unmatched.emplace_back(argv[current]);
}
//if we return from here then it was parsed successfully, so continue
}
else
{
//short or long option?
- if (result[4].length() != 0)
+ if (argu_desc.grouping)
{
- const std::string& s = result[4];
+ const std::string& s = argu_desc.arg_name;
for (std::size_t i = 0; i != s.size(); ++i)
{
@@ -1945,10 +2451,11 @@ OptionParser::parse(int argc, const char* const* argv)
{
if (m_allow_unrecognised)
{
+ unmatched.push_back(std::string("-") + s[i]);
continue;
}
//error
- throw_or_mimic<option_not_exists_exception>(name);
+ throw_or_mimic<exceptions::no_such_option>(name);
}
auto value = iter->second;
@@ -1962,16 +2469,22 @@ OptionParser::parse(int argc, const char* const* argv)
{
parse_option(value, name, value->value().get_implicit_value());
}
+ else if (i + 1 < s.size())
+ {
+ std::string arg_value = s.substr(i + 1);
+ parse_option(value, name, arg_value);
+ break;
+ }
else
{
//error
- throw_or_mimic<option_requires_argument_exception>(name);
+ throw_or_mimic<exceptions::option_requires_argument>(name);
}
}
}
- else if (result[1].length() != 0)
+ else if (argu_desc.arg_name.length() != 0)
{
- const std::string& name = result[1];
+ const std::string& name = argu_desc.arg_name;
auto iter = m_options.find(name);
@@ -1980,22 +2493,22 @@ OptionParser::parse(int argc, const char* const* argv)
if (m_allow_unrecognised)
{
// keep unrecognised options in argument list, skip to next argument
- unmatched.push_back(argv[current]);
+ unmatched.emplace_back(argv[current]);
++current;
continue;
}
//error
- throw_or_mimic<option_not_exists_exception>(name);
+ throw_or_mimic<exceptions::no_such_option>(name);
}
auto opt = iter->second;
//equals provided for long option?
- if (result[2].length() != 0)
+ if (argu_desc.set_value)
{
//parse the option given
- parse_option(opt, name, result[3]);
+ parse_option(opt, name, argu_desc.value);
}
else
{
@@ -2016,8 +2529,13 @@ OptionParser::parse(int argc, const char* const* argv)
auto& store = m_parsed[detail->hash()];
- if(value.has_default() && !store.count() && !store.has_default()){
- parse_default(detail);
+ if (value.has_default()) {
+ if (!store.count() && !store.has_default()) {
+ parse_default(detail);
+ }
+ }
+ else {
+ parse_no_value(detail);
}
}
@@ -2033,14 +2551,14 @@ OptionParser::parse(int argc, const char* const* argv)
//adjust argv for any that couldn't be swallowed
while (current != argc) {
- unmatched.push_back(argv[current]);
+ unmatched.emplace_back(argv[current]);
++current;
}
}
finalise_aliases();
- ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(unmatched));
+ ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(m_defaults), std::move(unmatched));
return parsed;
}
@@ -2053,7 +2571,9 @@ OptionParser::finalise_aliases()
auto& detail = *option.second;
auto hash = detail.hash();
m_keys[detail.short_name()] = hash;
- m_keys[detail.long_name()] = hash;
+ for(const auto& long_name : detail.long_names()) {
+ m_keys[long_name] = hash;
+ }
m_parsed.emplace(hash, OptionValue());
}
@@ -2076,7 +2596,7 @@ Options::add_option
(
const std::string& group,
const std::string& s,
- const std::string& l,
+ const OptionNames& l,
std::string desc,
const std::shared_ptr<const Value>& value,
std::string arg_help
@@ -2090,16 +2610,10 @@ Options::add_option
add_one_option(s, option);
}
- if (!l.empty())
- {
- add_one_option(l, option);
+ for(const auto& long_name : l) {
+ add_one_option(long_name, option);
}
- m_option_list.push_front(*option.get());
- auto iter = m_option_list.begin();
- m_option_map[s] = iter;
- m_option_map[l] = iter;
-
//add the help details
auto& options = m_help[group];
@@ -2123,7 +2637,7 @@ Options::add_one_option
if (!in.second)
{
- throw_or_mimic<option_exists_error>(option);
+ throw_or_mimic<exceptions::option_already_exists>(option);
}
}
@@ -2141,7 +2655,7 @@ Options::help_one_group(const std::string& g) const
OptionHelp format;
- size_t longest = 0;
+ std::size_t longest = 0;
String result;
@@ -2152,7 +2666,8 @@ Options::help_one_group(const std::string& g) const
for (const auto& o : group->second.options)
{
- if (m_positional_set.find(o.l) != m_positional_set.end() &&
+ if (o.l.size() &&
+ m_positional_set.find(o.l.front()) != m_positional_set.end() &&
!m_show_positional)
{
continue;
@@ -2162,22 +2677,26 @@ Options::help_one_group(const std::string& g) const
longest = (std::max)(longest, stringLength(s));
format.push_back(std::make_pair(s, String()));
}
+ longest = (std::min)(longest, OPTION_LONGEST);
- longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));
-
- //widest allowed description
- auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
+ //widest allowed description -- min 10 chars for helptext/line
+ std::size_t allowed = 10;
+ if (m_width > allowed + longest + OPTION_DESC_GAP)
+ {
+ allowed = m_width - 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() &&
+ if (o.l.size() &&
+ m_positional_set.find(o.l.front()) != m_positional_set.end() &&
!m_show_positional)
{
continue;
}
- auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
+ auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
result += fiter->first;
if (stringLength(fiter->first) > longest)
@@ -2208,7 +2727,7 @@ Options::generate_group_help
const std::vector<std::string>& print_groups
) const
{
- for (size_t i = 0; i != print_groups.size(); ++i)
+ for (std::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))
@@ -2228,22 +2747,34 @@ void
Options::generate_all_groups_help(String& result) const
{
std::vector<std::string> all_groups;
- all_groups.reserve(m_help.size());
- for (const auto& group : m_help)
- {
- all_groups.push_back(group.first);
- }
+ std::transform(
+ m_help.begin(),
+ m_help.end(),
+ std::back_inserter(all_groups),
+ [] (const std::map<std::string, HelpGroupDetails>::value_type& group)
+ {
+ return group.first;
+ }
+ );
generate_group_help(result, all_groups);
}
inline
std::string
-Options::help(const std::vector<std::string>& help_groups) const
+Options::help(const std::vector<std::string>& help_groups, bool print_usage) const
{
- String result = m_help_string + "\nUsage:\n " +
- toLocalString(m_program) + " " + toLocalString(m_custom_help);
+ String result = m_help_string;
+ if(print_usage)
+ {
+ result+= "\nUsage:\n " + toLocalString(m_program);
+ }
+
+ if (!m_custom_help.empty())
+ {
+ result += " " + toLocalString(m_custom_help);
+ }
if (!m_positional.empty() && !m_positional_help.empty()) {
result += " " + toLocalString(m_positional_help);