Merge pull request #99 from skrobinson/wip-append-method

Add Argument.append method to allow repeated argument use
This commit is contained in:
Pranav 2021-04-07 14:26:45 -05:00 committed by GitHub
commit 65f2ad4db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 3 deletions

View File

@ -154,6 +154,30 @@ 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`.
#### Joining values of repeated optional arguments
You may want to allow an optional argument to be repeated and gather all values in one place.
```cpp
program.add_argument("--color")
.default_value<std::vector<std::string>>({ "orange" })
.append()
.help("specify the cat's fur color");
try {
program.parse_args(argc, argv); // Example: ./main --color red --color green --color blue
}
catch (const std::runtime_error& err) {
std::cout << err.what() << std::endl;
std::cout << program;
exit(0);
}
auto colors = program.get<std::vector<std::string>>("--color"); // {"red", "green", "blue"}
```
Notice that ```.default_value``` is given an explicit template parameter to match the type you want to ```.get```.
### Negative Numbers
Optional arguments start with ```-```. Can ```argparse``` handle negative numbers? The answer is yes!

View File

@ -322,7 +322,7 @@ class Argument {
template <size_t N, size_t... I>
explicit Argument(std::string_view(&&a)[N], std::index_sequence<I...>)
: mIsOptional((is_optional(a[I]) || ...)), mIsRequired(false),
mIsUsed(false) {
mIsRepeatable(false), mIsUsed(false) {
((void)mNames.emplace_back(a[I]), ...);
std::sort(
mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) {
@ -376,6 +376,11 @@ public:
return *this;
}
auto &append() {
mIsRepeatable = true;
return *this;
}
template <char Shape, typename T>
auto scan() -> std::enable_if_t<std::is_arithmetic_v<T>, Argument &> {
static_assert(!(std::is_const_v<T> || std::is_volatile_v<T>),
@ -430,7 +435,7 @@ public:
template <typename Iterator>
Iterator consume(Iterator start, Iterator end,
std::string_view usedName = {}) {
if (mIsUsed) {
if (!mIsRepeatable && mIsUsed) {
throw std::runtime_error("Duplicate argument");
}
mIsUsed = true;
@ -477,7 +482,7 @@ public:
void validate() const {
if (auto expected = maybe_nargs()) {
if (mIsOptional) {
if (mIsUsed && mValues.size() != *expected &&
if (mIsUsed && mValues.size() != *expected && !mIsRepeatable &&
!mDefaultValue.has_value()) {
std::stringstream stream;
stream << mUsedName << ": expected " << *expected << " argument(s). "
@ -800,6 +805,7 @@ private:
int mNumArgs = 1;
bool mIsOptional : true;
bool mIsRequired : true;
bool mIsRepeatable : true;
bool mIsUsed : true; // True if the optional argument is used by user
};

View File

@ -25,6 +25,7 @@ endif()
file(GLOB ARGPARSE_TEST_SOURCES
main.cpp
test_actions.cpp
test_append.cpp
test_compound_arguments.cpp
test_container_arguments.cpp
test_help.cpp

46
test/test_append.cpp Normal file
View File

@ -0,0 +1,46 @@
#include <doctest.hpp>
#include <argparse/argparse.hpp>
using doctest::test_suite;
TEST_CASE("Simplest .append" * test_suite("append")) {
argparse::ArgumentParser program("test");
program.add_argument("--dir")
.append();
program.parse_args({ "test", "--dir", "./Docs" });
std::string result { program.get("--dir") };
REQUIRE(result == "./Docs");
}
TEST_CASE("Two parameter .append" * test_suite("append")) {
argparse::ArgumentParser program("test");
program.add_argument("--dir")
.append();
program.parse_args({ "test", "--dir", "./Docs", "--dir", "./Src" });
auto result { program.get<std::vector<std::string>>("--dir") };
REQUIRE(result.at(0) == "./Docs");
REQUIRE(result.at(1) == "./Src");
}
TEST_CASE("Two int .append" * test_suite("append")) {
argparse::ArgumentParser program("test");
program.add_argument("--factor")
.append()
.action([](auto s) { return stoi(s); });
program.parse_args({ "test", "--factor", "2", "--factor", "5" });
auto result { program.get<std::vector<int>>("--factor") };
REQUIRE(result.at(0) == 2);
REQUIRE(result.at(1) == 5);
}
TEST_CASE("Default value with .append" * test_suite("append")) {
std::vector<std::string> expected { "./Src", "./Imgs" };
argparse::ArgumentParser program("test");
program.add_argument("--dir")
.default_value(expected)
.append();
program.parse_args({ "test" });
auto result { program.get<std::vector<std::string>>("--dir") };
REQUIRE(result == expected);
}