52

I have two related questions:

  1. What is the simplest way to allow passing a series of values, using Boost Program Options? My aim is to avoid prog --opt 1 --opt 2 --opt 3 and have prog --opt 1 2 3 instead.

  2. What is the simplest way to have an option that takes exactly two numbers, e.g. prog --opt 137 42?

(I don't need any "free" program parameters.)

Szabolcs
  • 24,728
  • 9
  • 85
  • 174

2 Answers2

51

This is a late answer but I hope it helps someone. You could easily use the same technique in item #1, except you need to add another validation on the number of items in your vector:

from rcollyer's example:

namespace po = boost::program_options;
po::option_descriptions desc("");

desc.add_options()
 ("opt", po::value<std::vector<int> >()->multitoken(), "description");

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

vector<int> opts;
if (!vm["opt"].empty() && (opts = vm["opt"].as<vector<int> >()).size() == 2) {
  // good to go
}
greydet
  • 5,509
  • 3
  • 31
  • 51
Bee
  • 2,472
  • 20
  • 26
  • 4
    You can remove that space again if coding according to the C++11 standard, it's officially supported there (and in any major compiler out there, e.g. gcc>=4.6). VC has had this included for some years as a special extension. – Ela782 Feb 12 '14 at 10:58
36

For the first part, this should work

namespace po = boost::program_options;
po::option_descriptions desc("");

desc.add_options()
 ("opt", po::value<std::vector<int> >()->multitoken(), "description");

The second part, requires a bit more work. The function po::value returns a po::typed_value< T, charT > on which you'll have to override the behavior of several functions, as follows

template< typename T, typename charT = char >
class fixed_tokens_typed_value : public po::typed_value< T, charT > {
   unsigned _min, _max;

   typedef po::typed_value< T, charT > base;

 public:

   fixed_tokens_typed_value( T * t, unsigned min, unsigned max ) 
     : _min(min), _max(max), base( t ) {
       base::multitoken();
   }

   virtual multi_typed_value* min_tokens( unsigned min ) {
       _min = min;
       return *this;
   }
   unsigned min_tokens() const {return _min;}

   virtual multi_typed_value* max_tokens( unsigned max ) {
       _max = max;
       return *this;
   }
   unsigned max_tokens() const {return _max;}

   base* zero_tokens() {
       _min = _max = 0;
       base::zero_tokens();
       return *this;
   }
}

which needs to be accompanied by

template< typename T >
fixed_tokens_typed_value< T > 
fixed_tokens_value(unsigned min, unsigned max) {
    return fixed_tokens_typed_value< T >(0, min, max ); }

template< typename T >
fixed_tokens_typed_value< T > 
fixed_tokens_value(T * t, unsigned min, unsigned max) {
    fixed_tokens_typed_value< T >* r = new
                   fixed_tokens_typed_value< T >(t, min, max);
    return r; }

Then

desc.add_options()
 ("opt", po::fixed_tokens_value<std::vector<int> >(2,2), "description");

should work. I have not yet had a chance to test it, so it likely contains a few bugs. But, at a minimum, should give you an idea of what you need.

rcollyer
  • 10,475
  • 4
  • 48
  • 75
  • @Szabolcs, an interesting point with this code is that it would require the use of vectors by default. Otherwise, how are you going to store the values? So, I think `po::typed_value< T, charT >` should be changed to `po::typed_value< std::vector< T >, charT >`. – rcollyer Nov 18 '11 at 19:25
  • Does this also work for `std::array`s or `std::lists`s rather than vectors? – einpoklum May 11 '16 at 13:57
  • @einpoklum I don't see why not. But, I think `std::array` will be a little tricky as I don't know what is used to insert into the container, although that is likely configurable, and its fixed size would require some adaptation. In contrast, `std::list` does not have those limitations, so I suspect you could use it as a drop in replacement. – rcollyer May 11 '16 at 14:10