Search code examples
c++boostboost-program-options

How to determine if an argment was provided to boost::program_options, or if the default is used instead?


Using boost::program_options, I need to set the default_value for having that default visible in the help message. However, it is still needed to know if the default was applied or if an argument was provided. (The logic will, in certain cases override an existing configuration if the argument pas provided).

Possible solutions:

  • Remove default values: but then the help message will not show the default.

  • Find a way to check if an argument was provided (How?)

  • Create the option_description twice (one with defaults, another without). Not ideal.

    int main( int argc, char* argv[])
    {
        namespace po = boost::program_options;
        namespace fs = boost::filesystem;
    
        std::string ip;
        std::string port;
    
        const std::string ip_arg = "ip";
        const std::string port_arg = "ip";
    
        po::options_description options("Options");
        options.add_options()
        (ip_arg, po::value<std::string>(&ip, "IP")->default_value("127.0.0.1")
        (port_arg, po::value<std::string>(&port, "Port")->default_value("80"))
        ;
    
        po::variables_map vm;
        try
        {
            using bs = po::command_line_style::style_t;
    
            boost::program_options::store(
            boost::program_options::command_line_parser(argc, argv)
                .options(options)
                .style(bs::long_allow_next | bs::allow_long | bs::allow_short | bs::allow_dash_for_short | 
                     bs::long_allow_adjacent | bs::short_allow_adjacent | bs::short_allow_next | bs::allow_long_disguise)
                .run(),
                vm);
    
            po::notify(vm);
        }
        catch( ...)
        {
            std::cerr << "Some error" << std::endl;
            return 1;
        }
    
        std::stringstream ss;
        ss << options;
        std::cout << ss.str() << std::endl; // Print help, visible defaults
    
        if (vm.count(port_arg))
        {
            // Argument was provided, so make something
        }
        else
        {
            // Argument was no provided, make something else.
        }
    
        return 0;
    }
    

How can I detect if an argument was provided, or if the default was applied?


Solution

  • Your options descriptions are broken. Let's fix them. I opted against the ip_arg/port_arg variables (note how you had them copy pasted wrong anyways).

    po::options_description options("Options");
    options.add_options()
        ("ip", po::value<std::string>(&ip)->default_value("127.0.0.1"), "IP") //
        ("port", po::value<std::string>(&port)->default_value("80"), "Port")
     ;
    

    Now you can be sure that port is always set, so .count() or .contains() are redundant. Instead, ask the map entry whether it has been defaulted:

    if (vm["port"].defaulted()) {
        std::cout << "Port defaulted (" << port << ")\n";
    } else {
        std::cout << "Port specified as " << port << "\n";
    }
    

    Live Demo

    Live On Coliru

    #include <boost/filesystem.hpp>
    #include <boost/program_options.hpp>
    #include <iostream>
    namespace po = boost::program_options;
    namespace fs = boost::filesystem;
    
    int main( int argc, char* argv[])
    {
        std::string ip;
        std::string port;
    
        po::options_description options("Options");
        options.add_options()
            ("ip", po::value<std::string>(&ip)->default_value("127.0.0.1"), "IP") //
            ("port", po::value<std::string>(&port)->default_value("80"), "Port")
         ;
    
        po::variables_map vm;
        try {
            using bs = po::command_line_style::style_t;
    
            boost::program_options::store(
                boost::program_options::command_line_parser(argc, argv)
                    .options(options)
                    .style(bs::long_allow_next | bs::allow_long | bs::allow_short |
                           bs::allow_dash_for_short | bs::long_allow_adjacent |
                           bs::short_allow_adjacent | bs::short_allow_next |
                           bs::allow_long_disguise)
                    .run(),
                vm);
    
            po::notify(vm);
        } catch (...) {
            std::cerr << "Some error" << std::endl;
            return 1;
        }
    
        std::cout << options << std::endl;
    
        if (vm["port"].defaulted()) {
            std::cout << "Port defaulted (" << port << ")\n";
        } else {
            std::cout << "Port specified as " << port << "\n";
        }
    }
    

    Prints e.g.

    $ ./test --ip=192.168.1.2
    Options:
      --ip arg (=127.0.0.1) IP
      --port arg (=80)      Port
    
    Port defaulted (80)
    

    or

    $ ./test --port=8080
    Options:
      --ip arg (=127.0.0.1) IP
      --port arg (=80)      Port
    
    Port specified as 8080