Использование '-' в качестве маркера конца опций с boost :: program_options - PullRequest
16 голосов
/ 29 марта 2011

Традиционный способ указать конец параметров для программ командной строки - с помощью параметра --.Как я могу получить boost :: program_options, чтобы распознать это как параметр и принять остальную часть командной строки в качестве позиционных аргументов?Следующее не работает:

namespace po = boost::program_options;

po::positional_options_description posOpts;
posOpts.add("keywords", 1);
posOpts.add("input", 1);

std::vector<std::string> final_args;

po::options_description desc("Allowed Options");
desc.add_options()
  ...
  ("", po::value< std::vector<std::string> >(&final_args)->multitoken(), "end of options")
  ...
  ;

po::command_line_parser(argc, argv).options(desc).positional(posOpts).run();

Если я даю foo bar в качестве аргументов, я ничего не получаю в final_args (как ожидалось), но также когда я даю -- foo bar в качестве аргументов (когда яожидал найти final_args[0] == "foo" и final_args[1] == "bar").Я предполагаю, что -- - это длинный аргумент с пустой строкой в ​​качестве имени аргумента.Если вместо этого он должен интерпретироваться как короткий аргумент с - в качестве имени аргумента, как мне это указать?Насколько я вижу, изменение спецификации аргумента с "" на ",-" не влияет на результат.

Как получить boost :: program_options для правильной обработки --?

Редактировать: Вот попытка сделать то, что предложил Тим Сильвестр, создав extra_style_parser:

std::vector<po::option> end_of_opts_parser(std::vector<std::string>& args) {
  std::vector<po::option> result;

  std::vector<std::string>::const_iterator i(args.begin());
  if (i != args.end() && *i == "--") {
    for (++i; i != args.end(); ++i) {
      po::option opt;
      opt.string_key = "pargs";
      opt.original_tokens.push_back(*i);
      result.push_back(opt);
    }

    args.clear();
  }

  return result;
}

"pargs", который был добавлен к таким параметрам:

("pargs", po::value< std::vector<std::string> >(&pargs), "positional arguments")

Выполнение этого с -- в списке аргументов вызывает исключение required_option.(Я получаю аналогичные результаты, если вместо po::option для каждого конечного аргумента я упаковываю их все в po::option::original_tokens в один po::option.)

Ответы [ 3 ]

7 голосов
/ 08 июля 2011

Я немного запутался, потому что boost::program_options уже делает все это сам по себе:

$ ./testprog --the-only-option=23 aa --unknown bb
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::unknown_option> >'
  what():  unknown option unknown
Aborted

$ ./testprog --the-only-option=23 -- aa --unknown bb
the only option: 23
positional: aa
positional: --unknown
positional: bb

Программа:

#include <boost/program_options.hpp>

#include <iostream>
#include <vector>

using namespace std;

namespace po = boost::program_options;

static bool handle_command_line(int argc, char *argv[])
{
    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "Describe command line options")
        ("the-only-option", po::value<string>(), "some option")
        ;

    po::options_description hidden;
    hidden.add_options()
        ("positional", po::value<vector<string> >())
        ;

    po::options_description all_options;
    all_options.add(desc).add(hidden);

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

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

    if (vm.count("help")) {
        cout << "Usage: " << argv[0] << " [options] [args]" << endl;
        cout << desc << "\n";
        return false;
    }

    if (vm.count("the-only-option"))
        cout << "the only option: " << vm["the-only-option"].as<string>() << endl;

    if (vm.count("positional")) {
        const vector<string> &v = vm["positional"].as<vector<string> >();
        vector<string>::const_iterator it = v.begin();
        for (; it != v.end(); ++it)
            cout << "positional: " << *it << endl;
    }

    return true;
}

int main(int argc, char *argv[])
{
    if (!handle_command_line(argc, argv))
        return 1;
    return 0;
}
6 голосов
/ 30 марта 2011

Существует простой, неудовлетворительный обходной путь: перед передачей argv на command_line_parser, проверьте, есть ли в нем --. Если это так, сбросьте argc в положение --, чтобы скрыть его и аргументы, следующие за ним command_line_parser. Затем, когда закончите синтаксический анализ, обработайте позиционные аргументы после -- вручную. Blech!

4 голосов
/ 30 марта 2011

У меня был тот же вопрос, но я отказался от него.

Я считаю, что способ сделать это - вызвать program_options::command_line_parser::extra_style_parser(), передав ему функцию, которая берет вектор строки по ссылке и возвращает вектор option с (см. style_parser typedef в cmdline .hpp ).

Ваша функция должна будет обнаружить, что первый токен является "-", создать новый объект option, поместить все остальные токены на входе в вектор значений option и очистить вход вектор. См. program_options::detail::cmdline::parse_long_option и т. Д. В libs/program_options/src/cmdline.cpp, с чего можно начать.

Возможно, вам потребуется зарегистрировать определенное значение параметра, чтобы использовать его, чтобы вы могли легко найти этот специальный объект option в конце анализа и извлечь из него набор дополнительных неопционных параметров.

Хотел бы я дать вам некоторый код, но я так и не удосужился на самом деле сделать это, в итоге я просто взял дополнительные параметры по одному на строку в stdin.

редактирование:

Мне было жаль указывать вам направление, которое не решило проблему, поэтому я вернулся и заставил его работать. Проблема в том, что ваш позиционный аргумент не был настроен на прием нескольких токенов, и вы не заполняли value. Код program_options ожидает обоих или не работает.

Вот полный код, который работает для меня:

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

namespace po = boost::program_options;
typedef std::vector<std::string> stringvec;

std::vector<po::option> end_of_opts_parser(stringvec& args) {
  std::vector<po::option> result;
  stringvec::const_iterator i(args.begin());
  if (i != args.end() && *i == "--") {
    for (++i; i != args.end(); ++i) {
      po::option opt;
      opt.string_key = "pargs";
      opt.value.push_back(*i);      //  <== here
      opt.original_tokens.push_back(*i);
      result.push_back(opt);
    }
    args.clear();
  }
  return result;
}

int main(int argc, char* argv[])
{
    po::options_description desc("Allowed Options");
    desc.add_options()
        ("help,h", "produce help message")
        ("pargs", po::value<stringvec>()->multitoken(), "positional arguments");
    //                          and here     ^^^^^^^^^^^^^

    po::command_line_parser clparser(argc, argv);
    clparser.extra_style_parser(end_of_opts_parser);

    po::variables_map vm;
    po::store(clparser.options(desc).run(), vm);
    po::notify(vm);

    bool const help = !vm["help"].empty();
    std::cout << "help = " << help << " - ";

    // in addition, you don't need to use a separate vector of strings:    
    stringvec const& pargs = vm["pargs"].as<stringvec>();
    std::copy(pargs.begin(), pargs.end(),
        std::ostream_iterator<std::string>(std::cout, ","));

    return 0;
}

При запуске с -h -- foo bar baz печатается help = 1 - foo,bar,baz,.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...