CLI11 icon indicating copy to clipboard operation
CLI11 copied to clipboard

Enable a set of flags if one specific flag is enabled

Open aangelos28 opened this issue 5 years ago • 9 comments

Hello,

Assume I have flags -a, -b, -c, -d.

Now let's say that I want to create a flag -Z. When provided, this flag should also enable flags -b and -c. This is similar for example to the -a flag in rsync, which automatically enables certain other flags. From the rsync man page: -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)

Is there any functionality in CLI11 to facilitate this?

aangelos28 avatar Mar 03 '20 16:03 aangelos28

If I am reading your question correctly you want to define a flag -Z which essentially acts like you specified -b -c.

If that is what you mean there is no prebuilt functionality for it but you can add it using a preparse_callback. For example

auto b=app.add_flag("-b",bflag,"b flag");
auto c=app.add_flag("-c",cflag,"c flag");

auto triggerGroup=app.add_option_group("Zgroup");
triggerGroup->add_flag("-Z");
triggerGroup->preparse_callback([a,b](std::size_t){b->add_result("true"); c->add_result("true");});

The preparse callback gets executed immediately after the -Z flag is specified in this case. I have never tried it but I don't know why it wouldn't work.

I might add a test case for this soon, and maybe consider making a function like TriggerOn or deprecate

phlptp avatar Mar 03 '20 17:03 phlptp

Hi Philip,

The method you proposed works. However, I would like the -Z flag to appear like a regular flag in the help menu and not show the "[Option Group: Zgroup]" above it.

How can this be done?

I would like to treat it as a regular flag and assign it to visual groups etc. If there was a preparse_callback function for regular flags that would make this trivial without having to use option groups.

Thank you.

aangelos28 avatar Mar 03 '20 18:03 aangelos28

I might look at this a little more later on to see if there are things that can be done to make this simpler in the future. In the mean time

 CLI::Option *b;
    CLI::Option *c;
    app.add_flag_callback("-Z", [&b, &c]() {b->add_result("true"); c->add_result("true"); }, "equivalent to -b -c");
    bool bval{ false };
    bool cval{ false };
        b = app.add_flag("-b", bval, "set b to true");
        c = app.add_flag("-c", cval, "set c to true");

        args = { "-Z"};
        run();
        EXPECT_TRUE(bval);
        EXPECT_TRUE(cval);

I tried out this test, It relies on ordering of the options. So the callback for the Z flag would have to come before the callbacks for the others. So this does work, not the most elegant of solutions but I am not sure a general facility to order option callbacks would be all that wise either since option groups can already do that to some extent. But perhaps a means of making the option group options in the help show up like other options is in order.

phlptp avatar Mar 03 '20 19:03 phlptp

Here is an alternative using the app callback

 bool bval{false};
    bool cval{false};
    auto b = app.add_flag("-b", bval, "set b to true");
    auto c = app.add_flag("-c", cval, "set c to true");
    app.add_flag("-Z", "equivalent to -b -c");

    auto tapp = &app;
    app.final_callback([b, c, tapp]() {
        if((*tapp)["-Z"]) {
            b->add_result("true")->run_callback();
            c->add_result("true")->run_callback();
        }
    });
    args = {"-Z"};
    run();
    EXPECT_TRUE(bval);
    EXPECT_TRUE(cval);

phlptp avatar Mar 03 '20 20:03 phlptp

Thank you for writing these solutions.

The last solution does not seem to work if there is an option that needs one of the options enabled by flag -Z. So if there is a flag -k that needs flag -b, if I provide flag -Z, flag -k will cause an error because it requires -b, even though it is enabled by flag -Z.

I assume this is because final_callback() executes in the end, after the phase where needs, excludes, etc are processed.

aangelos28 avatar Mar 03 '20 21:03 aangelos28

You might try to use the parse_complete_callback that executes a bit earlier in the process.

Maybe there is a use case for a fourth callback that executes after the arguments have been parsed but before the the options are actually processed and checked, or maybe parse_complete should be there and a new one where parse complete actually is now.

phlptp avatar Mar 03 '20 21:03 phlptp

I am guessing that is not going to work either, since I am pretty sure that still runs after the option callbacks.

phlptp avatar Mar 03 '20 21:03 phlptp

Yes, parse_complete_callback does not work either.

I think it would be great to have a callback that occurs after parsing but before option processing or checking. It could also allow for custom, more advanced option validation logic if a user wishes to provide some (for example check if two different float options when divided have a result less than 0.5 and provide custom error messages etc).

aangelos28 avatar Mar 04 '20 20:03 aangelos28

4 callbacks 1). pre_parse - After the first argument is called in the app/subcommand 2). [NEW] post_parse (maybe pre_processing)- After parsing, but before option callbacks and checks are processed 3). parse_complete - After option_processing before subcommand callbacks 4). final_callback - Last thing done before exiting

That would allow some new and interesting possibilities.
It will probably be a little while before I get to that though, there is a bit of a backlog until @henryiii is able to devote some time again.

phlptp avatar Mar 04 '20 20:03 phlptp