skip to Main Content

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 😉

2

Answers


  1. Chosen as BEST ANSWER

    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
    
    

  2. 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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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; 
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search