Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Want to allow options to be specified multiple times when using boost program options. Right now I get multiple occurrences

I am using boost program_options 1.50.0

I want to ALLOW the following for my program foobar foobar --debug 2 --debug 3

From the boost program_options code, there is an example regex.cpp that shows creating a new type and creating a validator for that type.
I tried that, and it works, but now I cannot use some of the other add_options() typed_value options, like default_value, composing, etc.

Here is what I tried so far:

    #include <boost/program_options.hpp>

    using namespace boost;

    using namespace boost::program_options;

    #include <iostream>
    using namespace std;
    struct lastmultioccurrenceint {
        public:
         lastmultioccurrenceint(int n) : n(n) {}
         int n;
    };    

void validate(boost::any& v, 
                  const std::vector< std::string >& xs, 
                  //const std::vector< std::basic_string<charT> >& xs, 
                  lastmultioccurrenceint* , int)
    {
        using namespace boost::program_options;

        cerr << "IN VALIDATE" << endl;
        //validators::check_first_occurrence(v);
        string s = validators::get_single_string(xs);
        if (!v.empty()) {
            cerr << "\tPRINTTING MULTIOCCURENCE WARNING, allowing v to be overwritten" << endl;
            cerr << "\tEarlier value was: " <<  boost::any_cast<int>(v) << endl;
            cerr << "\tNew value is: " << s << endl;
        }
        try {
            //v = any(lastmultioccurrenceint(lexical_cast<int>(sx)));
            //v = any(lexical_cast<int>(sx)); // works
            v = any(lexical_cast<int>(s));
            //v = any(lexical_cast<lastmultioccurrenceint>(s));
            //v = any(4);
        //}
        /*catch(const bad_lexical_cast&) {
            boost::throw_exception(validation_error::invalid_option_value(s));
        } */
        }
        catch(const bad_lexical_cast&) {
            throw validation_error(validation_error::invalid_option_value);
        }
        cerr << "made it through" << endl;

        int main (int argc, char **argv) {

    variables_map m_varMap;
        // define style
        //  unix_style =  (allow_short | short_allow_adjacent | short_allow_next
        //            | allow_long | long_allow_adjacent | long_allow_next
        //            | allow_sticky | allow_guessing 
        //            | allow_dash_for_short), 
        // ... allows typical unix-style options
        // allow_long_disguise = can use "-" instead of "--"
        // Reference: http://www.boost.org/doc/libs/1_42_0/doc/html/boost/program_options/command_line_style/style_t.html
        //
    try {

    ProgOpts::command_line_style::style_t style = ProgOpts::command_line_style::style_t(
            ProgOpts::command_line_style::unix_style |
            //ProgOpts::command_line_style::case_insensitive |
            ProgOpts::command_line_style::allow_long_disguise );

    options_description options("YDD");

    //lastmultioccurrenceint debugOpt;

    options.add_options()
    ("debug", value<lastmultioccurrenceint>(), "debug value (0-4), default is 0 (performance mode)")
    //("debug", value<lastmultioccurrenceint>(&debugOpt)->default_value(0)->composing(), "debug value (0-4), default is 0 (performance mode)")
    ;

        //ProgOpts::parsed_options firstPreParsed = ProgOpts::command_line_parser(argc,argv).options(options).style(style).allow_unregistered().run();
        ProgOpts::parsed_options firstPreParsed = ProgOpts::command_line_parser(argc,argv).options(options).allow_unregistered().run();
    ProgOpts::store(firstPreParsed, m_varMap);

    ProgOpts::notify(m_varMap);
    } 
    /*catch (boost::program_options::multiple_occurrences &e) {
        cerr << "GOT MULTIPLES" << endl;
        cerr << "Option Name: " << e.get_option_name() << endl;
        cerr << e.what() << endl;
    }
    catch(boost::bad_any_cast& e) {
        cerr << "WRONG TYPE" << endl;
        cerr << e.what() << endl;
    } */
    catch(std::exception& e) {
        cerr << "SOMETHING ELSE" << endl;
        cerr << e.what() << endl;
    }
    catch(...) {
        cerr << "UNKNOWN ERROR" << endl;
    }

    cerr << "DEBUG OPT IS: " << m_varMap["debug"].as<int>() << endl;
}

So if I do: foobar --debug 2 --debug 3

If I comment out the current debug option ....

("debug", value<lastmultioccurrenceint>(), "debug value (0-4), default is 0 (performance mode)")

... and uncomment out the following two lines:

lastmultioccurrenceint debugOpt;
("debug", value<lastmultioccurrenceint>(&debugOpt)->default_value(0)->composing(), "debug value (0-4), default is 0 (performance mode)")

... then it doesn't even compile.

Do you know how to do this so that it allows me to use default_value and composing? It might be inheriting from typed_value, but I haven't found a good way to do this yet.

like image 243
Matt Frazer Avatar asked Oct 06 '12 00:10

Matt Frazer


1 Answers

I don't think you need to define a custom type with a validator to achieve the desired result. It can be done with the existing semantic information support of the library. Consider this example

#include <boost/assign/list_of.hpp>
#include <boost/program_options.hpp>
#include <boost/version.hpp>

#include <iostream>

int
main( int argc, char** argv )
{
    namespace po = boost::program_options;

    po::options_description desc("Options");

    typedef std::vector<unsigned> DebugValues;
    DebugValues debug;
    desc.add_options()
        ("help,h", "produce help message")
        ("debug", po::value<DebugValues>(&debug)->default_value(boost::assign::list_of(0), "0")->composing(), "set debug level")

        ;

    po::variables_map vm;
    try {
        const po::positional_options_description p; // note empty positional options
        po::store(
                po::command_line_parser( argc, argv).
                          options( desc ).
                          positional( p ).
                          run(),
                          vm
                          );
        po::notify( vm );

        if ( vm.count("help") ) {
            std::cout << desc << "\n";
            std::cout << "boost version: " << BOOST_LIB_VERSION << std::endl;
            return 0;
        }
    } catch ( const boost::program_options::error& e ) {
        std::cerr << e.what() << std::endl;
    }

    std::cout << "got " << debug.size() << " debug values" << std::endl;
    if ( !debug.empty() ) {
        DebugValues::const_iterator value( debug.end() );
        std::advance( value, -1 );
        std::cout << "using last value of " << *value << std::endl;
    }
}

and sample usage:

samm$ ./a.out -h
Options:
  -h [ --help ]         produce help message
  --debug arg (=0)      set debug level

boost version: 1_46_1
samm$ ./a.out --debug 1 --debug 2
got 2 debug values
using last value of 2
samm$ ./a.out --debug 4 --debug 1
got 2 debug values
using last value of 1
samm$ ./a.out --debug 4 --debug 1 --debug 9
got 3 debug values
using last value of 9
samm$ 
like image 126
Sam Miller Avatar answered Oct 21 '22 06:10

Sam Miller