argparse
argparse copied to clipboard
What if actions want to return std::vector?
If an option wants to parse a list of values --foo 1,4,2,6
, then it cannot return std::vector<int>
because this type is considered a container. We probably will never want to parse a list like that (because there are so many good parsing libraries for structural data), so we cannot return the existing containers functionality to "make get<std::vector<T>>()
work" for this demand.
We should consider changing the multivalue argument support. Some observations:
- Blessing types takes away possible return types
-
std::vector<std::string>
is verbose to spell -
std::array
is not supported (and is tricky to support, because it's not a container that can grow) -
std::list
is probably never used for lots of reasons
Inventing a new set of API is an option, but the present
API (#66) will double the number of names. I think the situation can be improved by blessing a type that is both impossible to be stored in std::any
, and is terse to write -- array type.
Here is how the new get
may look like:
-
get<T[]>
: returnsstd::vector<T>
-
get<T[N]>
: returnsstd::array<T, N>
ifN
== nargs, throws otherwise -
get<T>
: returnsT
The old spelling get<std::vector<std::string>>
becomes get<std::string[]>
after the change. To not to break everything immediately, we can make the old spelling [[deprecated]]
.
FAQ
- Why
T[]
andT[N]
return entirely different types? I hope users can interpret[]
as "returning a type withoperator[]
." - Why not other kinds of containers? If users want they can append to anything with void actions. We are providing a good default here.
- Why not
std::span
(of static extent or dynamic extent)? Technical issue, we only have storage forstd::vector<std::any>
rather thanstd::vector<T>
.
Actions were designed to be similar to the visitor pattern in ANTLR - visit each argument/fragment, make changes given some context and save it. The parameter to .action() is a const string&
representing each fragment, e.g.,
$./program --foo 1 4 3 6
Here, action receives 1
, then 4
, then 3
, and finally 6
. Inside the visitor, I can convert each fragment to whatever type I want, e.g., MyStringWrapper(arg) and return it.
How does this change in your proposal? What is the argument to .action(). Is it now a vector of strings if nargs > 1?
I know this part, but what if users want to do this:
program.action([](std::string const& s) {
return std::vector<int>(std::stoi(s)); // let's say it's a vector of N random numbers
});
Then the user cannot use program.get<std::vector<int>>("...");
to get the value, because that ends up in the container case, and the value type is deemed int
. The "real" value type is std::vector<int>
, so bad_any_cast
will be thrown.
Understood. Just need some clarifications:
What is the argument s
in your example? Let's say the use-case is --foo 1 2 3 4
Then is s == 1
? What is the action returning? The vector {1}
then the vector {2}
and so on?
.. Or is s == "1 2 3 4"
which is the whole argument and the action returns {1, 2, 3, 4}
? - This is clearly not the case per your example but it might be the intention.
Just need to clarify the semantics here.
s
is "1" in --foo 1
. It's "1" then "2" in --foo 1 2
but I don't meant to bring in nargs > 1. It's an nargs == 1 argument, but wants to return a std::vector
.
Ah I see now. Okay, makes sense.
Parsing a list of values is now supported with .nargs(...)
. See here for more details. Closing this now.