И как всегда, после того как разобрался, решение оказывается простым и понятным, но где оно было раньше - не понятно. Всмысле это могло бы быть написано в докумментации.
Начнем с устройства
program_options::command_line_parser
. Устроен он следующим образом:Имеется некоторый набор парсеров стиля (style_parser), которые по некоторым критериям выбирают из строки опции. набор стайл парсеров формируется на основе стиля командной строки, который может варьироваться в некоторых пределах - поддержка длинных имен, значения у опций, опции формата DOS и тд.
Каждый парсер возвращает список опций (
vector<program_options::option>
), обработанных им на данном этапе. Это может быть пустой список, одна или много опций, хотя парсить много опций за раз нет необходимости, там цикл и остатки командной строки обязательно снова пройдут через каждый парсер. Обработка заканчивается после того, как командная строка опустевает.После прохождения командной строки через набор парсеров формируется список опций. неопциональные элементы в котором представлены некоей нетипизированной опцией.
Это было введение. :)
Теперь предположим, что командная строка предположительно будет разбираться за два прохода. Первый проход выделяет глобальные опции (идущие перед командой) и команду соответственно. А второй проход получит список аргументов, объединяющий в себе локальные опции и все остальное.
Но как и в любом деле здесь главное вовремя остановиться, то есть хотелось бы чтобы boost не анализировал строку до конца в поисках опций. И тут нам на помощь приходит
extra_style_parser
. Дополниельный парсер стиля - это отдельная функция. Её задачей будет при обнаружении первого неопционального слова, предположительно команды, оставшуюся часть строки без разбора загнать в результат. Cлайды:
vector<program_options::option> stop_on_command (vector<string> &args)
{
vector<program_options::option> result;
const string &tok = args[0];
if (tok[0] != '-') {
BOOST_FOREACH (string &arg, args) {
program_options::option opt;
opt.value.push_back(arg);
result.push_back(opt);
}
args.clear();
}
return result;
}
Используем это при разборе строки:
program_options::command_line_parser parser (argc, argv);
parser.extra_style_parser (stop_on_command);
...
program_options::store(parser.run(), vm);
Естественно чтобы это дело сработало необходимо создать позиционный параметр неограниченного количества, куда будут сложены все локальные опции/аргументы.
Вот собственно и все. Может пригодиться кому.
7 коммент.:
Можно потребовать чтобы "команда" обязательно стояла 1-ым аргументом. Тогда, проанализировав первый аргумент командной строки, составить список опций (глобальные + локальные) и начать разбор со второго аргумента.
Не, это не наш путь... :)
Это называется будем делать так, как диктует бибиотека. Всмысле я хочу сказать, что не никогда не нужно делать так, как получается. Надо всегда делать так, как хочется. :)
И при генерации каких-то идей ни в коем случае не надо думать о том, как это может быть реализовано. :) Это сразу все портит.
Я предпочитаю обдумывать идеи без привязки к реализации, и потом делать как хочу, а не как получается. :) Только так я могу написать то, что мне действительно будет нравится. А не оправдываться потом, что дескать я не мог сделать лучше, потому что буст, сцуко, не дает. :)
Где-то так я мыслю.
Мне понятна Ваша точка зрения. И я ее даже разделяю :). Непонятно мне другое, чем хуже та идея что я высказал выше? И не применительно к boost'у, а вообще, так сказать абстрактно, в отрыве от конкретных интсрументальных средств.
Когда мы говорим - что ладно, пускай команда стоит первой, а опции разрулим исходя из нее - тем самым мы признаемся себе в бессилии сделать так, как хочется.
Кроме того среди глобальных и локальных опций могут попасться одинаковые. И тогда эта версия не сработает так как надо.
например
./prog -h
может выдавать общий хелп по командам, а
./prog command -h может выдавать хелп по конкретной команде.
есть разница всетаки.
Конечно можно интерпретировать (положение опции предположительно не играет роли)
./prog -h
для отображения глобального хелпа, а
./prog -h command
для локального, но это опять получится допущение. А если опции несут разную функцию (делать так кончено не следует, но вдруг захочется?)
И кроме того в основном разборе придется плодить условия типа
if (command == "cmd1") {
help1;
} else if (command == "cmd2")
и так далее
Хотя по правильному этим должны заниматься подобъекты.
Мы конечно можем сперва сконструировать объект команды а протом сказать
command->help().
пожалуй это будет лучше, но никак не решит проблему разного смысла одноименных опций.
If you want to have something done, do it yourself (c) "Fifth Element"
Не быстрее самому написать свой класс/набор классов? А то обсуждать проблемы boost дольше :-) Я, конечно, понимаю, что я похож на того, который на форуме осеписателей говорит, а зачем нужна ось, уже написано много... но все же :-)
Нет, этот блог не посвящен бусту...
А что касается трудностей, без буста трудностей будет не меньше.
Буст экономит код, просто в данном случае небольшой изврат с командами, поэтому сложности. Но это не повод отказываться от boost. :)
Отправить комментарий