Get arguments in optional<T> with .present<T>()

fixes: p-ranav/argparse#66
This commit is contained in:
Zhihao Yuan 2019-11-30 00:34:56 -06:00
parent 4277e68e57
commit e6c6c9b31c
No known key found for this signature in database
GPG Key ID: 0BC0BC626A0C5A8E
3 changed files with 100 additions and 3 deletions

View File

@ -140,6 +140,18 @@ program.add_argument("-o", "--output")
.help("specify the output file.");
```
#### Accessing optional arguments without default values
If you do not require an optional argument to present but has no good default value for it, you can combine testing and accessing the argument as following:
```cpp
if (auto fn = program.present("-o")) {
do_something_with(*fn);
}
```
Similar to `get`, the `present` method also accepts a template argument. But rather than returning `T`, `parser.present<T>(key)` returns `std::optional<T>`, so that when the user does not provide a value to this parameter, the return value compares equal to `std::nullopt`.
### Negative Numbers
Optional arguments start with ```-```. Can ```argparse``` handle negative numbers? The answer is yes!

View File

@ -702,6 +702,23 @@ private:
throw std::logic_error("No value provided");
}
/*
* Get argument value given a type.
* @pre The object has no default value.
* @returns The stored value if any, std::nullopt otherwise.
*/
template <typename T> auto present() const -> std::optional<T> {
if (mDefaultValue.has_value())
throw std::logic_error("Argument with default value always presents");
if (mValues.empty())
return std::nullopt;
else if constexpr (details::is_container_v<T>)
return any_cast_container<T>(mValues);
else
return std::any_cast<T>(mValues.front());
}
template <typename T>
static auto any_cast_container(const std::vector<std::any> &aOperand) -> T {
using ValueType = typename T::value_type;
@ -811,14 +828,25 @@ public:
parse_args(arguments);
}
/* Getter enabled for all template types other than std::vector and std::list
* @throws std::logic_error in case of an invalid argument name
* @throws std::logic_error in case of incompatible types
/* Getter for options with default values.
* @throws std::logic_error if there is no such option
* @throws std::logic_error if the option has no value
* @throws std::bad_any_cast if the option is not of type T
*/
template <typename T = std::string> T get(std::string_view aArgumentName) {
return (*this)[aArgumentName].get<T>();
}
/* Getter for options without default values.
* @pre The option has no default value.
* @throws std::logic_error if there is no such option
* @throws std::bad_any_cast if the option is not of type T
*/
template <typename T = std::string>
auto present(std::string_view aArgumentName) -> std::optional<T> {
return (*this)[aArgumentName].present<T>();
}
/* Indexing operator. Return a reference to an Argument object
* Used in conjuction with Argument.operator== e.g., parser["foo"] == true
* @throws std::logic_error in case of an invalid argument name

View File

@ -19,6 +19,32 @@ TEST_CASE("Parse a string argument with default value" *
REQUIRE(program.get("--config") == "foo.yml");
}
TEST_CASE("Parse a string argument without default value" *
test_suite("parse_args")) {
argparse::ArgumentParser program("test");
program.add_argument("--config");
WHEN("no value provided") {
program.parse_args({"test"});
THEN("the option is nullopt") {
auto opt = program.present("--config");
REQUIRE_FALSE(opt);
REQUIRE(opt == std::nullopt);
}
}
WHEN("a value is provided") {
program.parse_args({"test", "--config", ""});
THEN("the option has a value") {
auto opt = program.present("--config");
REQUIRE(opt);
REQUIRE(opt->empty());
}
}
}
TEST_CASE("Parse an int argument with value" * test_suite("parse_args")) {
argparse::ArgumentParser program("test");
program.add_argument("--count")
@ -103,6 +129,37 @@ TEST_CASE("Parse a vector of float arguments" * test_suite("parse_args")) {
REQUIRE(vector[4] == 5.5f);
}
TEST_CASE("Parse a vector of float without default value" *
test_suite("parse_args")) {
argparse::ArgumentParser program("test");
program.add_argument("--vector").scan<'f', float>().nargs(3);
WHEN("no value is provided") {
program.parse_args({"test"});
THEN("the option is nullopt") {
auto opt = program.present<std::vector<float>>("--vector");
REQUIRE_FALSE(opt.has_value());
REQUIRE(opt == std::nullopt);
}
}
WHEN("a value is provided") {
program.parse_args({"test", "--vector", ".3", "1.3", "6"});
THEN("the option has a value") {
auto opt = program.present<std::vector<float>>("--vector");
REQUIRE(opt.has_value());
auto &&vec = opt.value();
REQUIRE(vec.size() == 3);
REQUIRE(vec[0] == .3f);
REQUIRE(vec[1] == 1.3f);
REQUIRE(vec[2] == 6.f);
}
}
}
TEST_CASE("Parse a vector of double arguments" * test_suite("parse_args")) {
argparse::ArgumentParser program("test");
program.add_argument("--vector")