p-ranav / argparse

Argument Parser for Modern C++
MIT License
2.59k stars 244 forks source link

Parser can't deal with optional argument with varying number of values, when it is just before first positional argument #335

Open rouault opened 5 months ago

rouault commented 5 months ago

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:

rouault commented 5 months ago

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]...