Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/jarro2783/cxxopts.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarryd Beck <jarro.2783@gmail.com>2017-11-15 10:04:20 +0300
committerJarryd Beck <jarro.2783@gmail.com>2017-11-15 10:04:20 +0300
commit8893afe13cc47dd0be4f25b5ae491e652c146098 (patch)
tree6c2c56bf4940eee0aec1ce397d9225a4f6756241
parentd7b930846cdccfc8bcecc4d7150ddcbadffac360 (diff)
parent70b9230639cad36c54d19aae37ae890e44939cdf (diff)
Merge branch '2_0'v2.0.0
-rw-r--r--CHANGELOG.md29
-rw-r--r--README.md19
-rw-r--r--include/cxxopts.hpp648
-rw-r--r--src/example.cpp36
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/link_a.cpp6
-rw-r--r--test/link_b.cpp1
-rw-r--r--test/options.cpp174
8 files changed, 621 insertions, 295 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..8b4ba45
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,29 @@
+# Changelog
+
+This is the changelog for `cxxopts`, a C++11 library for parsing command line
+options. The project adheres to semantic versioning.
+
+## 2.0
+
+### Changed
+
+* `Options::parse` returns a ParseResult rather than storing the parse
+ result internally.
+* Options with default values now get counted as appearing once if they
+ were not specified by the user.
+
+### Added
+
+* A new `ParseResult` object that is the immutable result of parsing. It
+ responds to the same `count` and `operator[]` as `Options` of 1.x did.
+* The function `ParseResult::arguments` returns a vector of the parsed
+ arguments to iterate through in the order they were provided.
+* The symbol `cxxopts::version` for the version of the library.
+* Booleans can be specified with various strings and explicitly set false.
+
+## 1.x
+
+The 1.x series was the first major version of the library, with release numbers
+starting to follow semantic versioning, after 0.x being unstable. It never had
+a changelog maintained for it. Releases mostly contained bug fixes, with the
+occasional feature added.
diff --git a/README.md b/README.md
index 355417e..cd869b8 100644
--- a/README.md
+++ b/README.md
@@ -39,12 +39,12 @@ Any type can be given as long as it can be parsed, with operator>>.
To parse the command line do:
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
-To retrieve an option use `options.count("option")` to get the number of times
+To retrieve an option use `result.count("option")` to get the number of times
it appeared, and
- options["opt"].as<type>()
+ result["opt"].as<type>()
to get its value. If "opt" doesn't exist, or isn't of the right type, then an
exception will be thrown.
@@ -84,6 +84,17 @@ If an option had both, then not specifying it would give the value `"value"`,
writing it on the command line as `--option` would give the value `"implicit"`,
and writing `--option=another` would give it the value `"another"`.
+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.
+
+## Boolean values
+
+Boolean options have a default implicit value of `"true"`, which can be
+overridden. The effect is that writing `-o` by itself will set option `o` to
+`true`. However, they can also be written with various strings using either
+`=value` or the next argument.
+
# Linking
This is a header only library.
@@ -93,9 +104,7 @@ This is a header only library.
The only build requirement is a C++ compiler that supports C++11 regular
expressions. For example GCC >= 4.9 or clang with libc++.
-
# TODO list
* Allow unrecognised options.
* Various help strings.
-* Unicode aware for help strings.
diff --git a/include/cxxopts.hpp b/include/cxxopts.hpp
index 17bb590..3117e9d 100644
--- a/include/cxxopts.hpp
+++ b/include/cxxopts.hpp
@@ -22,13 +22,8 @@ THE SOFTWARE.
*/
-#ifndef CXX_OPTS_HPP
-#define CXX_OPTS_HPP
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
-#endif
+#ifndef CXXOPTS_HPP_INCLUDED
+#define CXXOPTS_HPP_INCLUDED
#include <cstring>
#include <cctype>
@@ -39,9 +34,17 @@ THE SOFTWARE.
#include <regex>
#include <sstream>
#include <string>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
+namespace cxxopts
+{
+ static constexpr struct {
+ uint8_t major, minor, patch;
+ } version = {2, 0, 0};
+}
+
//when we ask cxxopts to use Unicode, help strings are processed using ICU,
//which results in the correct lengths being computed for strings when they
//are formatted for the help output
@@ -168,12 +171,14 @@ 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)
{
@@ -258,6 +263,12 @@ namespace cxxopts
{
public:
+ virtual ~Value() = default;
+
+ virtual
+ std::shared_ptr<Value>
+ clone() const = 0;
+
virtual void
parse(const std::string& text) const = 0;
@@ -265,9 +276,6 @@ namespace cxxopts
parse() const = 0;
virtual bool
- has_arg() const = 0;
-
- virtual bool
has_default() const = 0;
virtual bool
@@ -287,6 +295,9 @@ namespace cxxopts
virtual std::shared_ptr<Value>
implicit_value(const std::string& value) = 0;
+
+ virtual bool
+ is_boolean() const = 0;
};
class OptionException : public std::exception
@@ -385,7 +396,7 @@ namespace cxxopts
)
: OptionParseException(
u8"Option " + LQUOTE + option + RQUOTE +
- u8" does not take an argument, but argument" +
+ u8" does not take an argument, but argument " +
LQUOTE + arg + RQUOTE + " given"
)
{
@@ -432,6 +443,10 @@ namespace cxxopts
{
std::basic_regex<char> integer_pattern
("(-)?(0x)?([1-9a-zA-Z][0-9a-zA-Z]*)|((0x)?0)");
+ std::basic_regex<char> truthy_pattern
+ ("t|true|T|True");
+ std::basic_regex<char> falsy_pattern
+ ("(f|false|F|False)?");
}
namespace detail
@@ -561,11 +576,6 @@ namespace cxxopts
value = checked_negate<T>(result,
text,
std::integral_constant<bool, is_signed>());
- //if (!is_signed)
- //{
- // throw argument_incorrect_type(text);
- //}
- //value = -result;
}
else
{
@@ -641,11 +651,25 @@ namespace cxxopts
inline
void
- parse_value(const std::string& /*text*/, bool& value)
+ parse_value(const std::string& text, bool& value)
{
- //TODO recognise on, off, yes, no, enable, disable
- //so that we can write --long=yes explicitly
- value = true;
+ std::smatch result;
+ std::regex_match(text, result, truthy_pattern);
+
+ if (!result.empty())
+ {
+ value = true;
+ return;
+ }
+
+ std::regex_match(text, result, falsy_pattern);
+ if (!result.empty())
+ {
+ value = false;
+ return;
+ }
+
+ throw argument_incorrect_type(text);
}
inline
@@ -674,18 +698,6 @@ namespace cxxopts
}
template <typename T>
- struct value_has_arg
- {
- static constexpr bool value = true;
- };
-
- template <>
- struct value_has_arg<bool>
- {
- static constexpr bool value = false;
- };
-
- template <typename T>
struct type_is_container
{
static constexpr bool value = false;
@@ -698,20 +710,42 @@ namespace cxxopts
};
template <typename T>
- class standard_value final : public Value
+ class abstract_value : public Value
{
+ using Self = abstract_value<T>;
+
public:
- standard_value()
+ abstract_value()
: m_result(std::make_shared<T>())
, m_store(m_result.get())
{
}
- standard_value(T* t)
+ abstract_value(T* t)
: m_store(t)
{
}
+ virtual ~abstract_value() = default;
+
+ 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;
+ }
+
+ m_default = rhs.m_default;
+ m_implicit = rhs.m_implicit;
+ m_default_value = rhs.m_default_value;
+ m_implicit_value = rhs.m_implicit_value;
+ }
+
void
parse(const std::string& text) const
{
@@ -731,12 +765,6 @@ namespace cxxopts
}
bool
- has_arg() const
- {
- return value_has_arg<T>::value;
- }
-
- bool
has_default() const
{
return m_default;
@@ -748,15 +776,17 @@ namespace cxxopts
return m_implicit;
}
- virtual std::shared_ptr<Value>
- default_value(const std::string& value){
+ std::shared_ptr<Value>
+ default_value(const std::string& value)
+ {
m_default = true;
m_default_value = value;
return shared_from_this();
}
- virtual std::shared_ptr<Value>
- implicit_value(const std::string& value){
+ std::shared_ptr<Value>
+ implicit_value(const std::string& value)
+ {
m_implicit = true;
m_implicit_value = value;
return shared_from_this();
@@ -774,6 +804,12 @@ namespace cxxopts
return m_implicit_value;
}
+ bool
+ is_boolean() const
+ {
+ return std::is_same<T, bool>::value;
+ }
+
const T&
get() const
{
@@ -790,11 +826,59 @@ namespace cxxopts
protected:
std::shared_ptr<T> m_result;
T* m_store;
+
bool m_default = false;
- std::string m_default_value;
bool m_implicit = false;
+
+ std::string m_default_value;
std::string m_implicit_value;
};
+
+ template <typename T>
+ class standard_value : public abstract_value<T>
+ {
+ public:
+ using abstract_value<T>::abstract_value;
+
+ std::shared_ptr<Value>
+ clone() const
+ {
+ return std::make_shared<standard_value<T>>(*this);
+ }
+ };
+
+ template <>
+ class standard_value<bool> : public abstract_value<bool>
+ {
+ public:
+ ~standard_value() = default;
+
+ standard_value()
+ {
+ set_implicit();
+ }
+
+ standard_value(bool* b)
+ : abstract_value(b)
+ {
+ set_implicit();
+ }
+
+ std::shared_ptr<Value>
+ clone() const
+ {
+ return std::make_shared<standard_value<bool>>(*this);
+ }
+
+ private:
+
+ void
+ set_implicit()
+ {
+ m_implicit = true;
+ m_implicit_value = "true";
+ }
+ };
}
template <typename T>
@@ -818,62 +902,59 @@ namespace cxxopts
public:
OptionDetails
(
+ const std::string& short_name,
+ const std::string& long_name,
const String& desc,
std::shared_ptr<const Value> val
)
- : m_desc(desc)
+ : m_short(short_name)
+ , m_long(long_name)
+ , m_desc(desc)
, m_value(val)
, m_count(0)
{
}
- const String&
- description() const
+ OptionDetails(const OptionDetails& rhs)
+ : m_desc(rhs.m_desc)
+ , m_count(rhs.m_count)
{
- return m_desc;
+ m_value = rhs.m_value->clone();
}
- bool
- has_arg() const
- {
- return m_value->has_arg();
- }
+ OptionDetails(OptionDetails&& rhs) = default;
- void
- parse(const std::string& text)
+ const String&
+ description() const
{
- m_value->parse(text);
- ++m_count;
+ return m_desc;
}
- void
- parse_default()
- {
- m_value->parse();
+ const Value& value() const {
+ return *m_value;
}
- int
- count() const
+ std::shared_ptr<Value>
+ make_storage() const
{
- return m_count;
+ return m_value->clone();
}
- const Value& value() const {
- return *m_value;
+ const std::string&
+ short_name() const
+ {
+ return m_short;
}
- template <typename T>
- const T&
- as() const
+ const std::string&
+ long_name() const
{
-#ifdef CXXOPTS_NO_RTTI
- return static_cast<const values::standard_value<T>&>(*m_value).get();
-#else
- return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
-#endif
+ return m_long;
}
private:
+ std::string m_short;
+ std::string m_long;
String m_desc;
std::shared_ptr<const Value> m_value;
int m_count;
@@ -884,13 +965,13 @@ namespace cxxopts
std::string s;
std::string l;
String desc;
- bool has_arg;
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
@@ -900,45 +981,104 @@ namespace cxxopts
std::vector<HelpOptionDetails> options;
};
- class Options
+ class OptionValue
{
public:
+ void
+ parse
+ (
+ std::shared_ptr<const OptionDetails> details,
+ const std::string& text
+ )
+ {
+ ensure_value(details);
+ ++m_count;
+ m_value->parse(text);
+ }
- Options(std::string program, std::string help_string = "")
- : m_program(std::move(program))
- , m_help_string(toLocalString(std::move(help_string)))
- , m_positional_help("positional parameters")
- , m_next_positional(m_positional.end())
+ void
+ parse_default(std::shared_ptr<const OptionDetails> details)
{
+ ensure_value(details);
+ m_value->parse();
+ m_count++;
}
- inline
- Options&
- positional_help(std::string help_text)
+ size_t
+ count() const
{
- m_positional_help = std::move(help_text);
- return *this;
+ return m_count;
}
- inline
+ template <typename T>
+ const T&
+ as() const
+ {
+#ifdef CXXOPTS_NO_RTTI
+ return static_cast<const values::standard_value<T>&>(*m_value).get();
+#else
+ return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
+#endif
+ }
+
+ private:
void
- parse(int& argc, char**& argv);
+ ensure_value(std::shared_ptr<const OptionDetails> details)
+ {
+ if (m_value == nullptr)
+ {
+ m_value = details->make_storage();
+ }
+ }
- inline
- OptionAdder
- add_options(std::string group = "");
+ std::shared_ptr<Value> m_value;
+ size_t m_count = 0;
+ };
- inline
- void
- add_option
- (
- const std::string& group,
- const std::string& s,
- const std::string& l,
- std::string desc,
- std::shared_ptr<const Value> value,
- std::string arg_help
- );
+ class KeyValue
+ {
+ public:
+ KeyValue(std::string key, std::string value)
+ : m_key(std::move(key))
+ , m_value(std::move(value))
+ {
+ }
+
+ const
+ std::string&
+ key() const
+ {
+ return m_key;
+ }
+
+ 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;
+ };
+
+ class ParseResult
+ {
+ public:
+
+ ParseResult(
+ const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>&,
+ std::vector<std::string>,
+ int&, char**&);
int
count(const std::string& o) const
@@ -949,10 +1089,12 @@ namespace cxxopts
return 0;
}
- return iter->second->count();
+ auto riter = m_results.find(iter->second);
+
+ return riter->second.count();
}
- const OptionDetails&
+ const OptionValue&
operator[](const std::string& option) const
{
auto iter = m_options.find(option);
@@ -962,49 +1104,31 @@ namespace cxxopts
throw option_not_present_exception(option);
}
- return *iter->second;
- }
-
- //parse positional arguments into the given option
- inline
- void
- parse_positional(std::string option);
+ auto riter = m_results.find(iter->second);
- inline
- void
- parse_positional(std::vector<std::string> options);
+ return riter->second;
+ }
- inline
- std::string
- help(const std::vector<std::string>& groups = {""}) const;
+ const std::vector<KeyValue>&
+ arguments() const
+ {
+ return m_sequential;
+ }
- inline
- const std::vector<std::string>
- groups() const;
+ private:
- inline
- const HelpGroupDetails&
- group_help(const std::string& group) const;
+ OptionValue&
+ get_option(std::shared_ptr<OptionDetails>);
- private:
+ void
+ parse(int& argc, char**& argv);
- inline
void
- add_one_option
- (
- const std::string& option,
- std::shared_ptr<OptionDetails> details
- );
+ add_to_option(const std::string& option, const std::string& arg);
- inline
bool
consume_positional(std::string a);
- inline
- void
- add_to_option(const std::string& option, const std::string& arg);
-
- inline
void
parse_option
(
@@ -1013,7 +1137,9 @@ namespace cxxopts
const std::string& arg = ""
);
- inline
+ void
+ parse_default(std::shared_ptr<OptionDetails> details);
+
void
checked_parse_arg
(
@@ -1024,11 +1150,88 @@ namespace cxxopts
const std::string& name
);
- inline
+ const 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;
+
+ std::vector<KeyValue> m_sequential;
+ };
+
+ class Options
+ {
+ public:
+
+ Options(std::string program, std::string help_string = "")
+ : m_program(std::move(program))
+ , m_help_string(toLocalString(std::move(help_string)))
+ , m_positional_help("positional parameters")
+ , m_show_positional(false)
+ , m_next_positional(m_positional.end())
+ {
+ }
+
+ Options&
+ positional_help(std::string help_text)
+ {
+ m_positional_help = std::move(help_text);
+ return *this;
+ }
+
+ Options&
+ show_positional_help()
+ {
+ m_show_positional = true;
+ return *this;
+ }
+
+ ParseResult
+ parse(int& argc, char**& argv);
+
+ OptionAdder
+ add_options(std::string group = "");
+
+ void
+ add_option
+ (
+ const std::string& group,
+ const std::string& s,
+ const std::string& l,
+ std::string desc,
+ std::shared_ptr<const Value> value,
+ std::string arg_help
+ );
+
+ //parse positional arguments into the given option
+ void
+ parse_positional(std::string option);
+
+ void
+ parse_positional(std::vector<std::string> options);
+
+ std::string
+ help(const std::vector<std::string>& groups = {""}) const;
+
+ const std::vector<std::string>
+ groups() const;
+
+ const HelpGroupDetails&
+ group_help(const std::string& group) const;
+
+ private:
+
+ void
+ add_one_option
+ (
+ const std::string& option,
+ std::shared_ptr<OptionDetails> details
+ );
+
String
help_one_group(const std::string& group) const;
- inline
void
generate_group_help
(
@@ -1036,15 +1239,15 @@ namespace cxxopts
const std::vector<std::string>& groups
) const;
- inline
void
generate_all_groups_help(String& result) const;
std::string m_program;
String m_help_string;
std::string m_positional_help;
+ bool m_show_positional;
- std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
+ 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;
@@ -1062,7 +1265,6 @@ namespace cxxopts
{
}
- inline
OptionAdder&
operator()
(
@@ -1078,24 +1280,6 @@ namespace cxxopts
std::string m_group;
};
- // A helper function for setting required arguments
- inline
- void
- check_required
- (
- const Options& options,
- const std::vector<std::string>& required
- )
- {
- for (auto& r : required)
- {
- if (options.count(r) == 0)
- {
- throw option_required_exception(r);
- }
- }
- }
-
namespace
{
constexpr int OPTION_LONGEST = 30;
@@ -1132,10 +1316,10 @@ namespace cxxopts
result += " --" + toLocalString(l);
}
- if (o.has_arg)
- {
- auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
+ auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
+ if (!o.is_boolean)
+ {
if (o.has_implicit)
{
result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
@@ -1213,12 +1397,28 @@ namespace cxxopts
}
}
+inline
+ParseResult::ParseResult
+(
+ const std::unordered_map<std::string, std::shared_ptr<OptionDetails>>& options,
+ std::vector<std::string> positional,
+ int& argc, char**& argv
+)
+: m_options(options)
+, m_positional(std::move(positional))
+, m_next_positional(m_positional.begin())
+{
+ parse(argc, argv);
+}
+
+inline
OptionAdder
Options::add_options(std::string group)
{
return OptionAdder(*this, std::move(group));
}
+inline
OptionAdder&
OptionAdder::operator()
(
@@ -1276,19 +1476,31 @@ OptionAdder::operator()
return *this;
}
+inline
+void
+ParseResult::parse_default(std::shared_ptr<OptionDetails> details)
+{
+ m_results[details].parse_default(details);
+}
+
+inline
void
-Options::parse_option
+ParseResult::parse_option
(
std::shared_ptr<OptionDetails> value,
const std::string& /*name*/,
const std::string& arg
)
{
- value->parse(arg);
+ auto& result = m_results[value];
+ result.parse(value, arg);
+
+ m_sequential.emplace_back(value->long_name(), arg);
}
+inline
void
-Options::checked_parse_arg
+ParseResult::checked_parse_arg
(
int argc,
char* argv[],
@@ -1322,8 +1534,9 @@ Options::checked_parse_arg
}
}
+inline
void
-Options::add_to_option(const std::string& option, const std::string& arg)
+ParseResult::add_to_option(const std::string& option, const std::string& arg)
{
auto iter = m_options.find(option);
@@ -1335,17 +1548,19 @@ Options::add_to_option(const std::string& option, const std::string& arg)
parse_option(iter->second, option, arg);
}
+inline
bool
-Options::consume_positional(std::string a)
+ParseResult::consume_positional(std::string a)
{
while (m_next_positional != m_positional.end())
{
auto iter = m_options.find(*m_next_positional);
if (iter != m_options.end())
{
+ auto& result = m_results[iter->second];
if (!iter->second->value().is_container())
{
- if (iter->second->count() == 0)
+ if (result.count() == 0)
{
add_to_option(*m_next_positional, a);
++m_next_positional;
@@ -1369,12 +1584,14 @@ Options::consume_positional(std::string a)
return false;
}
+inline
void
Options::parse_positional(std::string option)
{
parse_positional(std::vector<std::string>{option});
}
+inline
void
Options::parse_positional(std::vector<std::string> options)
{
@@ -1384,9 +1601,18 @@ Options::parse_positional(std::vector<std::string> options)
m_positional_set.insert(m_positional.begin(), m_positional.end());
}
-void
+inline
+ParseResult
Options::parse(int& argc, char**& argv)
{
+ ParseResult result(m_options, m_positional, argc, argv);
+ return result;
+}
+
+inline
+void
+ParseResult::parse(int& argc, char**& argv)
+{
int current = 1;
int nextKeep = 1;
@@ -1440,27 +1666,19 @@ Options::parse(int& argc, char**& argv)
auto value = iter->second;
- //if no argument then just add it
- if (!value->has_arg())
+ if (i + 1 == s.size())
+ {
+ //it must be the last argument
+ checked_parse_arg(argc, argv, current, value, name);
+ }
+ else if (value->value().has_implicit())
{
- parse_option(value, name);
+ parse_option(value, name, value->value().get_implicit_value());
}
else
{
- //it must be the last argument
- if (i + 1 == s.size())
- {
- checked_parse_arg(argc, argv, current, value, name);
- }
- else if (value->value().has_implicit())
- {
- parse_option(value, name, value->value().get_implicit_value());
- }
- else
- {
- //error
- throw option_requires_argument_exception(name);
- }
+ //error
+ throw option_requires_argument_exception(name);
}
}
}
@@ -1482,26 +1700,12 @@ Options::parse(int& argc, char**& argv)
{
//parse the option given
- //but if it doesn't take an argument, this is an error
- if (!opt->has_arg())
- {
- throw option_not_has_argument_exception(name, result[3]);
- }
-
parse_option(opt, name, result[3]);
}
else
{
- if (opt->has_arg())
- {
- //parse the next argument
- checked_parse_arg(argc, argv, current, opt, name);
- }
- else
- {
- //parse with empty argument
- parse_option(opt, name);
- }
+ //parse the next argument
+ checked_parse_arg(argc, argv, current, opt, name);
}
}
@@ -1515,8 +1719,10 @@ Options::parse(int& argc, char**& argv)
auto& detail = opt.second;
auto& value = detail->value();
- if(!detail->count() && value.has_default()){
- detail->parse_default();
+ auto& store = m_results[detail];
+
+ if(!store.count() && value.has_default()){
+ parse_default(detail);
}
}
@@ -1542,6 +1748,7 @@ Options::parse(int& argc, char**& argv)
}
+inline
void
Options::add_option
(
@@ -1554,7 +1761,7 @@ Options::add_option
)
{
auto stringDesc = toLocalString(std::move(desc));
- auto option = std::make_shared<OptionDetails>(stringDesc, value);
+ auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
if (s.size() > 0)
{
@@ -1570,13 +1777,14 @@ Options::add_option
auto& options = m_help[group];
options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
- value->has_arg(),
value->has_default(), value->get_default_value(),
value->has_implicit(), value->get_implicit_value(),
std::move(arg_help),
- value->is_container()});
+ value->is_container(),
+ value->is_boolean()});
}
+inline
void
Options::add_one_option
(
@@ -1592,6 +1800,7 @@ Options::add_one_option
}
}
+inline
String
Options::help_one_group(const std::string& g) const
{
@@ -1616,7 +1825,9 @@ Options::help_one_group(const std::string& g) const
for (const auto& o : group->second.options)
{
- if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end())
+ if (o.is_container &&
+ m_positional_set.find(o.l) != m_positional_set.end() &&
+ !m_show_positional)
{
continue;
}
@@ -1634,7 +1845,9 @@ Options::help_one_group(const std::string& g) const
auto fiter = format.begin();
for (const auto& o : group->second.options)
{
- if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end())
+ if (o.is_container &&
+ m_positional_set.find(o.l) != m_positional_set.end() &&
+ !m_show_positional)
{
continue;
}
@@ -1662,6 +1875,7 @@ Options::help_one_group(const std::string& g) const
return result;
}
+inline
void
Options::generate_group_help
(
@@ -1684,6 +1898,7 @@ Options::generate_group_help
}
}
+inline
void
Options::generate_all_groups_help(String& result) const
{
@@ -1698,6 +1913,7 @@ Options::generate_all_groups_help(String& result) const
generate_group_help(result, all_groups);
}
+inline
std::string
Options::help(const std::vector<std::string>& help_groups) const
{
@@ -1722,6 +1938,7 @@ Options::help(const std::vector<std::string>& help_groups) const
return toUTF8String(result);
}
+inline
const std::vector<std::string>
Options::groups() const
{
@@ -1740,6 +1957,7 @@ Options::groups() const
return g;
}
+inline
const HelpGroupDetails&
Options::group_help(const std::string& group) const
{
@@ -1748,8 +1966,4 @@ Options::group_help(const std::string& group) const
}
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#endif
-
-#endif //CXX_OPTS_HPP
+#endif //CXXOPTS_HPP_INCLUDED
diff --git a/src/example.cpp b/src/example.cpp
index 44271b4..b541774 100644
--- a/src/example.cpp
+++ b/src/example.cpp
@@ -31,7 +31,9 @@ int main(int argc, char* argv[])
try
{
cxxopts::Options options(argv[0], " - example command line options");
- options.positional_help("[optional args]");
+ options
+ .positional_help("[optional args]")
+ .show_positional_help();
bool apple = false;
@@ -63,9 +65,9 @@ int main(int argc, char* argv[])
options.parse_positional({"input", "output", "positional"});
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
- if (options.count("help"))
+ if (result.count("help"))
{
std::cout << options.help({"", "Group"}) << std::endl;
exit(0);
@@ -73,18 +75,18 @@ int main(int argc, char* argv[])
if (apple)
{
- std::cout << "Saw option ‘a’ " << options.count("a") << " times " <<
+ std::cout << "Saw option ‘a’ " << result.count("a") << " times " <<
std::endl;
}
- if (options.count("b"))
+ if (result.count("b"))
{
std::cout << "Saw option ‘b’" << std::endl;
}
- if (options.count("f"))
+ if (result.count("f"))
{
- auto& ff = options["f"].as<std::vector<std::string>>();
+ auto& ff = result["f"].as<std::vector<std::string>>();
std::cout << "Files" << std::endl;
for (const auto& f : ff)
{
@@ -92,36 +94,36 @@ int main(int argc, char* argv[])
}
}
- if (options.count("input"))
+ if (result.count("input"))
{
- std::cout << "Input = " << options["input"].as<std::string>()
+ std::cout << "Input = " << result["input"].as<std::string>()
<< std::endl;
}
- if (options.count("output"))
+ if (result.count("output"))
{
- std::cout << "Output = " << options["output"].as<std::string>()
+ std::cout << "Output = " << result["output"].as<std::string>()
<< std::endl;
}
- if (options.count("positional"))
+ if (result.count("positional"))
{
std::cout << "Positional = {";
- auto& v = options["positional"].as<std::vector<std::string>>();
+ auto& v = result["positional"].as<std::vector<std::string>>();
for (const auto& s : v) {
std::cout << s << ", ";
}
std::cout << "}" << std::endl;
}
- if (options.count("int"))
+ if (result.count("int"))
{
- std::cout << "int = " << options["int"].as<int>() << std::endl;
+ std::cout << "int = " << result["int"].as<int>() << std::endl;
}
- if (options.count("float"))
+ if (result.count("float"))
{
- std::cout << "float = " << options["float"].as<float>() << std::endl;
+ std::cout << "float = " << result["float"].as<float>() << std::endl;
}
std::cout << "Arguments remain = " << argc << std::endl;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 0920e90..1969545 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -29,4 +29,7 @@ if (CXXOPTS_BUILD_TESTS)
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
)
+
+ add_executable(link_test link_a.cpp link_b.cpp)
+ target_link_libraries(link_test cxxopts)
endif()
diff --git a/test/link_a.cpp b/test/link_a.cpp
new file mode 100644
index 0000000..3611692
--- /dev/null
+++ b/test/link_a.cpp
@@ -0,0 +1,6 @@
+#include "cxxopts.hpp"
+
+int main(int, char**)
+{
+ return 0;
+}
diff --git a/test/link_b.cpp b/test/link_b.cpp
new file mode 100644
index 0000000..e48e22a
--- /dev/null
+++ b/test/link_b.cpp
@@ -0,0 +1 @@
+#include <cxxopts.hpp>
diff --git a/test/options.cpp b/test/options.cpp
index d428f9a..afd7292 100644
--- a/test/options.cpp
+++ b/test/options.cpp
@@ -71,17 +71,27 @@ TEST_CASE("Basic options", "[options]")
char** actual_argv = argv.argv();
auto argc = argv.argc();
- options.parse(argc, actual_argv);
-
- CHECK(options.count("long") == 1);
- CHECK(options.count("s") == 1);
- CHECK(options.count("value") == 1);
- CHECK(options.count("a") == 1);
- CHECK(options["value"].as<std::string>() == "value");
- CHECK(options["a"].as<std::string>() == "b");
- CHECK(options.count("6") == 1);
- CHECK(options.count("p") == 2);
- CHECK(options.count("space") == 2);
+ auto result = options.parse(argc, actual_argv);
+
+ CHECK(result.count("long") == 1);
+ CHECK(result.count("s") == 1);
+ CHECK(result.count("value") == 1);
+ CHECK(result.count("a") == 1);
+ CHECK(result["value"].as<std::string>() == "value");
+ CHECK(result["a"].as<std::string>() == "b");
+ CHECK(result.count("6") == 1);
+ CHECK(result.count("p") == 2);
+ CHECK(result.count("space") == 2);
+
+ auto& arguments = result.arguments();
+ REQUIRE(arguments.size() == 7);
+ CHECK(arguments[0].key() == "long");
+ CHECK(arguments[0].value() == "true");
+ CHECK(arguments[0].as<bool>() == true);
+
+ CHECK(arguments[1].key() == "short");
+ CHECK(arguments[2].key() == "value");
+ CHECK(arguments[3].key() == "av");
}
TEST_CASE("Short options", "[options]")
@@ -96,36 +106,15 @@ TEST_CASE("Short options", "[options]")
auto actual_argv = argv.argv();
auto argc = argv.argc();
- options.parse(argc, actual_argv);
+ auto result = options.parse(argc, actual_argv);
- CHECK(options.count("a") == 1);
- CHECK(options["a"].as<std::string>() == "value");
+ CHECK(result.count("a") == 1);
+ CHECK(result["a"].as<std::string>() == "value");
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
cxxopts::invalid_option_format_error);
}
-TEST_CASE("Required arguments", "[options]")
-{
- cxxopts::Options options("required", " - test required options");
- options.add_options()
- ("one", "one option")
- ("two", "second option")
- ;
-
- Argv argv({
- "required",
- "--one"
- });
-
- auto aargv = argv.argv();
- auto argc = argv.argc();
-
- options.parse(argc, aargv);
- REQUIRE_THROWS_AS(cxxopts::check_required(options, {"two"}),
- cxxopts::option_required_exception);
-}
-
TEST_CASE("No positional", "[positional]")
{
cxxopts::Options options("test_no_positional",
@@ -135,7 +124,7 @@ TEST_CASE("No positional", "[positional]")
char** argv = av.argv();
auto argc = av.argc();
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
REQUIRE(argc == 4);
CHECK(strcmp(argv[1], "a") == 0);
@@ -158,7 +147,7 @@ TEST_CASE("All positional", "[positional]")
options.parse_positional("positional");
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
REQUIRE(argc == 1);
REQUIRE(positional.size() == 3);
@@ -186,14 +175,14 @@ TEST_CASE("Some positional explicit", "[positional]")
char** argv = av.argv();
auto argc = av.argc();
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
CHECK(argc == 1);
- CHECK(options.count("output"));
- CHECK(options["input"].as<std::string>() == "b");
- CHECK(options["output"].as<std::string>() == "a");
+ CHECK(result.count("output"));
+ CHECK(result["input"].as<std::string>() == "b");
+ CHECK(result["output"].as<std::string>() == "a");
- auto& positional = options["positional"].as<std::vector<std::string>>();
+ auto& positional = result["positional"].as<std::vector<std::string>>();
REQUIRE(positional.size() == 2);
CHECK(positional[0] == "c");
@@ -234,10 +223,58 @@ TEST_CASE("Empty with implicit value", "[implicit]")
char** argv = av.argv();
auto argc = av.argc();
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
- REQUIRE(options.count("implicit") == 1);
- REQUIRE(options["implicit"].as<std::string>() == "");
+ REQUIRE(result.count("implicit") == 1);
+ REQUIRE(result["implicit"].as<std::string>() == "");
+}
+
+TEST_CASE("Default values", "[default]")
+{
+ cxxopts::Options options("defaults", "has defaults");
+ options.add_options()
+ ("default", "Has implicit", cxxopts::value<int>()
+ ->default_value("42"));
+
+ SECTION("Sets defaults") {
+ Argv av({"implicit"});
+
+ char** argv = av.argv();
+ auto argc = av.argc();
+
+ auto result = options.parse(argc, argv);
+ CHECK(result.count("default") == 1);
+ CHECK(result["default"].as<int>() == 42);
+ }
+
+ SECTION("When values provided") {
+ Argv av({"implicit", "--default", "5"});
+
+ char** argv = av.argv();
+ auto argc = av.argc();
+
+ auto result = options.parse(argc, argv);
+ CHECK(result.count("default") == 1);
+ CHECK(result["default"].as<int>() == 5);
+ }
+}
+
+TEST_CASE("Parse into a reference", "[reference]")
+{
+ int value = 0;
+
+ cxxopts::Options options("into_reference", "parses into a reference");
+ options.add_options()
+ ("ref", "A reference", cxxopts::value(value));
+
+ Argv av({"into_reference", "--ref", "42"});
+
+ auto argv = av.argv();
+ auto argc = av.argc();
+
+ auto result = options.parse(argc, argv);
+ CHECK(result.count("ref") == 1);
+ CHECK(value == 42);
}
TEST_CASE("Integers", "[options]")
@@ -252,11 +289,12 @@ TEST_CASE("Integers", "[options]")
auto argc = av.argc();
options.parse_positional("positional");
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
- REQUIRE(options.count("positional") == 7);
+ REQUIRE(result.count("positional") == 7);
- auto& positional = options["positional"].as<std::vector<int>>();
+ auto& positional = result["positional"].as<std::vector<int>>();
+ REQUIRE(positional.size() == 7);
CHECK(positional[0] == 5);
CHECK(positional[1] == 6);
CHECK(positional[2] == -6);
@@ -295,11 +333,11 @@ TEST_CASE("Integer bounds", "[integer]")
auto argc = av.argc();
options.parse_positional("positional");
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
- REQUIRE(options.count("positional") == 5);
+ REQUIRE(result.count("positional") == 5);
- auto& positional = options["positional"].as<std::vector<int8_t>>();
+ auto& positional = result["positional"].as<std::vector<int8_t>>();
CHECK(positional[0] == 127);
CHECK(positional[1] == -128);
CHECK(positional[2] == 0x7f);
@@ -351,14 +389,14 @@ TEST_CASE("Floats", "[options]")
auto argc = av.argc();
options.parse_positional("positional");
- options.parse(argc, argv);
+ auto result = options.parse(argc, argv);
- REQUIRE(options.count("double") == 1);
- REQUIRE(options.count("positional") == 4);
+ REQUIRE(result.count("double") == 1);
+ REQUIRE(result.count("positional") == 4);
- CHECK(options["double"].as<double>() == 0.5);
+ CHECK(result["double"].as<double>() == 0.5);
- auto& positional = options["positional"].as<std::vector<float>>();
+ auto& positional = result["positional"].as<std::vector<float>>();
CHECK(positional[0] == 4);
CHECK(positional[1] == -4);
CHECK(positional[2] == 1.5e6);
@@ -378,3 +416,27 @@ TEST_CASE("Invalid integers", "[integer]") {
options.parse_positional("positional");
CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type);
}
+
+TEST_CASE("Booleans", "[boolean]") {
+ cxxopts::Options options("parses_floats", "parses floats correctly");
+ options.add_options()
+ ("bool", "A Boolean", cxxopts::value<bool>())
+ ("debug", "Debugging", cxxopts::value<bool>())
+ ("timing", "Timing", cxxopts::value<bool>())
+ ;
+
+ Argv av({"booleans", "--bool=false", "--debug", "true", "--timing"});
+
+ char** argv = av.argv();
+ auto argc = av.argc();
+
+ auto result = options.parse(argc, argv);
+
+ REQUIRE(result.count("bool") == 1);
+ REQUIRE(result.count("debug") == 1);
+ REQUIRE(result.count("timing") == 1);
+
+ CHECK(result["bool"].as<bool>() == false);
+ CHECK(result["debug"].as<bool>() == true);
+ CHECK(result["timing"].as<bool>() == true);
+}