mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-03 22:54:39 +00:00
Added support for integer type in choices
This commit is contained in:
parent
9bb553b882
commit
0b8d0e2426
37
README.md
37
README.md
@ -1060,13 +1060,7 @@ argparse::ArgumentParser program("test");
|
||||
|
||||
program.add_argument("input")
|
||||
.default_value(std::string{"baz"})
|
||||
.action([](const std::string& value) {
|
||||
static const std::vector<std::string> choices = { "foo", "bar", "baz" };
|
||||
if (std::find(choices.begin(), choices.end(), value) != choices.end()) {
|
||||
return value;
|
||||
}
|
||||
return std::string{ "baz" };
|
||||
});
|
||||
.choices("foo", "bar", "baz");
|
||||
|
||||
try {
|
||||
program.parse_args(argc, argv);
|
||||
@ -1083,7 +1077,34 @@ std::cout << input << std::endl;
|
||||
|
||||
```console
|
||||
foo@bar:/home/dev/$ ./main fex
|
||||
baz
|
||||
Invalid argument "fex" - allowed options: {foo, bar, baz}
|
||||
```
|
||||
|
||||
Using choices also works in integer types, e.g.,
|
||||
|
||||
```cpp
|
||||
argparse::ArgumentParser program("test");
|
||||
|
||||
program.add_argument("input")
|
||||
.default_value(0)
|
||||
.choices(0, 1, 2, 3, 4, 5);
|
||||
|
||||
try {
|
||||
program.parse_args(argc, argv);
|
||||
}
|
||||
catch (const std::exception& err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
std::cerr << program;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
auto input = program.get("input");
|
||||
std::cout << input << std::endl;
|
||||
```
|
||||
|
||||
```console
|
||||
foo@bar:/home/dev/$ ./main 6
|
||||
Invalid argument "6" - allowed options: {0, 1, 2, 3, 4, 5}
|
||||
```
|
||||
|
||||
## Using `option=value` syntax
|
||||
|
@ -350,6 +350,17 @@ std::string join(StrIt first, StrIt last, const std::string &separator) {
|
||||
return value.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct can_invoke_to_string {
|
||||
template <typename U>
|
||||
static auto test(int) -> decltype(std::to_string(std::declval<U>()), std::true_type{});
|
||||
|
||||
template <typename U>
|
||||
static auto test(...) -> std::false_type;
|
||||
|
||||
static constexpr bool value = decltype(test<T>(0))::value;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
enum class nargs_pattern { optional, any, at_least_one };
|
||||
@ -408,6 +419,14 @@ public:
|
||||
|
||||
template <typename T> Argument &default_value(T &&value) {
|
||||
m_default_value_repr = details::repr(value);
|
||||
|
||||
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
||||
m_default_value_str = std::string{std::string_view{value}};
|
||||
}
|
||||
else if constexpr (details::can_invoke_to_string<T>::value) {
|
||||
m_default_value_str = std::to_string(value);
|
||||
}
|
||||
|
||||
m_default_value = std::forward<T>(value);
|
||||
return *this;
|
||||
}
|
||||
@ -525,12 +544,19 @@ public:
|
||||
return nargs(nargs_pattern::any);
|
||||
}
|
||||
|
||||
void add_choice(const std::string &choice) {
|
||||
template <typename T>
|
||||
void add_choice(T&& choice) {
|
||||
static_assert(std::is_convertible_v<T, std::string_view> || details::can_invoke_to_string<T>::value, "Choice is not convertible to string_type");
|
||||
if (!m_choices.has_value()) {
|
||||
/// create it
|
||||
m_choices = std::vector<std::string>{};
|
||||
}
|
||||
m_choices.value().push_back(choice);
|
||||
|
||||
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
||||
m_choices.value().push_back(std::string{std::string_view{std::forward<T>(choice)}});
|
||||
}
|
||||
else if constexpr (details::can_invoke_to_string<T>::value) {
|
||||
m_choices.value().push_back(std::to_string(std::forward<T>(choice)));
|
||||
}
|
||||
}
|
||||
|
||||
Argument &choices() {
|
||||
@ -540,18 +566,10 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
Argument &choices(const std::string &first, T &...rest) {
|
||||
add_choice(first);
|
||||
choices(rest...);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... T> Argument &choices(const char *first, T &...rest) {
|
||||
add_choice(first);
|
||||
choices(rest...);
|
||||
|
||||
template <typename T, typename... U>
|
||||
Argument &choices(T&& first, U&&...rest) {
|
||||
add_choice(std::forward<T>(first));
|
||||
choices(std::forward<U>(rest)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -560,7 +578,7 @@ public:
|
||||
const auto &choices = m_choices.value();
|
||||
|
||||
if (m_default_value.has_value()) {
|
||||
if (std::find(choices.begin(), choices.end(), m_default_value_repr) ==
|
||||
if (std::find(choices.begin(), choices.end(), m_default_value_str) ==
|
||||
choices.end()) {
|
||||
// provided arg not in list of allowed choices
|
||||
// report error
|
||||
@ -1152,6 +1170,7 @@ private:
|
||||
std::string m_metavar;
|
||||
std::any m_default_value;
|
||||
std::string m_default_value_repr;
|
||||
std::optional<std::string> m_default_value_str; // used for checking default_value against choices
|
||||
std::any m_implicit_value;
|
||||
std::optional<std::vector<std::string>> m_choices{std::nullopt};
|
||||
using valued_action = std::function<std::any(const std::string &)>;
|
||||
|
@ -67,4 +67,25 @@ TEST_CASE("Parse multiple arguments one of which is not in the fixed number of "
|
||||
program.parse_args({"test", "red", "green2"}),
|
||||
"Invalid argument \"green2\" - allowed options: {red, green, blue}",
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Parse multiple arguments that are in the fixed number of allowed INTEGER choices" *
|
||||
test_suite("choices")) {
|
||||
argparse::ArgumentParser program("test");
|
||||
program.add_argument("indices").nargs(2).choices(1, 2, 3, 4, 5);
|
||||
|
||||
program.parse_args({"test", "1", "2"});
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Parse multiple arguments that are not in fixed number of allowed INTEGER choices" *
|
||||
test_suite("choices")) {
|
||||
argparse::ArgumentParser program("test");
|
||||
program.add_argument("indices").nargs(2).choices(1, 2, 3, 4, 5);
|
||||
|
||||
REQUIRE_THROWS_WITH_AS(
|
||||
program.parse_args({"test", "6", "7"}),
|
||||
"Invalid argument \"6\" - allowed options: {1, 2, 3, 4, 5}",
|
||||
std::runtime_error);
|
||||
}
|
Loading…
Reference in New Issue
Block a user