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

force_callback prevents subcommands with excludes from running #1002

Closed
Demonslay335 opened this issue Feb 6, 2024 · 2 comments
Closed

force_callback prevents subcommands with excludes from running #1002

Demonslay335 opened this issue Feb 6, 2024 · 2 comments

Comments

@Demonslay335
Copy link

While adding a new subcommand to our app and using excludes to setup mutual exclusivity with the other subcommands, we found the subcommands refuse to run, even when only one was specified at command line.

This seems to happen when force_callback is used, which sets a variable on the subcommand before the exclusion tests are performed.

Here is a minimal app to reproduce the issue. subcommand_1 has a forced callback, and prevents subcommand_2 to run, despite no parameters being passed for subcommand_1.

#include <iostream>
#include "CLI11.hpp" // v2.4.0

int main(int argc, char** argv)
{
    CLI::App app{"CLI11-excludes-bug"};
    
    auto subcommand_1 = app.add_subcommand("subcommand_1", "subcommand 1");
    subcommand_1
        ->add_flag_function(
            "-f",
            [](bool f) {
                std::cout << "subcommand_1 callback " << f << std::endl;
            },
            "subcommand_1 flag")
        ->force_callback();

    auto subcommand_2 = app.add_subcommand("subcommand_2", "subcommand 2");

    subcommand_1->excludes(subcommand_2);
    
    CLI11_PARSE(app, argc, argv);
    return 0;
}

Expected output:

>CLI11-excludes-bug.exe subcommand_2
subcomand_1 callback 0
[Exit Code 0]

Actual output:

>CLI11-excludes-bug.exe subcommand_2
subcommand_1 callback 0
subcommand_2 excludes subcommand_1
[Exit Code 108]

Basically, CLI11 is processing the forced callback for subcommand_1 first, which is setting the flag. Then, the requirements for subcommand_2 are processed; when it reaches the exclusion for subcommand_1, it sees a variable is set, and believes it is a conflict.

for(const auto &subc : exclude_subcommands_) {
if(subc->count_all() > 0) {
excluded = true;
excluder = subc->get_display_name();
}
}

I don't know the best way of fixing this in the library, other than perhaps marking the variable as being set by a forced callback (and not explicitly set by the user/program otherwise), and ignore those when counting the variables only for the exclusion check?

Our workaround has involved removing the flag lambda entirely, and putting the logic into app.parse_complete_callback() instead. This works, but decouples the flag logic a bit.

@Demonslay335 Demonslay335 changed the title force_callback prevents excluded subcommands from running force_callback prevents subcommands with excludes from running Feb 6, 2024
@phlptp
Copy link
Collaborator

phlptp commented Aug 5, 2024

I will be looking at this in the near future

@phlptp
Copy link
Collaborator

phlptp commented Oct 10, 2024

So apparently this was resolved by #1060, and didn't know it. Was adding a test and couldn't get it to fail. So dug in a bit more and some of the changes for handling round trips on config files, found by the fuzzer, resolved this issue as well.
I added this test

TEST_CASE_METHOD(TApp, "ForcedSubcommandExclude", "[subcom]") {
    auto *subcommand_1 = app.add_subcommand("sub_1");
    std::string forced;
    subcommand_1
        ->add_flag_function(
            "-f",
            [&forced](bool f) {
                forced=f?"got true":"got false";
            })
        ->force_callback();

    auto subcommand_2 = app.add_subcommand("sub2");

    subcommand_1->excludes(subcommand_2);

    args={"sub2"};
    CHECK_NOTHROW(run());
    CHECK(forced=="got false");
}

@phlptp phlptp closed this as completed Oct 19, 2024
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