2010-11-05 4 views
9

Я должен уметь иметь boost::program_options разбор массива парных номеров , которые передаются по командной строке. Конечно, для положительных удвоений это не проблема, конечно (используйте multitoken с std::vector<double> в add_options), но для отрицательных, я знаю, что это неоднозначные аргументы.Принятие отрицательных удвоений с boost :: program_options

Вот демонстрация того, что я хотел бы взять в:

 
mycommand --extent -1.0 -2.0 -3.0 1.0 2.0 3.0 --some-other-argument somevalue 

степени должна быть подкреплена Bounds класса, по крайней мере, одного конструктора , который принимает в шести отдельных T аргументов (в этот случай - double).

template <typename T> 
class Bounds 
{ 
public: 
    typedef T value_type; 
    typedef typename std::vector< Range<T> >::size_type size_type; 

    typedef typename std::vector< Range<T> > Ranges; 

    Bounds(T minx, T miny, T minz, 
      T maxx, T maxy, T maxz) 
    { 
     // fill Ranges vector 
    } 

private: 
    Ranges ranges; 
}; 

Что еще я должен предоставить для поддержки использования add_options взять в Bounds классе? Я хотел бы сделать что-то похожее на это. Возможное?

namespace po = boost::program_options; 
po::options_description options("options"); 

options.add_options() 
    ("extent,e", po::value< Bounds<double> >(), "Extent to clip points to") 

po::variables_map vm; 
po::store(po::command_line_parser(argc, argv). 
    options(options).positional(p).run(), vm); 

po::notify(vm); 

if (vm.count("extent")) 
{ 
    Bounds<double> bounds = vm["extent"].as< Bounds<double> >(); 
    // do other stuff 
} 
+1

После разговора с автором program_options на IRC было установлено, что в настоящее время это невозможно. Решение состоит в том, чтобы отключить короткие варианты или использовать кавычки. Я выбрал цитаты. –

+0

Я сообщил об этом в качестве билета, возможно, он будет решен в ближайшем будущем: https://svn.boost.org/trac/boost/ticket/5201 – mloskot

ответ

1

Подход к обработке отрицательных номеров, указанных here, может также работать на вас.

Я разборе его с помощью простого парсера

store(command_line_parser(argc, argv).options(commands).run(), vm); 

, но решение было использовать расширенный один:

parse_command_line 
+0

Примечание. Это решение основано на отключении коротких опций, поэтому парсер игнорирует все, что угодно начинается с дефиса – mloskot

+0

@mloskot - интересный, так как OP в этой ветке задал вопрос об обработке -ve, а также + ve значения. Я надеялся, что обсуждение также будет применяться здесь, но не пробовал. –

+0

Я думаю, что проблема заключается в обработке негативов как значений и одновременном использовании --option и -option. В настоящее время program_options анализирует отрицательные значения как имя опции (короткий стиль) – mloskot

1

Простой способ будет для переноса ваших параметров в кавычки: mycommand --extent '-1.0 -2.0 -3.0 1.0 2.0 3.0' - some-other-argument somevalue

6

Фокус в том, чтобы заставить boost классифицировать все числа как позиционные значения (не путать с positional_options_description. То, как вы делаете это определить style_parser и раздай command_line_parser как extra_style_parser:

#include <boost/program_options/option.hpp> 
    #include <boost/lexical_cast/try_lexical_convert.hpp> 
    #include <boost/program_options/value_semantic.hpp> 

    using po = boost::program_options; 

    std::vector<po::option> ignore_numbers(std::vector<std::string>& args) 
    { 
     std::vector<po::option> result; 
     int pos = 0; 
     while(!args.empty()) { 
      const auto& arg = args[0]; 
      double num; 
      if(boost::conversion::try_lexical_convert(arg, num)) { 
       result.push_back(po::option()); 
       po::option& opt = result.back(); 

       opt.position_key = pos++; 
       opt.value.push_back(arg); 
       opt.original_tokens.push_back(arg); 

       args.erase(args.begin()); 
      } else { 
       break; 
      } 
     } 

     return result; 
    } 

После того, как у вас есть, это то, как вы его используете:

po::store(po::command_line_parser(argc, argv) 
     .extra_style_parser(&po::ignore_numbers) 
     .options(commands) 
     .run(), vm); 

Теперь вы можете использовать отрицательный номера и короткие аргументы командной строки одновременно.

Однако все еще существует проблема, нет возможности ограничить количество токенов, принимаемых каждым аргументом, что может быть проблематичным, если вы используете позиционные аргументы.Например, что-то вроде этого не будет работать:

foo --coords 1 2 3 4 bar.baz 

Для того, чтобы исправить это, нам нужно добавить способ заставить количество жетонов аргумента требует:

template<class T, class charT = char> 
    class bounded_typed_value : public po::typed_value<T, charT> 
    { 
    public: 
     bounded_typed_value(T* store_to) 
      : typed_value<T, charT>(store_to), m_min(-1), m_max(-1) {} 

     unsigned min_tokens() const { 
      if(m_min < 0) { 
       return po::typed_value<T, charT>::min_tokens(); 
      } else { 
       return (unsigned)m_min; 
      } 
     } 

     unsigned max_tokens() const { 
      if(m_max < 0) { 
       return po::typed_value<T, charT>::max_tokens(); 
      } else { 
       return (unsigned)m_max; 
      } 
     } 

     bounded_typed_value* min_tokens(unsigned min_tokens) 
     { 
      if(min_tokens > 1) { 
       po::typed_value<T, charT>::multitoken(); 
      } 

      m_min = min_tokens; 

      return this; 
     } 

     bounded_typed_value* max_tokens(unsigned max_tokens) 
     { 
      if(max_tokens > 1) { 
       po::typed_value<T, charT>::multitoken(); 
      } 

      m_max = max_tokens; 

      return this; 
     } 

     bounded_typed_value* fixed_tokens(unsigned num_tokens) 
     { 
      if(num_tokens > 1) { 
       po::typed_value<T, charT>::multitoken(); 
      } 

      m_min = num_tokens; 
      m_max = num_tokens; 

      return this; 
     } 

    private: 
     int m_min; 
     int m_max; 
    }; 


    template<class T, class charT = char> 
    bounded_typed_value<T, charT>* 
    bounded_value() 
    { 
     return new bounded_typed_value<T, charT>(0); 
    } 

Вы теперь можно соединить все это следующим образом:

po::positional_options_description p; 
    p.add("file-name", -1); 

    boost::program_options::options_description desc; 
    desc.add_options() 
     ("coords,c", boost::program_options::bounded_value<vector<double>>()->fixed_tokens(4), "Bounding box"); 

    po::store(po::command_line_parser(argc, argv) 
     .extra_style_parser(&po::ignore_numbers) 
     .positional(p) 
     .options(commands) 
     .run(), vm); 
+2

Я думаю, что это должен быть принятый ответ, потому что он решает проблему и не требует блокировки коротких опций. Есть некоторые ошибки, такие как 'option()' должно быть 'po :: option()', если вы не предполагаете 'using namespace po;' ... – rytis

+0

Спасибо, хороший улов. Исправлено. –