mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 07:04: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`.
|
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
|
### Negative Numbers
|
||||||
|
|
||||||
Optional arguments start with ```-```. Can ```argparse``` handle negative numbers? The answer is yes!
|
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>
|
template <size_t N, size_t... I>
|
||||||
explicit Argument(std::string_view(&&a)[N], std::index_sequence<I...>)
|
explicit Argument(std::string_view(&&a)[N], std::index_sequence<I...>)
|
||||||
: mIsOptional((is_optional(a[I]) || ...)), mIsRequired(false),
|
: mIsOptional((is_optional(a[I]) || ...)), mIsRequired(false),
|
||||||
mIsUsed(false) {
|
mIsRepeatable(false), mIsUsed(false) {
|
||||||
((void)mNames.emplace_back(a[I]), ...);
|
((void)mNames.emplace_back(a[I]), ...);
|
||||||
std::sort(
|
std::sort(
|
||||||
mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) {
|
mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) {
|
||||||
@ -376,6 +376,11 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto &append() {
|
||||||
|
mIsRepeatable = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template <char Shape, typename T>
|
template <char Shape, typename T>
|
||||||
auto scan() -> std::enable_if_t<std::is_arithmetic_v<T>, Argument &> {
|
auto scan() -> std::enable_if_t<std::is_arithmetic_v<T>, Argument &> {
|
||||||
static_assert(!(std::is_const_v<T> || std::is_volatile_v<T>),
|
static_assert(!(std::is_const_v<T> || std::is_volatile_v<T>),
|
||||||
@ -430,7 +435,7 @@ public:
|
|||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
Iterator consume(Iterator start, Iterator end,
|
Iterator consume(Iterator start, Iterator end,
|
||||||
std::string_view usedName = {}) {
|
std::string_view usedName = {}) {
|
||||||
if (mIsUsed) {
|
if (!mIsRepeatable && mIsUsed) {
|
||||||
throw std::runtime_error("Duplicate argument");
|
throw std::runtime_error("Duplicate argument");
|
||||||
}
|
}
|
||||||
mIsUsed = true;
|
mIsUsed = true;
|
||||||
@ -477,7 +482,7 @@ public:
|
|||||||
void validate() const {
|
void validate() const {
|
||||||
if (auto expected = maybe_nargs()) {
|
if (auto expected = maybe_nargs()) {
|
||||||
if (mIsOptional) {
|
if (mIsOptional) {
|
||||||
if (mIsUsed && mValues.size() != *expected &&
|
if (mIsUsed && mValues.size() != *expected && !mIsRepeatable &&
|
||||||
!mDefaultValue.has_value()) {
|
!mDefaultValue.has_value()) {
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << mUsedName << ": expected " << *expected << " argument(s). "
|
stream << mUsedName << ": expected " << *expected << " argument(s). "
|
||||||
@ -800,6 +805,7 @@ private:
|
|||||||
int mNumArgs = 1;
|
int mNumArgs = 1;
|
||||||
bool mIsOptional : true;
|
bool mIsOptional : true;
|
||||||
bool mIsRequired : true;
|
bool mIsRequired : true;
|
||||||
|
bool mIsRepeatable : true;
|
||||||
bool mIsUsed : true; // True if the optional argument is used by user
|
bool mIsUsed : true; // True if the optional argument is used by user
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ endif()
|
|||||||
file(GLOB ARGPARSE_TEST_SOURCES
|
file(GLOB ARGPARSE_TEST_SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
test_actions.cpp
|
test_actions.cpp
|
||||||
|
test_append.cpp
|
||||||
test_compound_arguments.cpp
|
test_compound_arguments.cpp
|
||||||
test_container_arguments.cpp
|
test_container_arguments.cpp
|
||||||
test_help.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