Search code examples
c++boostc++11boost-program-options

Can Boost Program_options separate comma separated argument values


If my command line is:

> prog --mylist=a,b,c

Can Boost's program_options be setup to see three distinct argument values for the mylist argument? I have configured program_options as:

namespace po = boost::program_options;
po::options_description opts("blah")

opts.add_options()
    ("mylist", std::vector<std::string>>()->multitoken, "description");

po::variables_map vm;
po::store(po::parse_command_line(argc, argv, opts), vm);
po::notify(vm);

When I check the value of the mylist argument, I see one value as a,b,c. I'd like to see three distinct values, split on comma. This works fine if I specify the command line as:

> prog --mylist=a b c

or

> prog --mylist=a --mylist=b --mylist=c

Is there a way to configure program_options so that it sees a,b,c as three values that should each be inserted into the vector, rather than one?

I am using boost 1.41, g++ 4.5.0 20100520, and have enabled c++0x experimental extensions.

EDIT:

The accepted solution works but ends up being more complicated, IMO, than just iterating through a vector and splitting the values manually. In the end, I took the suggestion from James McNellis and implemented it that way. His solution wasn't submitted as an answer, however, so I accepted the other correct solution from hkaiser. Both worked, but the manual tokenization is clearer.


Solution

  • You could register a custom validator for your option:

    namespace po = boost::program_options;
    
    struct mylist_option 
    {
        // values specified with --mylist will be stored here
        vector<std::string> values;
    
        // Function which validates additional tokens from command line.
        static void
        validate(boost::any &v, std::vector<std::string> const &tokens)
        {
            if (v.empty())
                v = boost::any(mylist_option());
    
            mylist_option *p = boost::any_cast<mylist_option>(&v);
            BOOST_ASSERT(p);
    
            boost::char_separator<char> sep(",");
            BOOST_FOREACH(std::string const& t, tokens)
            {
                if (t.find(",")) {
                    // tokenize values and push them back onto p->values
                    boost::tokenizer<boost::char_separator<char> > tok(t, sep);
                    std::copy(tok.begin(), tok.end(), 
                        std::back_inserter(p->values));
                }
                else {
                    // store value as is
                    p->values.push_back(t);
                }
            }
        }
    };
    

    which then can be used as:

    opts.add_options()                 
        ("mylist", po::value<mylist_option>()->multitoken(), "description");
    

    and:

    if (vm.count("mylist"))
    {
        // vm["mylist"].as<mylist_option>().values will hold the value specified
        // using --mylist
    }