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 spellstd::arrayis not supported (and is tricky to support, because it's not a container that can grow)std::listis 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 otherwiseget<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.