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