23 KiB
Общая архитектура библиотеки program_options
Основные компоненты библиотеки
Библиотека состоит из трёх главных компонентов:
- Компонент описания опций — определяет допустимые параметры и действия с их значениями
- Компонент парсеров — использует эту информацию для поиска имён параметров и их значений в источниках ввода
- Компонент хранения — предоставляет интерфейс для доступа к значениям параметров и преобразует строковые представления в нужные типы C++
Ключевые классы и функции
- Класс options_description относится к компоненту описания опций
- Функция parse_command_line является частью компонента парсеров
- Класс variables_map принадлежит компоненту хранения
Взаимодействие компонентов
В предыдущих примерах мы видели, как эти компоненты используются в функции main для парсинга командной строки и конфигурационных файлов. Рассмотрим теперь особенности работы вне функции main.
Архитектура программы
Для частей программы, находящихся вне main, наиболее важен компонент хранения. Он предоставляет класс, хранящий все значения параметров, который можно свободно передавать между модулями программы.
Важные особенности:
- Остальные компоненты используются только там, где выполняется парсинг
- Отдельные модули программы могут описывать свои параметры и передавать их в основной модуль
- Основной модуль может объединять все описания параметров
- Такой подход особенно полезен при большом количестве параметров
Рекомендации по использованию
При разработке крупных приложений рекомендуется:
- Централизовать описание параметров в одном месте
- Использовать модульный подход при большом количестве опций
- Передавать объект хранения между компонентами программы
- Обеспечивать корректную обработку параметров во всех модулях
Компонент описания опций
Основные классы компонента
Компонент описания опций включает три ключевых класса:
- option_description — содержит имя опции, описание и указатель на value_semantic
- value_semantic — знает тип значения опции, может парсить значение, применять значение по умолчанию
- options_description — контейнер для экземпляров option_description
Синтаксическое и семантическое описание
Синтаксическая информация
Включает:
- Имя опции
- Количество токенов для указания значения
- Используется парсерами для группировки токенов в пары (имя, значение)
Семантическая информация
Отвечает за:
- Преобразование значений в типы C++
- Валидацию данных
- Применение значений по умолчанию
Практическое применение
Пример объявления опций:
options_description desc;
desc.add_options()
("help", "показать справку")
("optimization", value<int>()->default_value(10), "уровень оптимизации")
;
Механизм работы
- Функция value создаёт экземпляр класса typed_value
- typed_value — производный класс от value_semantic
- Содержит код для парсинга значений определённого типа
- Поддерживает методы для указания дополнительной информации
Дополнительные возможности
Библиотека предоставляет:
- Функцию bool_switch для булевых значений
- Возможность создания пользовательских подклассов value_semantic
Важные аспекты проектирования
Разделение на синтаксический и семантический уровни:
- Парсеры работают только с синтаксическим уровнем
- Ограничивает использование чрезмерно сложных структур
- Пример проблемы:
calc --expression=1 + 2/3 # сложно парсить без контекста
- Решённая версия:
calc --expression="1 + 2/3" # синтаксис ясен
Такое разделение является ключевым элементом дизайна библиотеки, обеспечивая чёткое разделение ответственности между компонентами.
Синтаксическая информация опций
Основные компоненты
Синтаксическая информация предоставляется классами boost::program_options::options_description и методами boost::program_options::value_semantic. Она включает:
- Имя опции — используется для идентификации внутри программы
- Описание опции — отображается пользователю
- Количество токенов — определяет, сколько элементов может содержать значение опции при парсинге
Примеры использования
Рассмотрим практический пример объявления опций:
options_description desc;
desc.add_options()
("help", "показать справочное сообщение")
("compression", value<string>(), "уровень сжатия")
("verbose", value<string>()->implicit_value("0"), "уровень детализации")
("email", value<string>()->multitoken(), "email для отправки")
;
Особенности каждой опции:
- Первая опция (
help) не требует значения - Вторая опция (
compression) требует ровно один токен значения - Третья опция (
verbose) может принимать либо один токен, либо не принимать значение вообще - Четвёртая опция (
email) может принимать несколько токенов
Пример командной строки
Допустимый вариант использования опций:
test --help --compression 10 --verbose --email beadle@mars beadle2@mars
Важные характеристики
- Неявные значения (implicit_value) позволяют опции иметь значение по умолчанию при отсутствии явного указания
- Многокомпонентные значения (multitoken) позволяют опции принимать несколько токенов
- Каждая опция может иметь собственное правило обработки значений
Такой подход обеспечивает гибкость при определении правил обработки параметров командной строки и конфигурационных файлов.
Форматирование описаний опций
Базовое форматирование
Описания опций могут быть достаточно объёмными, особенно когда требуется документировать несколько значений. Библиотека предоставляет простые механизмы форматирования.
Структура описания
- Описание состоит из одного или нескольких параграфов, разделённых символом новой строки (
\n) - При выводе библиотека автоматически вычисляет отступы для описания опции
- Каждый параграф выводится как отдельная строка с рассчитанным отступом
- Если параграф не помещается в одну строку, он переносится на несколько строк с одинаковым отступом
Управление отступами
Базовый отступ
Можно задать дополнительный отступ для первой строки, добавив пробелы в начале параграфа:
options.add_options()
("help", " Подробное описание опции, которое может быть очень длинным и занимать несколько строк")
;
Результат будет выглядеть так:
--help Подробное описание опции, которое может быть
очень длинным и занимать несколько строк
Расширенное форматирование
Для более сложного форматирования можно использовать символ табуляции (\t):
options.add_options()
("well_formated", "Описание опции:\n"
"\n"
"Значения:\n"
" Значение1:\tОписание первого значения, которое может быть длинным\n"
" Значение2:\tОписание второго значения\n"
"\n"
" Этот параграф имеет отступ только для первой строки");
Результат:
--well_formated Описание опции:
Значения:
Значение1: Описание первого значения, которое может быть
длинным
Значение2: Описание второго значения
Этот параграф имеет отступ только для первой строки
Важные ограничения
- Символ табуляции удаляется перед выводом
- Разрешён только один символ табуляции на параграф
- При нарушении этого правила выбрасывается исключение типа
program_options::error - Табуляция игнорируется, если:
- Она находится не в первой строке параграфа
- Она расположена в последней возможной позиции первой строки
Семантическая информация и парсеры
Семантическая информация
Семантическая информация полностью предоставляется классом boost::program_options::value_semantic. Рассмотрим пример:
options_description desc;
desc.add_options()
("compression", value<int>()->default_value(10), "уровень сжатия")
("email", value<vector<string>>()
->composing()->notifier(&your_function), "email")
;
Этот код определяет:
- Значение по умолчанию для первой опции — 10
- Вторая опция может появляться несколько раз, и все значения будут объединены
- После парсинга будет вызвана функция
your_functionс значением опции "email"
Позиционные опции
Позиционные опции — это элементы командной строки без явного указания имени опции. Например:
archiver --compression=9 /etc/passwd
Здесь /etc/passwd — позиционная опция.
Для обработки таких опций используется класс positional_options_description:
positional_options_description pd;
pd.add("input-file", 1); // Первая позиционная опция будет иметь имя "input-file"
Можно задать несколько позиционных опций:
positional_options_description pd;
pd.add("output-file", 2).add("input-file", -1);
В этом примере:
- Первые две позиционные опции получат имя "output-file"
- Все последующие — имя "input-file"
Важное замечание: класс positional_options_description только определяет соответствие позиции имени, само имя должно быть зарегистрировано в экземпляре options_description.
Компонент парсеров
Компонент парсеров выполняет следующие функции:
- Разбивает входные данные на пары (имя, значение)
- Проверяет, известны ли опции
- Определяет способ указания значений
Процесс парсинга:
- Парсер ищет возможные опции
- Сверяется с описанием опций
- Определяет, известно ли имя опции
- Использует value_semantic для обработки значений
Особенности парсинга
Три важных исключения из стандартной модели:
-
Короткие имена опций:
- Можно использовать сокращённые имена
- Поддерживается автоматическое определение по префиксам
-
Многокомпонентные значения:
- Опция может принимать несколько токенов
- Требует явного включения
-
Позиционные опции:
- Механизм автоматического определения имён
- Гибкая настройка через positional_options_description
Использование парсеров
Для запуска парсера обычно вызывается функция с параметрами:
- Описание опций
- Источник данных (командная строка, конфигурационный файл)
Результаты парсинга возвращаются в виде объекта класса parsed_options, который передаётся компоненту хранения.
Компонент хранения
Основные функции
Компонент хранения отвечает за:
- Хранение конечных значений опций в специальном классе и обычных переменных
- Управление приоритетами между различными источниками
- Вызов пользовательских функций уведомления с финальными значениями опций
Пример использования
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
store(parse_config_file("example.cfg", desc), vm);
notify(vm);
Особенности работы
- Класс variables_map используется для хранения значений опций
- Функция store добавляет значения из различных источников
- Функция notify запускает пользовательские обработчики и сохраняет значения в обычные переменные
- Приоритет определяется порядком обработки: если значение уже установлено, оно не перезаписывается
Важное предупреждение
Не забудьте вызвать функцию notify после сохранения всех проанализированных значений!
Парсеры конфигурационных файлов
Синтаксис конфигурационных файлов
Файлы конфигурации имеют простой INI-подобный формат:
- Строка вида
name=valueзадаёт значение опции - Секция начинается со строки
[section name] - Символ
#начинает комментарий до конца строки
Иерархия опций
Имена опций относительны к именам секций. Например:
[gui.accessibility]
visual_bell=yes
эквивалентно:
gui.accessibility.visual_bell=yes
Пример описания опции
options_description desc;
desc.add_options()
("gui.accessibility.visual_bell", value<string>(), "визуальный звонок")
;
Парсер переменных окружения
Библиотека также поддерживает парсинг переменных окружения, что позволяет:
- Использовать значения из окружения системы
- Объединять их с другими источниками конфигурации
- Управлять поведением программы через системные переменные
Такой подход обеспечивает гибкую систему конфигурации приложения с возможностью комбинирования различных источников настроек.
Парсер переменных окружения
Что такое переменные окружения
Переменные окружения — это строковые переменные, доступные всем программам через функцию getenv стандартной библиотеки C. Их можно установить:
- Для всей системы
- Для конкретного пользователя
- Через командную строку
Использование парсера
Функция parse_environment позволяет парсить переменные окружения. У неё есть несколько перегруженных версий. Первый параметр — экземпляр options_description, второй определяет, какие переменные обрабатывать.
Соглашения об именовании
Для избежания конфликтов имён рекомендуется:
- Использовать уникальный префикс для переменных окружения
- Использовать верхний регистр для имён переменных
- Преобразовывать имена опций в нижний регистр
Пример:
- Опция:
proxy - Переменная окружения:
BOOST_PROXY
Настройка парсинга
При вызове parse_environment можно указать:
- Префикс для фильтрации переменных
- Функцию преобразования имён (принимает
std::stringи возвращаетstd::string)
Преобразование типов
Все значения из командной строки, переменных окружения и конфигурационных файлов изначально являются строками. Библиотека автоматически преобразует их в нужные типы.
Числовые типы
Преобразование выполняется через lexical_cast:
- Поддерживаются целые числа:
41,-42 - Поддерживаются числа с плавающей точкой:
51.1,-52.1,53.1234567890 - Поддерживаются экспоненциальные записи:
57.1e5,58.1E5
Булевы значения
Есть два способа работы с булевыми значениями:
- Через явное указание:
example --my-option=true
- Через переключатель:
example --other-switch
Значения, интерпретируемые как true:
trueyeson1
Значения, интерпретируемые как false:
falsenooff0
В конфигурационных файлах опция с именем и знаком равенства без значения также интерпретируется как true.
Важные замечания
- Шестнадцатеричные, восьмеричные и двоичные представления не поддерживаются
- Преобразование типов выполняется автоматически при обращении к значениям
- Рекомендуется явно указывать ожидаемый тип при описании опций