Может Boost Program_options разделять значения аргументов через запятую

Если моя командная строка:

> prog --mylist=a,b,c

Можно ли настроить параметры программы Boost для просмотра трех различных значений аргумента для аргумента mylist? Я настроил program_options как:

namespace po = boost::program_options;
po::options_description opts("blah")

opts.add_options()
    ("mylist", std::vector<std::string>>()->multitoken, "description");

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

Когда я проверяю значение аргумента mylist, я вижу одно значение как a,b,c. Я хотел бы видеть три разных значения, разделенных запятой. Это отлично работает, если я укажу командную строку как:

> prog --mylist=a b c

or

> prog --mylist=a --mylist=b --mylist=c

Есть ли способ настроить program_options так, чтобы он видел a,b,c как три значения, каждое из которых должно быть вставлено в вектор, а не одно?

Я использую boost 1.41, g++ 4.5.0 20100520 и включил экспериментальные расширения C++0x.

ИЗМЕНИТЬ:

Принятое решение работает, но в конечном итоге оказывается более сложным, IMO, чем просто повторение вектора и разделение значений вручную. В конце концов, я воспользовался предложением Джеймса Макнеллиса и реализовал его таким образом. Однако его решение не было представлено в качестве ответа, поэтому я принял другое правильное решение от hkaiser. Оба работали, но ручная токенизация понятнее.


person Louis Marascio    schedule 17.06.2010    source источник
comment
Если ничего другого, вы можете использовать boost::tokenizer для токенизации значений, разделенных запятыми.   -  person James McNellis    schedule 17.06.2010
comment
Это, наверное, самое простое. Просто обработайте аргумент и обработайте случай, когда появляется запятая.   -  person Louis Marascio    schedule 18.06.2010


Ответы (3)


Вы можете зарегистрировать собственный валидатор для своего варианта:

namespace po = boost::program_options;

struct mylist_option 
{
    // values specified with --mylist will be stored here
    vector<std::string> values;

    // Function which validates additional tokens from command line.
    static void
    validate(boost::any &v, std::vector<std::string> const &tokens)
    {
        if (v.empty())
            v = boost::any(mylist_option());

        mylist_option *p = boost::any_cast<mylist_option>(&v);
        BOOST_ASSERT(p);

        boost::char_separator<char> sep(",");
        BOOST_FOREACH(std::string const& t, tokens)
        {
            if (t.find(",")) {
                // tokenize values and push them back onto p->values
                boost::tokenizer<boost::char_separator<char> > tok(t, sep);
                std::copy(tok.begin(), tok.end(), 
                    std::back_inserter(p->values));
            }
            else {
                // store value as is
                p->values.push_back(t);
            }
        }
    }
};

который затем можно использовать как:

opts.add_options()                 
    ("mylist", po::value<mylist_option>()->multitoken(), "description");

а также:

if (vm.count("mylist"))
{
    // vm["mylist"].as<mylist_option>().values will hold the value specified
    // using --mylist
}
person hkaiser    schedule 18.06.2010
comment
Это сработало, с некоторыми изменениями. Мне пришлось извлечь функцию проверки из структуры и перегрузить ее в соответствии с документацией. Я ненавижу создавать искусственный тип, чтобы делать то, что я хочу, но это жизнь. В конце концов, я просто перебрал вектор и токенизировал значения с помощью простого цикла. Это примерно на 50% меньше кода, чем пользовательский валидатор. Тем не менее, я принимаю этот ответ, поскольку он работает и является единственным представленным правильным ответом. - person Louis Marascio; 20.06.2010
comment
у меня почему-то этот код больше не компилируется (boost 1.55) - person malat; 06.02.2014
comment
я решил это: stackoverflow.com/questions/26389297/ - person 4ntoine; 16.10.2014

Я не пытался сделать это сам, но вы, возможно, сможете использовать тот же подход, что и в примере custom_syntax.cpp, который предоставляется с program_options, чтобы написать свой собственный синтаксический анализатор, который вы можете предоставить в качестве дополнительного синтаксического анализатора. Немного информации здесь с коротким примером. Затем вы можете либо объединить это с предложением Джеймса использовать boost::tokenizer, либо просто последовать его предложению.

person Jacob    schedule 17.06.2010
comment
Я не думаю, что это сработает. Похоже, что синтаксический анализатор вызывает только аргументы, а не их значения. - person Louis Marascio; 18.06.2010
comment
Я беру это обратно, он вызывает его для каждого токена, а не для каждого аргумента. Поэкспериментирую еще. - person Louis Marascio; 18.06.2010
comment
@lrm: Извините, я не могу больше помочь, я не использовал program_options много, но дайте нам знать, как это происходит, если вы решите пойти с этим, а не просто токенизировать строку. - person Jacob; 18.06.2010

вот что я использую прямо сейчас:

template<typename T, int N> class mytype;
template<typename T, int N> std::istream& operator>> (std::istream& is, mytype<T,N>& rhs);
template<typename T, int N> std::ostream& operator<< (std::ostream& os, const mytype<T,N>& rhs);
template < typename T, int N >
struct mytype
{
  T values[N];
  friend std::istream& operator>> <>(std::istream &is, mytype<T,N> &val);
  friend std::ostream& operator<< <>(std::ostream &os, const mytype<T,N> &val);
};
template<typename T, int N>
inline std::istream& operator>>(std::istream &is, mytype<T,N> &val)
{
  for( int i = 0; i < N; ++i )
    {
    if( i )
      if (is.peek() == ',')
        is.ignore();
    is >> val.values[i];
    }
  return is;
}
template<typename T, int N>
inline std::ostream& operator<<(std::ostream &os, const mytype<T,N> &val)
{
  for( int i = 0; i < N; ++i )
    {
    if( i ) os << ',';
    os << val.values[i];
    }
  return os;
}

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

  typedef mytype<int,2> mytype; // let's test with 2 int
  mytype my;
  try
    {
    po::options_description desc("the desc");
    desc.add_options()
      ("mylist", po::value< mytype >(&my), "mylist desc")
      ;

    po::variables_map vm;
    po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
    po::notify(vm);

    if (vm.count("mylist"))
      {
      const mytype ret = vm["mylist"].as<mytype >();
      std::cerr << "mylist: " << ret << " or: " << my << std::endl;
      }
    }
  catch(std::exception& e)
    {
    std::cout << e.what() << "\n";
    }    
  return 0;
}
person malat    schedule 06.02.2014