Reputation: 549
I'm trying to parse unregistered options in any number of files provided at command line. Let's say I have files:
configs0.ini
configs1.ini
configs2.ini
And I wanted to support any number of these.
My code (simplified):
namespace po = boost::program_options;
po::options_description cmd_opts{"Options"};
po::options_description config_file_opts;
po::variables_map vm;
cmd_opts.add_options()
("help,h", "Help message")
("config_files", po::value<std::vector<std::string>>()->multitoken(), "Configuration files to get settings from")
po::parser.options(reg_config_file_opts).allow_unregistered();
po::store(parse_command_line(argc, argv, cmd_opts), vm);
config_files = vm["config_files"].as<std::vector<std::string>>();
po::parsed_options parsed_opts;
for(auto file : config_files) {
std::ifstream ifs(file, std::ifstream::in);
if(ifs.fail()) {
std::cerr << "Error opening config file: " << file << std::endl;
return false;
}
ifs.close();
<NEED HELP HERE>
parsed_opt.add(parse_config_file(ifs, reg_config_file_opts));
}
po::store(parsed_opts, vm);
Does parsed_options have some sort of .add ability?
Upvotes: 1
Views: 625
Reputation: 393114
The library distinguishes three layers:
The design assumes that you will be using one or more sets of descriptions with one or more parsers, and combine the results into one storage.
In practice this means you will store into the same variable map, and notify to update any value-semantics with side-effects.
Live Demo
File main.cpp
#include <boost/program_options.hpp>
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/config.hpp>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <fstream>
namespace po = boost::program_options;
int main() {
for (auto args : {
std::vector{"test.exe"},
std::vector{"test.exe", "--config_files", "a.cfg", "b.cfg", "c.cfg"},
})
{
int const argc = args.size();
char const** argv = args.data();
po::options_description cmd_opts{"Options"};
using values = std::vector<std::string>;
cmd_opts.add_options()
("help,h", "Help message")
("config_files", po::value<values>()->multitoken(), "Configuration files to get settings from")
;
po::variables_map vm;
po::variables_map cfg_vm; // can also reuse vm
po::store(parse_command_line(argc, argv, cmd_opts), vm);
auto& config_files = vm["config_files"];
if (!config_files.empty()) {
po::options_description config_file_opts;
config_file_opts.add_options()
("foo", po::value<values>()->composing())
("bar_a", po::value<values>()->composing())
("bar_b", po::value<values>()->composing())
("bar_c", po::value<values>()->composing())
;
for(auto file : config_files.as<values>()) try {
std::ifstream ifs(file);
po::store(parse_config_file(ifs, config_file_opts), cfg_vm);
} catch(std::exception const& e) {
fmt::print(stderr, "{}: {}\n", file, e.what());
}
fmt::print("Cmdline opts\n");
for (auto& [k, v] : vm) {
fmt::print("{}={}\n", k, v.as<values>());
}
fmt::print("Combined configs\n");
for (auto& [k, v] : cfg_vm) {
fmt::print("{}={}\n", k, v.as<values>());
}
}
}
}
File a.cfg
foo=foo_val_a
bar_a=bar_val_a
File b.cfg
foo=foo_val_b
bar_b=bar_val_b
File c.cfg
foo=foo_val_c
bar_c=bar_val_c
Prints:
Cmdline opts
config_files={"a.cfg", "b.cfg", "c.cfg"}
Combined configs
bar_a={"bar_val_a"}
bar_b={"bar_val_b"}
bar_c={"bar_val_c"}
foo={"foo_val_a", "foo_val_b", "foo_val_c"}
Upvotes: 3