Boost:: program_options: итерация и печать всех параметров
Недавно я начал использовать boost::program_options
и нашел, что это очень удобно. Тем не менее, есть одна вещь, которую я не смог правильно кодировать:
Я хотел бы перебрать все параметры, которые были собраны в boost::program_options::variables_map
, чтобы выводить их на экран. Это должно стать удобной функцией, которую я могу просто вызвать, чтобы перечислить все параметры, которые были установлены без необходимости обновлять функцию, когда я добавляю новые параметры или для каждой программы.
Я знаю, что я могу проверять и выводить отдельные параметры, но, как сказано выше, это должно стать общим решением, которое не обращает внимания на фактические параметры. Я также знаю, что я могу перебирать содержимое variables_map
, поскольку это просто расширенный std::map
. Затем я мог проверить тип, содержащийся в сохраненной переменной boost::any
, и использовать .as<>
, чтобы преобразовать его в соответствующий тип. Но это означало бы кодирование длинного блока переключения с одним случаем для каждого типа. И это не похоже на хороший стиль кодирования для меня.
Итак, вопрос в том, есть ли лучший способ перебора этих параметров и вывода их?
Ответы
Ответ 1
Это хороший пример использования шаблона посетителя. К сожалению, boost::any
не поддерживает шаблон посетителя, например boost::variant
. Тем не менее есть сторонние сторонние подходы.
Другая возможная идея - использовать RTTI: создать карту type_info
известных типов, отображаемых для типа функтора обработчика.
Ответ 2
Как уже упоминалось ранее, Rovost, шаблон посетителя является хорошим выбором. Чтобы использовать его с ПО, вам нужно использовать оповещения для своих параметров таким образом, что если опция передана, уведомитель заполнит запись в вашем наборе значений boost::variant
. Набор должен храниться отдельно. После этого вы можете выполнять итерацию по своему набору и автоматически обрабатывать действия (т.е. Печатать) на них с помощью boost::apply_visitor
.
Для посетителей наследуйте от boost::static_visitor<>
Фактически, я сделал подход Visitor и общий подход более широким.
Я создал class MyOption
, который содержит описание, boost::variant
для значения и других параметров, таких как неявный, по умолчанию и т.д. Я заполняю вектор объектов типа MyOption
так же, как PO для своих опций (см. boost::po::options_add()
) через шаблоны. В момент прохождения std::string()
или double()
для boosts::varian
t инициализации вы заполняете тип значения и другие вещи, такие как default, implicit.
После этого я использовал шаблон посетителя для заполнения контейнера boost::po::options_description
, так как boost::po
нуждается в собственных структурах для синтаксического анализа входной командной строки. Во время заполнения я устанавливал уведомление для каждой опции - если он будет принят boost::po
, он автоматически заполнит мой оригинальный объект MyOption
.
Затем вам нужно выполнить po::parse
и po::notify
. После этого вы сможете использовать уже заполненный шаблон std::vector<MyOption*>
через Visitor, так как он содержит boost:: variant внутри.
Что хорошо обо всем этом - вы должны написать свой тип параметра только один раз в коде - при заполнении std::vector<MyOption*>
.
PS. если вы используете этот подход, вам придется столкнуться с проблемой установки notifierer для опции без значения, обратитесь к этому разделу, чтобы получить решение: boost-program-options: notifier для параметров без значения
PS2. Пример кода:
std::vector<MyOptionDef> options;
OptionsEasyAdd(options)
("opt1", double(), "description1")
("opt2", std::string(), "description2")
...
;
po::options_descripton boost_descriptions;
AddDescriptionAndNotifyerForBoostVisitor add_decr_visitor(boost_descriptions);
// here all notifiers will be set automatically for correct work with each options' value type
for_each(options.begin(), options.end(), boost::apply_visitor(add_descr_visitor));
Ответ 3
Сегодня я столкнулся с такой проблемой. Это старый вопрос, но, возможно, это поможет людям, которые ищут ответ.
Метод, с которым я столкнулся, состоит в том, чтобы попробовать связку как <... > (), а затем проигнорировать исключение. Это не ужасно красиво, но я получил его на работу.
В нижнем кодовом блоке vm - переменная_map из boost program_options. vit - это итератор над vm, что делает его парой std::string и boost:: program_options:: variable_value, причем последняя является boost:: any. Я могу напечатать имя переменной с помощью vit- > first, но vit- > second не так просто выводить, потому что это boost:: any, т.е. Исходный тип был потерян. Некоторые должны быть представлены как std::string, некоторые как double и т.д.
Итак, чтобы указать значение переменной, я могу использовать это:
std::cout << vit->first << "=";
try { std::cout << vit->second.as<double>() << std::endl;
} catch(...) {/* do nothing */ }
try { std::cout << vit->second.as<int>() << std::endl;
} catch(...) {/* do nothing */ }
try { std::cout << vit->second.as<std::string>() << std::endl;
} catch(...) {/* do nothing */ }
try { std::cout << vit->second.as<bool>() << std::endl;
} catch(...) {/* do nothing */ }
У меня есть только 4 типа, которые я использую для получения информации из файла командной строки /config, если бы я добавил больше типов, мне пришлось бы добавить больше строк. Я признаю, что это немного уродливо.
Ответ 4
Поскольку вы собираетесь просто распечатать их в любом случае, вы можете захватить исходное строковое представление при синтаксическом анализе. (Вероятно, в коде есть ошибки компилятора, я вырвал его из своей кодовой базы и непечатал кучу вещей)
std::vector<std::string> GetArgumentList(const std::vector<boost::program_options::option>& raw)
{
std::vector<std::string> args;
BOOST_FOREACH(const boost::program_options::option& option, raw)
{
if(option.unregistered) continue; // Skipping unknown options
if(option.value.empty())
args.push_back("--" + option.string_key));
else
{
// this loses order of positional options
BOOST_FOREACH(const std::string& value, option.value)
{
args.push_back("--" + option.string_key));
args.push_back(value);
}
}
}
return args;
}
Использование:
boost::program_options::parsed_options parsed = boost::program_options::command_line_parser( ...
std::vector<std::string> arguments = GetArgumentList(parsed.options);
// print