CLI11
CLI11 copied to clipboard
Config subcommand validators are enforced even when the subcommand is not requested
If I create a config for an app which contains subcommands, with default_also = true, the created config contains the default values of all options for all subcommands. When parsing this config, the Validators for this option are run, even if the subcommand is not triggered. This means the config may not be useable, as the Validators fail for options we weren't planning to use.
When writing the config, is it possible to only include the options on the active subcommand(s)? Or else to avoid parsing/processing options from config on inactive subcommand(s)?
Here's a small repro illustrating the problem, and why we have options with conflicting/unsatisfiable Validators - one subcommand requires that a file is not present, while another requires that it is present.
#include <CLI11/CLI11.hpp>
#include <fstream>
#include <iostream>
int main(int argc, char** argv)
{
CLI::App app{"repro"};
app.set_config("--config", "", "Read config", false);
app.allow_config_extras(false);
auto create = app.add_subcommand("create");
create->configurable();
std::string create_path = "./my_file.txt";
create->add_option("--create-path", create_path, "A file which must not exist")
->capture_default_str()
->check(CLI::NonexistentPath);
auto load = app.add_subcommand("load");
load->configurable();
std::string load_path = "./my_file.txt";
load->add_option("--load-path", load_path, "A file which must exist")
->capture_default_str()
->check(CLI::ExistingPath);
CLI11_PARSE(app, argc, argv);
std::cout << "Reached end of main!" << std::endl;
const auto config_file = "./config.ini";
std::cout << "Writing config to " << config_file << std::endl;
std::fstream fs;
fs.open(config_file, std::fstream::out | std::fstream::trunc);
fs << app.config_to_str(true, false);
fs.close();
return 0;
}
$ ./a.out
Reached end of main!
Writing config to ./config.ini
$ cat ./config.ini
create.create-path="./my_file.txt"
load.load-path="./my_file.txt"
$ ./a.out --config ./config.ini
--load-path: Path does not exist: ./my_file.txt
Run with --help for more information.
$ touch ./my_file.txt
$ ./a.out --config ./config.ini
--create-path: Path already exists: ./my_file.txt
Run with --help for more information.
Let me look into this a little more. I thought there was a flag that could control this but I might have to dig a little as I don't remember it right now.
By default having a subcommand section in the config file causes it to trigger which is likely what is happening
Can you try it with removing the lines
create->configurable();
and
load->configurable();
I believe those lines are instructing the config to consider those subcommands as configurable which means they will be executed if any section in the config file. with them out they update values but won't execute if not called from the command line
Thanks for the rapid response.
Removing the configurable lines didn't help, the config.ini produced is identical and both validators are still run:
$ cat main.cpp
...
auto create = app.add_subcommand("create");
//create->configurable();
std::string create_path = "./my_file.txt";
create->add_option("--create-path", create_path, "A file which must not exist")
->capture_default_str()
->check(CLI::NonexistentPath);
auto load = app.add_subcommand("load");
//load->configurable();
std::string load_path = "./my_file.txt";
...
$ ./a.out
Reached end of main!
Writing config to ./config.ini
$ cat ./config.ini
create.create-path="./my_file.txt"
load.load-path="./my_file.txt"
$ ./a.out --config ./config.ini
--load-path: Path does not exist: ./my_file.txt
Run with --help for more information.
The only difference matches what the docs describe - if I do specify a subcommand and it is configurable(), then it is added to the config. This functionality seems useful (re-triggering subcommands from config), though it can also trigger another parsing error as it puts the unrelated subcommand option under the subcommand's group (this is with configurable() enabled for both again):
$ ./a.out create
Reached end of main!
Writing config to ./config.ini
$ cat ./config.ini
[create]
create-path="./my_file.txt"
load.load-path="./my_file.txt"
$ ./a.out --config ./config.ini create
INI was not able to parse create.load.load-path
Run with --help for more information.