2012-09-03 1 views
11

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

Я хотел бы перебрать все параметры, которые были собраны в boost::program_options::variables_map, чтобы выводить их на экран. Это должно стать удобной функцией, которую я могу просто вызвать, чтобы перечислять все параметры, которые были установлены без необходимости обновлять функцию, когда я добавляю новые параметры или для каждой программы.

Я знаю, что я могу проверять и выводить отдельные параметры, но, как сказано выше, это должно стать общим решением, которое не обращает внимания на фактические параметры. Я также знаю, что я могу перебирать содержимое variables_map, так как это просто расширенный std::map. Затем я мог проверить тип, содержащийся в сохраненной переменной boost::any, и использовать .as<>, чтобы преобразовать его в соответствующий тип. Но это означало бы кодирование длинного блока переключения с одним случаем для каждого типа. И это не похоже на хороший стиль кодирования для меня.

Итак, вопрос в том, есть ли лучший способ перебора этих параметров и вывода их?

ответ

5

Это хороший пример использования шаблона посетителей. К сожалению, boost::any не поддерживает шаблон посетителя, такой как boost::variant. Тем не менее, есть некоторые сторонние лица approaches.

Еще одна возможная идея - использовать RTTI: создать карту type_info известных типов, отображаемых для ввода функтора обработчика.

+0

Спасибо за ссылку и идею о RTTI , Я надеялся, что смогу предотвратить создание структуры для всех поддерживаемых типов, которые мне придется управлять, если типы увеличиваются, но, похоже, это будет невозможно. В принципе, я хотел передать доллар типам - например, если они поддерживают «оператор <<», все работает нормально, иначе компиляция должна завершиться неудачей. – shiin

5

Как @Rost ранее упомянутый, шаблон посетителя - хороший выбор здесь. Чтобы использовать его с ПО, вам нужно использовать оповещения для своих опций таким образом, чтобы, если опция передана, уведомитель заполнит запись в вашем наборе значений boost::variant. Набор должен храниться отдельно. После этого вы можете выполнять итерацию по своему набору и автоматически обрабатывать действия (то есть печатать) на них, используя boost::apply_visitor.

Для посетителей, унаследован от boost::static_visitor<>

На самом деле, я сделал посетителей и общий подход использования более широким.

Я создал class MyOption, который содержит описание, boost::variant для значений и других параметров, таких как неявное, значение по умолчанию и т. Д. Я заполняю вектор объектов типа MyOption так же, как PO do для своих опций (см. 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*> через шаблон посетителя, так как он содержит boost :: variant внутри.

Что хорошо обо всем этом - вы должны написать свой тип параметра только один раз в коде - при заполнении std::vector<MyOption*>.

PS. если вы используете этот подход, вы столкнетесь с проблемой установки уведомления для опции без значения, обратитесь к этой теме, чтобы получить решение: boost-program-options: notifier for options with no value

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)); 
+0

Благодарим вас за подробное объяснение. Это также интересное решение. Таким образом, я мог бы также легко добавлять разные описания для вывода справки и просто перечислять значения параметров. – shiin

0

Сегодня я имел дело с такими проблемами. Это старый вопрос, но, возможно, это поможет людям, которые ищут ответ.

Метод, который я придумал, состоит в том, чтобы попробовать кучу как < ...>(), а затем игнорировать исключение. Это не очень красиво, но я заработал.

В приведенном ниже блоке кода 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 и т. Д.

Итак, COUT значение переменной, я могу использовать это:

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 типа, которые я использую, чтобы получить информацию из файла командной строки/конфигурации, если я добавил еще типов, мне пришлось бы добавить больше строк. Я признаю, что это немного уродливо.

0

Поскольку вы собираетесь просто распечатать их в любом случае, вы можете захватить оригинальное строковое представление при синтаксическом анализе. (Вероятно, есть ошибки компилятора в коде, я разорвал его из моих кодовых и ун-typedefed кучи вещей)

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