Home > Back-end >  boost::program_options multitoken value from file
boost::program_options multitoken value from file

Time:06-30

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

Live On 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::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

  • Related