Add ArgumentParser.is_used to discern user-supplied values from defaults

.present returns std::nullopt if the optional argument is not given by the
user -- as long as a .default_value is not defined.  With a .default_value,
.present cannot be used to determine if a value is user-provided or the
default.

.is_used fills that role and only returns true if the argument was passed
by the user.

Signed-off-by: Sean Robinson <sean.robinson@scottsdalecc.edu>
This commit is contained in:
Sean Robinson 2021-04-01 15:49:10 -07:00
parent 65f2ad4db2
commit 3efd045ea9
4 changed files with 52 additions and 0 deletions

View File

@ -154,6 +154,28 @@ if (auto fn = program.present("-o")) {
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`. 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`.
#### Deciding if the value was given by the user
If you want to know whether the user supplied a value for an argument that has a ```.default_value```, check whether the argument ```.is_used()```.
```cpp
program.add_argument("--color")
.default_value("orange")
.help("specify the cat's fur color");
try {
program.parse_args(argc, argv); // Example: ./main --color orange
}
catch (const std::runtime_error& err) {
std::cout << err.what() << std::endl;
std::cout << program;
exit(0);
}
auto color = program.get<std::string>("--color"); // "orange"
auto explicit_color = program.is_used("--color"); // true, user provided orange
```
#### Joining values of repeated optional arguments #### Joining values of repeated optional arguments
You may want to allow an optional argument to be repeated and gather all values in one place. You may want to allow an optional argument to be repeated and gather all values in one place.

View File

@ -925,6 +925,13 @@ public:
return (*this)[aArgumentName].present<T>(); return (*this)[aArgumentName].present<T>();
} }
/* Getter that returns true for user-supplied options. Returns false if not
* user-supplied, even with a default value.
*/
auto is_used(std::string_view aArgumentName) const {
return (*this)[aArgumentName].mIsUsed;
}
/* Indexing operator. Return a reference to an Argument object /* Indexing operator. Return a reference to an Argument object
* Used in conjuction with Argument.operator== e.g., parser["foo"] == true * Used in conjuction with Argument.operator== e.g., parser["foo"] == true
* @throws std::logic_error in case of an invalid argument name * @throws std::logic_error in case of an invalid argument name

View File

@ -30,6 +30,7 @@ file(GLOB ARGPARSE_TEST_SOURCES
test_container_arguments.cpp test_container_arguments.cpp
test_help.cpp test_help.cpp
test_invalid_arguments.cpp test_invalid_arguments.cpp
test_is_used.cpp
test_issue_37.cpp test_issue_37.cpp
test_negative_numbers.cpp test_negative_numbers.cpp
test_optional_arguments.cpp test_optional_arguments.cpp

22
test/test_is_used.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <doctest.hpp>
#include <argparse/argparse.hpp>
using doctest::test_suite;
TEST_CASE("User-supplied argument" * test_suite("is_used")) {
argparse::ArgumentParser program("test");
program.add_argument("--dir")
.default_value(std::string("/"));
program.parse_args({ "test", "--dir", "/home/user" });
REQUIRE(program.get("--dir") == "/home/user");
REQUIRE(program.is_used("--dir") == true);
}
TEST_CASE("Not user-supplied argument" * test_suite("is_used")) {
argparse::ArgumentParser program("test");
program.add_argument("--dir")
.default_value(std::string("/"));
program.parse_args({ "test" });
REQUIRE(program.get("--dir") == "/");
REQUIRE(program.is_used("--dir") == false);
}