#include <exception>
#include <iostream>
#include <limits>
+#include <list>
#include <map>
#include <memory>
#include <regex>
#define CXXOPTS_HAS_OPTIONAL
#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 2
-#define CXXOPTS__VERSION_MINOR 2
+#define CXXOPTS__VERSION_MAJOR 3
+#define CXXOPTS__VERSION_MINOR 0
#define CXXOPTS__VERSION_PATCH 0
namespace cxxopts
inline
String&
- stringAppend(String& s, int n, UChar32 c)
+ stringAppend(String& s, size_t n, UChar32 c)
{
- for (int i = 0; i != n; ++i)
+ for (size_t i = 0; i != n; ++i)
{
s.append(c);
}
#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>
{
public:
virtual bool
is_boolean() const = 0;
};
-
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
class OptionException : public std::exception
{
public:
{
}
+ CXXOPTS_NODISCARD
const char*
what() const noexcept override
{
}
};
+ 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:
} // namespace detail
template <typename R, typename T>
- R
- checked_negate(T&& t, const std::string&, std::true_type)
+ 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.
- return static_cast<R>(-static_cast<R>(t-1)-1);
+ r = static_cast<R>(-static_cast<R>(t-1)-1);
}
template <typename R, typename T>
- T
- checked_negate(T&& t, const std::string& text, std::false_type)
+ void
+ checked_negate(R&, T&&, const std::string& text, std::false_type)
{
throw_or_mimic<argument_incorrect_type>(text);
- return t;
}
template <typename T>
if (negative)
{
- value = checked_negate<T>(result,
- text,
- std::integral_constant<bool, is_signed>());
+ checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
}
else
{
~abstract_value() override = default;
+ abstract_value& operator=(const abstract_value&) = default;
+
abstract_value(const abstract_value& rhs)
{
if (rhs.m_result)
}
protected:
- std::shared_ptr<T> m_result;
- T* m_store;
+ std::shared_ptr<T> m_result{};
+ T* m_store{};
bool m_default = false;
bool m_implicit = false;
- std::string m_default_value;
- std::string m_implicit_value;
+ std::string m_default_value{};
+ std::string m_implicit_value{};
};
template <typename T>
public:
using abstract_value<T>::abstract_value;
+ CXXOPTS_NODISCARD
std::shared_ptr<Value>
clone() const
{
, m_value(std::move(val))
, m_count(0)
{
+ m_hash = std::hash<std::string>{}(m_long + m_short);
}
OptionDetails(const OptionDetails& rhs)
OptionDetails(OptionDetails&& rhs) = default;
+ CXXOPTS_NODISCARD
const String&
description() const
{
return m_desc;
}
- const Value& value() const {
+ CXXOPTS_NODISCARD
+ const Value&
+ value() const {
return *m_value;
}
+ CXXOPTS_NODISCARD
std::shared_ptr<Value>
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<const Value> m_value;
+ std::string m_short{};
+ std::string m_long{};
+ String m_desc{};
+ std::shared_ptr<const Value> m_value{};
int m_count;
+
+ size_t m_hash{};
};
struct HelpOptionDetails
struct HelpGroupDetails
{
- std::string name;
- std::string description;
- std::vector<HelpOptionDetails> options;
+ std::string name{};
+ std::string description{};
+ std::vector<HelpOptionDetails> options{};
};
class OptionValue
ensure_value(details);
++m_count;
m_value->parse(text);
+ m_long_name = &details->long_name();
}
void
{
ensure_value(details);
m_default = true;
+ m_long_name = &details->long_name();
m_value->parse();
}
+ CXXOPTS_NODISCARD
size_t
count() const noexcept
{
}
// TODO: maybe default options should count towards the number of arguments
+ CXXOPTS_NODISCARD
bool
has_default() const noexcept
{
as() const
{
if (m_value == nullptr) {
- throw_or_mimic<std::domain_error>("No value");
+ throw_or_mimic<option_has_no_value_exception>(
+ m_long_name == nullptr ? "" : *m_long_name);
}
#ifdef CXXOPTS_NO_RTTI
}
}
- std::shared_ptr<Value> m_value;
+
+ 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;
};
{
}
- const
- std::string&
+ CXXOPTS_NODISCARD
+ const std::string&
key() const
{
return m_key;
}
- const
- std::string&
+ CXXOPTS_NODISCARD
+ const std::string&
value() const
{
return m_value;
std::string m_value;
};
+ using ParsedHashMap = std::unordered_map<size_t, OptionValue>;
+ using NameHashMap = std::unordered_map<std::string, size_t>;
+
class ParseResult
{
public:
- ParseResult(
- std::shared_ptr<
- std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
- >,
- std::vector<std::string>,
- bool allow_unrecognised,
- int&, char**&);
+ ParseResult() {}
+
+ ParseResult(const ParseResult&) = default;
+
+ 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))
+ {
+ }
+
+ ParseResult& operator=(ParseResult&&) = default;
+ ParseResult& operator=(const ParseResult&) = default;
size_t
count(const std::string& o) const
{
- auto iter = m_options->find(o);
- if (iter == m_options->end())
+ auto iter = m_keys.find(o);
+ if (iter == m_keys.end())
{
return 0;
}
- auto riter = m_results.find(iter->second);
+ auto viter = m_values.find(iter->second);
+
+ if (viter == m_values.end())
+ {
+ return 0;
+ }
- return riter->second.count();
+ return viter->second.count();
}
const OptionValue&
operator[](const std::string& option) const
{
- auto iter = m_options->find(option);
+ auto iter = m_keys.find(option);
- if (iter == m_options->end())
+ if (iter == m_keys.end())
{
throw_or_mimic<option_not_present_exception>(option);
}
- auto riter = m_results.find(iter->second);
+ auto viter = m_values.find(iter->second);
- return riter->second;
+ if (viter == m_values.end())
+ {
+ throw_or_mimic<option_not_present_exception>(option);
+ }
+
+ return viter->second;
}
const std::vector<KeyValue>&
return m_sequential;
}
- private:
-
- void
- parse(int& argc, char**& argv);
-
- void
- add_to_option(const std::string& option, const std::string& arg);
-
- bool
- consume_positional(const std::string& a);
-
- 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
- checked_parse_arg
- (
- int argc,
- char* argv[],
- int& current,
- const std::shared_ptr<OptionDetails>& value,
- const std::string& name
- );
-
- const std::shared_ptr<
- std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
- > m_options;
- std::vector<std::string> m_positional;
- std::vector<std::string>::iterator m_next_positional;
- std::unordered_set<std::string> m_positional_set;
- std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;
-
- bool m_allow_unrecognised;
+ const std::vector<std::string>&
+ unmatched() const
+ {
+ return m_unmatched;
+ }
- std::vector<KeyValue> m_sequential;
+ private:
+ NameHashMap m_keys{};
+ ParsedHashMap m_values{};
+ std::vector<KeyValue> m_sequential{};
+ std::vector<std::string> m_unmatched{};
};
struct Option
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;
+
+ 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<OptionDetails>& 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<OptionDetails>& value,
+ const std::string& name,
+ const std::string& arg = ""
+ );
+
+ void
+ parse_default(const std::shared_ptr<OptionDetails>& details);
+
+ private:
+
+ void finalise_aliases();
+
+ const OptionMap& m_options;
+ const PositionalList& m_positional;
+
+ std::vector<KeyValue> m_sequential{};
+ bool m_allow_unrecognised;
+
+ ParsedHashMap m_parsed{};
+ NameHashMap m_keys{};
+ };
+
class Options
{
- using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
public:
explicit Options(std::string program, std::string help_string = "")
, m_show_positional(false)
, m_allow_unrecognised(false)
, m_options(std::make_shared<OptionMap>())
- , m_next_positional(m_positional.end())
{
}
}
ParseResult
- parse(int& argc, char**& argv);
+ parse(int argc, const char* const* argv);
OptionAdder
add_options(std::string group = "");
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;
+ std::string m_program{};
+ String m_help_string{};
+ std::string m_custom_help{};
+ std::string m_positional_help{};
bool m_show_positional;
bool m_allow_unrecognised;
std::shared_ptr<OptionMap> m_options;
- std::vector<std::string> m_positional;
- std::vector<std::string>::iterator m_next_positional;
- std::unordered_set<std::string> m_positional_set;
+ 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;
+ std::map<std::string, HelpGroupDetails> m_help{};
+
+ std::list<OptionDetails> m_option_list{};
+ std::unordered_map<std::string, decltype(m_option_list)::iterator> m_option_map{};
};
class OptionAdder
} // namespace
inline
-ParseResult::ParseResult
-(
- std::shared_ptr<
- std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
- > options,
- std::vector<std::string> positional,
- bool allow_unrecognised,
- int& argc, char**& argv
-)
-: m_options(std::move(options))
-, m_positional(std::move(positional))
-, m_next_positional(m_positional.begin())
-, m_allow_unrecognised(allow_unrecognised)
-{
- parse(argc, argv);
-}
-
-inline
void
Options::add_options
(
inline
void
-ParseResult::parse_default(const std::shared_ptr<OptionDetails>& details)
+OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
{
- m_results[details].parse_default(details);
+ // TODO: remove the duplicate code here
+ auto& store = m_parsed[details->hash()];
+ store.parse_default(details);
}
inline
void
-ParseResult::parse_option
+OptionParser::parse_option
(
const std::shared_ptr<OptionDetails>& value,
const std::string& /*name*/,
const std::string& arg
)
{
- auto& result = m_results[value];
+ auto hash = value->hash();
+ auto& result = m_parsed[hash];
result.parse(value, arg);
m_sequential.emplace_back(value->long_name(), arg);
inline
void
-ParseResult::checked_parse_arg
+OptionParser::checked_parse_arg
(
int argc,
- char* argv[],
+ const char* const* argv,
int& current,
const std::shared_ptr<OptionDetails>& value,
const std::string& name
inline
void
-ParseResult::add_to_option(const std::string& option, const std::string& arg)
+OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
{
- auto iter = m_options->find(option);
-
- if (iter == m_options->end())
- {
- throw_or_mimic<option_not_exists_exception>(option);
- }
-
parse_option(iter->second, option, arg);
}
inline
bool
-ParseResult::consume_positional(const std::string& a)
+OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
{
- while (m_next_positional != m_positional.end())
+ while (next != m_positional.end())
{
- auto iter = m_options->find(*m_next_positional);
- if (iter != m_options->end())
+ auto iter = m_options.find(*next);
+ if (iter != m_options.end())
{
- auto& result = m_results[iter->second];
+ auto& result = m_parsed[iter->second->hash()];
if (!iter->second->value().is_container())
{
if (result.count() == 0)
{
- add_to_option(*m_next_positional, a);
- ++m_next_positional;
+ add_to_option(iter, *next, a);
+ ++next;
return true;
}
- ++m_next_positional;
+ ++next;
continue;
}
- add_to_option(*m_next_positional, a);
+ add_to_option(iter, *next, a);
return true;
}
- throw_or_mimic<option_not_exists_exception>(*m_next_positional);
+ throw_or_mimic<option_not_exists_exception>(*next);
}
return false;
Options::parse_positional(std::vector<std::string> options)
{
m_positional = std::move(options);
- m_next_positional = m_positional.begin();
m_positional_set.insert(m_positional.begin(), m_positional.end());
}
inline
ParseResult
-Options::parse(int& argc, char**& argv)
+Options::parse(int argc, const char* const* argv)
{
- ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
- return result;
+ OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
+
+ return parser.parse(argc, argv);
}
-inline
-void
-ParseResult::parse(int& argc, char**& argv)
+inline ParseResult
+OptionParser::parse(int argc, const char* const* argv)
{
int current = 1;
-
- int nextKeep = 1;
-
bool consume_remaining = false;
+ PositionalListIterator next_positional = m_positional.begin();
+
+ std::vector<std::string> unmatched;
while (current != argc)
{
//if true is returned here then it was consumed, otherwise it is
//ignored
- if (consume_positional(argv[current]))
+ if (consume_positional(argv[current], next_positional))
{
}
else
{
- argv[nextKeep] = argv[current];
- ++nextKeep;
+ unmatched.push_back(argv[current]);
}
//if we return from here then it was parsed successfully, so continue
}
for (std::size_t i = 0; i != s.size(); ++i)
{
std::string name(1, s[i]);
- auto iter = m_options->find(name);
+ auto iter = m_options.find(name);
- if (iter == m_options->end())
+ if (iter == m_options.end())
{
if (m_allow_unrecognised)
{
{
const std::string& name = result[1];
- auto iter = m_options->find(name);
+ auto iter = m_options.find(name);
- if (iter == m_options->end())
+ if (iter == m_options.end())
{
if (m_allow_unrecognised)
{
// keep unrecognised options in argument list, skip to next argument
- argv[nextKeep] = argv[current];
- ++nextKeep;
+ unmatched.push_back(argv[current]);
++current;
continue;
}
++current;
}
- for (auto& opt : *m_options)
+ for (auto& opt : m_options)
{
auto& detail = opt.second;
const auto& value = detail->value();
- auto& store = m_results[detail];
+ auto& store = m_parsed[detail->hash()];
if(value.has_default() && !store.count() && !store.has_default()){
parse_default(detail);
{
while (current < argc)
{
- if (!consume_positional(argv[current])) {
+ if (!consume_positional(argv[current], next_positional)) {
break;
}
++current;
//adjust argv for any that couldn't be swallowed
while (current != argc) {
- argv[nextKeep] = argv[current];
- ++nextKeep;
+ unmatched.push_back(argv[current]);
++current;
}
}
- argc = nextKeep;
+ finalise_aliases();
+ ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(unmatched));
+ return parsed;
+}
+
+inline
+void
+OptionParser::finalise_aliases()
+{
+ for (auto& option: m_options)
+ {
+ auto& detail = *option.second;
+ auto hash = detail.hash();
+ m_keys[detail.short_name()] = hash;
+ m_keys[detail.long_name()] = hash;
+
+ m_parsed.emplace(hash, OptionValue());
+ }
}
inline
add_one_option(l, 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];