Parser can't deal with optional argument with varying number of values, when it is just before first positional argument
Consider the following parser
#include "argparse/argparse.hpp"
int main(int argc, char** argv)
{
argparse::ArgumentParser parser("main");
parser.add_argument("--a").nargs(2, 3);
parser.add_argument("--b");
parser.add_argument("positional");
try {
parser.parse_args(argc, argv);
}
catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << parser;
std::exit(1);
}
std::exit(0);
}
The following invocations work:
./mytest --a 1 2 3 foo
./mytest --a 1 2 --b bar foo
./mytest --a 1 2 3 --b bar foo
but not
$ ./mytest --a 1 2 foo
positional: 1 argument(s) expected. 0 provided.
The issue is that Argument::consume() consumes the foo value when parsing --a.
I believe that in the general case, such parser will be ambiguous. It can be de-ambiguated at least (only?) in the following particular situation: when the positional arguments have a fixed number of values each.
I was also thinking about criteria based on the typing of the arguments. For example if we know that the type of a --a value is a number whereas the type of the positional argument is a non-number strings, but that gets really complicated.
So what are our potential options to resolve the issue:
- just documenting the issue in user documentation?
- add a ArgumentParser::validate() method (independent of the actual argv[] values), possibly called by parse_args() at its beginnings, that would throw in situations where it detects the set of arguments is ambiguous? That is when there are optional arguments with a varying number of values, and also positional arguments with at least one with a varying number of values.
- add an optional callback to a Argument where it can return if it recognizes the provided string as a valid value for it? (would work in situations where the values of the optional argument with a varying number of values is numeric, whereas the first positional argument can't be)
Interestingly there is a similar situation to that issue at https://github.com/p-ranav/argparse/blob/cebee4bb4b6c4ec78232a39e6c94b3a32e148ac1/test/test_optional_arguments.cpp#L167 . But this requires specifying the optional argument after the positional one(s). Which doesn't match the advertize usage: Usage: test [--help] [--version] [-s VAR...] [input]...
Hi @rouault,
- I see documenting the issue is more convenient.
- We give the user the full power, it's similar to dynamic memory allocation in C++ (User is responsible for de-allocation) before smart pointers.
- Positional arguments after Optional arguments will not always cause an issue (It's our design we shall first process positional arguments before optional ones to avoid such a scenario or forcing user to specify number of values for optional arguments).
./mytest --a 1 2 fooin this example,foowill be consumed by--boption.- Third solution, not clear for me.
@rouault @p-ranav, If documenting is ok, I can create a PR.
Has there been movement on this?
Is the alternative to use ./mytest --a 1 --a 2 --a 3 foo?