Использование '-' в качестве маркера конца опций с boost:: program_options
Традиционным способом указания конца опций программ командной строки является опция --
. Как я могу получить 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
для каждого завершающего arg, я упаковываю их все в po::option::original_tokens
в один po::option
.)
Ответы
Ответ 1
У меня был такой же вопрос, но он отказался от него.
Я считаю, что способ сделать это - вызвать 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,
.
Ответ 2
Im немного запутался, потому что 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;
}
Ответ 3
Там есть простой, неудовлетворительный обходной путь: перед передачей argv
в command_line_parser
проверьте, есть ли в нем --
. Если да, reset argc
в положение --
, чтобы скрыть его, и аргументы, завершающие его с command_line_parser
. Затем, закончив синтаксический анализ, обработайте позиционные аргументы после --
вручную. Blech!