I am trying to set a multitoken value from a ini file.
commonOptions.add_options()("ccf.multiPort", po::value<std::vector<int>>()->multitoken(), "multi port");
parsing multitoken value from the command line works well
./main --ccf.multiPort 123 423 421
however I was not able to set the same values in an ini file
[ccf]
multiPort = 1234 2356 7745
produces the following error
terminate called after throwing an instance of 'boost::wrapexcept<boost::program_options::invalid_option_value>'
what(): the argument ('1234 2356 7745') for option 'ccf.multiPort' is invalid
I have also tried
[ccf]
multiPort = 1234,2356,7745
Boost::program_options versino 1.75 OS linux Compiler gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
Does anybody know the right format ?
Please help me to avoid digging into the source code ;)
CodePudding user response:
One way is using a custom notifier:
auto split_ports =
[&c](std::vector<std::string> const& vv) {
for (auto& v : vv) {
auto it = boost::make_split_iterator(
v, boost::token_finder(boost::algorithm::is_any_of(" ,")));
std::transform(it, {}, back_inserter(c), [](auto& s) {
return boost::lexical_cast<double>(s);
});
}
};
Which you configure as:
commonOptions.add_options()(
"ccf.multiPort",
po::value<std::vector<std::string>>()->multitoken()->notifier(
split_ports),
"multi port");
Now it works with both config file and command line args:
Demo
#include <boost/program_options.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <fstream>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char** argv) {
std::vector<uint16_t> c;
auto split_ports =
[&c](std::vector<std::string> const& vv) {
for (auto& v : vv) {
auto it = boost::make_split_iterator(
v, boost::token_finder(boost::algorithm::is_any_of(" ,")));
std::transform(it, {}, back_inserter(c), [](auto& s) {
return boost::lexical_cast<double>(s);
});
}
};
// Setup options.
po::options_description commonOptions("Options");
commonOptions.add_options()(
"ccf.multiPort",
po::value<std::vector<std::string>>()->multitoken()->notifier(
split_ports),
"multi port");
{
po::variables_map vm;
std::ifstream ifs("input.txt");
store(po::parse_config_file(ifs, commonOptions, false), vm);
po::notify(vm);
}
{
po::variables_map vm;
store(po::parse_command_line(argc, argv, commonOptions), vm);
po::notify(vm);
}
// print result
std::copy(c.begin(), c.end(), std::ostream_iterator<double>(std::cout << "c: ", "; "));
std::cout << "\n";
}
With input.txt:
[ccf]
multiPort = 1234,2356,7745
And command line:
./main --ccf.multiPort 123 423 421
Prints
c: 1234; 2356; 7745; 123; 423; 421;
Simplify
I'd simplify: Coliru
#include <boost/program_options.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <fstream>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char** argv) {
std::vector<uint16_t> c;
auto split_ports = [&c](std::string const& v) {
auto f = boost::token_finder(boost::algorithm::is_any_of(" ,"),
boost::token_compress_on);
std::transform(boost::make_split_iterator(v, f), {}, back_inserter(c),
[](auto& s) { return boost::lexical_cast<double>(s); });
};
// Setup options.
po::options_description commonOptions("Options");
commonOptions.add_options()("ccf.multiPort",
po::value<std::string>()->notifier(split_ports),
"multi port");
{
po::variables_map vm;
std::ifstream ifs("input.txt");
store(po::parse_config_file(ifs, commonOptions, false), vm);
po::notify(vm);
}
{
po::variables_map vm;
store(po::parse_command_line(argc, argv, commonOptions), vm);
po::notify(vm);
}
// print result
std::copy(c.begin(), c.end(), std::ostream_iterator<double>(std::cout << "c: ", "; "));
std::cout << "\n";
}
With
./a.out --ccf.multiPort "123 423 421"
./a.out --ccf.multiPort '123 423',421
./a.out --ccf.multiPort 123,423,421
./a.out --ccf.multiPort 123\ 423\ 421
Prints
c: 1234; 2356; 7745; 123; 423; 421;
c: 1234; 2356; 7745; 123; 423; 421;
c: 1234; 2356; 7745; 123; 423; 421;
c: 1234; 2356; 7745; 123; 423; 421;
Of course, substitute std::set
with almost no further changes:
c: 123; 421; 423; 1234; 2356; 7745;
c: 123; 421; 423; 1234; 2356; 7745;
c: 123; 421; 423; 1234; 2356; 7745;
c: 123; 421; 423; 1234; 2356; 7745;
CodePudding user response:
well ... this one works
[ccf]
multiPort = 1234
multiPort = 2356
multiPort = 7745
but is somehow ugly. Can I use something like
[ccf]
multiPort = 1234 2356 7745