mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 15:14:39 +00:00
Add Argument.append method to allow repeated argument use
The default behavior with optional arguments is to allow only a single use per invocation. One alternative is to use .nargs, but this requires previously knowing, and limiting, the quantity of values. The .append method removes the restriction on repeats for a single Argument. Signed-off-by: Sean Robinson <sean.robinson@scottsdalecc.edu>
This commit is contained in:
parent
9e9d969814
commit
54d3cda804
24
README.md
24
README.md
@ -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!
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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
46
test/test_append.cpp
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user