Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disallow negative argument for unsigned value with boost::program_options

Suppose I have a program using boost::program_options to parse command line arguments, and one has an unsigned value:

#include <boost/program_options.hpp>
#include <iostream>

namespace po = boost::program_options;

int main(int argc, char* argv[]) {
    unsigned num;
    po::options_description desc;
    desc.add_options()
        ("num,n", po::value<unsigned>(&num), "Non-negative number");
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);
    std::cout << "Number: " << num << '\n';
    return 0;
}

Then if I pass a negative value on the command line, it accepts it and wraps it around:

$ ./bponeg -n -1
Number: 4294967295

I would rather that negative numbers trigger an error (i.e. throw invalid_option_value) the same as it would if I wrote ./bponeg -n abc. From my own code after the parsing, it seems impossible to distinguish between the case where the user wrote -1 or if they wrote 4294967295.

Can I instruct the program_options parser to reject negative inputs for unsigned values?

like image 306
Nick Matteo Avatar asked Mar 12 '23 12:03

Nick Matteo


1 Answers

You could write a custom validator, which would be something like:

struct nonnegative {
    unsigned value;
};

void validate(boost::any& v, const std::vector<std::string>& values, nonnegative*, int)
{
    using namespace boost::program_options;
    validators::check_first_occurrence(v);

    std::string const& s = validators::get_single_string(values);
    if (s[0] == '-') {
        throw validation_error(validation_error::invalid_option_value);
    }

    v = lexical_cast<unsigned>(s);
}

And then just use a nonnegative instead of an unsigned.

But I think it'd be easier to just use an int64_t and throw an error if it's negative. Also less code.

like image 167
Barry Avatar answered Apr 09 '23 00:04

Barry