CLI11 icon indicating copy to clipboard operation
CLI11 copied to clipboard

Add modifier to treat options as vectors

Open inkychris opened this issue 3 years ago • 2 comments

The only way to make an option return a vector appears to be via variable assignment to a vector. The subcommands in files example shows use of std::shared_ptr to manage the scope of this variable; however, lambda capture of shared pointers causes a shared pointer cycle.

I've been using auto val = cmd->get_option("val")->as<TYPE>();, capturing only the command into the lambda for other types, but this does not appear to be possible for vector options:

void register_read_cmd(CLI::App& parent) {
    auto cmd = parent.add_subcommand("read", "parse input file(s)");

    cmd->add_option("input", "file(s) to parse")
        ->required()
        ->check(CLI::ExistingFile);

    cmd->callback([cmd]() {
        auto input_files = cmd->get_option("input")->as<std::vector<std::string>>();
        read_cmd(input_files);
    });
}

The code above results in The following argument was not expected, quoting the 2nd argument provided.

The shared pointer solution demo'd in the example does work, but of course with the shared pointer cycle issue:

void register_read_cmd(CLI::App& parent) {
    auto cmd = parent.add_subcommand("read", "parse input file(s)");

    auto input_files = std::make_shared<std::vector<std::string>>();
    cmd->add_option("input", *input_files, "file(s) to parse")
        ->required()
        ->check(CLI::ExistingFile);

    cmd->callback([input_files]() {
        read_mqb_cmd(*input_files);
    });
}

I would expect that using expected(1, ...) on the option would switch it into "vector mode".

A workaround for managing scope would be to subclass App and use member variables instead, or use static variables.

inkychris avatar Jun 21 '22 11:06 inkychris

If you want the first one to think it is a vector then you can

void register_read_cmd(CLI::App& parent) {
    auto cmd = parent.add_subcommand("read", "parse input file(s)");

    cmd->add_option("input", "file(s) to parse")
        ->required()
        ->check(CLI::ExistingFile)
       ->expected(CLI::details::expected_max_vector_size); //this will trigger any vector like changes

    cmd->callback([cmd]() {
        auto input_files = cmd->get_option("input")->as<std::vector<std::string>>();
        read_cmd(input_files);
    });
}

phlptp avatar Jun 21 '22 12:06 phlptp

Ah thanks, I saw that option in the Option *expected(int value_min, int value_max) definition too. Looks like the key here is actually allow_extra_args(). This avoids needing to worry about accessing the CLI::detail namespace. Maybe I can update this issue to add some clarity to the docs. Looks like the option does already exist!

inkychris avatar Jun 21 '22 12:06 inkychris