Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
rouault opened this issue Mar 16, 2024 · 3 comments

Comments

@rouault
Copy link
Contributor

rouault commented Mar 16, 2024

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)
@rouault
Copy link
Contributor Author

rouault commented Mar 16, 2024

Interestingly there is a similar situation to that issue at

WHEN("provided many arguments followed by an option with many arguments") {
. 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]...

@Eng-MohamedHussien
Copy link
Contributor

Eng-MohamedHussien commented Sep 21, 2024

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 foo in this example, foo will be consumed by --b option.
  • Third solution, not clear for me.

@Eng-MohamedHussien
Copy link
Contributor

@rouault @p-ranav,
If documenting is ok, I can create a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants