Search code examples
c++templatesboostcommand-lineboost-program-options

boost program_options custom parsing


Is there a way to have something like

myapp hostname:port

parsed by boost program_options? I'm also using other options and I would love to use boost program_options without having to roll my own parser for argc/argv.

I tried with some combinations of

desc.add_options()
    ("help", "list all available options")
    (new MyCustomValue(&store_var), "")

but it didn't work


Solution

  • As Dan Mašek writes, this looks more fitting for a positional argument. Since you specify a specific structure for the option, though, you might want to add std::regex_match.

    Say you start with

    #include <string>
    #include <iostream>
    #include <regex>
    #include <boost/program_options.hpp>
    
    using namespace std;
    using namespace boost::program_options;
    
    int main(int argc, const char *argv[]) {
        try {
            options_description desc{"Options"};
            desc.add_options()
                ("help,h", "Help screen")
                ("ip_port", value<std::string>()->required(), "ip:port");
    
            positional_options_description pos_desc;
            pos_desc.add("ip_port", -1);
    
            command_line_parser parser{argc, argv};
            parser.options(desc).positional(pos_desc).allow_unregistered();
            parsed_options parsed_options = parser.run();
    
            variables_map vm;
            store(parsed_options, vm);
            notify(vm);
    
            const auto ip_port = vm["ip_port"].as<string>();
    

    At this point, we have the user's input for ip_port. We can define a regex to match it. Note that first part can be a string (e.g., localhost), but the second part must be an integer:

            const regex ip_port_re("([^:]+):([[:digit:]]+)");
            smatch ip_port_match;
            if(!regex_match(ip_port, ip_port_match, ip_port_re)) 
                 throw validation_error{validation_error::invalid_option_value, ip_port, "ip_port"};
            cout << "ip: " << ip_port_match[1] << " port: " << ip_port_match[2] << endl;
        }
        catch (const error &ex) {
            cerr << ex.what() << '\n';
        }
    }
    

    When running it, it looks like this:

    $ g++ --std=c++11 po.cpp -lboost_program_options && ./a.out 127.0.0.1:30
    ip: 127.0.0.1 port: 30
    $ g++ --std=c++11 po.cpp -lboost_program_options && ./a.out localhost:30
    ip: localhost port: 30
    $ g++ --std=c++11 po.cpp -lboost_program_options && ./a.out localhost:30d
    the argument for option 'localhost:30d' is invalid
    $ g++ --std=c++11 po.cpp -lboost_program_options && ./a.out 
    the option '--ip_port' is required but missing
    $ g++ --std=c++11 po.cpp -lboost_program_options && ./a.out localhost:30 foo
    option '--ip_port' cannot be specified more than once