## Общая архитектура библиотеки program_options ### Основные компоненты библиотеки Библиотека состоит из трёх главных компонентов: * **Компонент описания опций** — определяет допустимые параметры и действия с их значениями * **Компонент парсеров** — использует эту информацию для поиска имён параметров и их значений в источниках ввода * **Компонент хранения** — предоставляет интерфейс для доступа к значениям параметров и преобразует строковые представления в нужные типы C++ ### Ключевые классы и функции * Класс **options_description** относится к компоненту описания опций * Функция **parse_command_line** является частью компонента парсеров * Класс **variables_map** принадлежит компоненту хранения ### Взаимодействие компонентов В предыдущих примерах мы видели, как эти компоненты используются в функции main для парсинга командной строки и конфигурационных файлов. Рассмотрим теперь особенности работы вне функции main. ### Архитектура программы Для частей программы, находящихся вне main, наиболее важен **компонент хранения**. Он предоставляет класс, хранящий все значения параметров, который можно свободно передавать между модулями программы. Важные особенности: * Остальные компоненты используются только там, где выполняется парсинг * Отдельные модули программы могут описывать свои параметры и передавать их в основной модуль * Основной модуль может объединять все описания параметров * Такой подход особенно полезен при большом количестве параметров ### Рекомендации по использованию При разработке крупных приложений рекомендуется: * Централизовать описание параметров в одном месте * Использовать модульный подход при большом количестве опций * Передавать объект хранения между компонентами программы * Обеспечивать корректную обработку параметров во всех модулях ## Компонент описания опций ### Основные классы компонента Компонент описания опций включает три ключевых класса: * **option_description** — содержит имя опции, описание и указатель на value_semantic * **value_semantic** — знает тип значения опции, может парсить значение, применять значение по умолчанию * **options_description** — контейнер для экземпляров option_description ### Синтаксическое и семантическое описание #### Синтаксическая информация Включает: * Имя опции * Количество токенов для указания значения * Используется парсерами для группировки токенов в пары (имя, значение) #### Семантическая информация Отвечает за: * Преобразование значений в типы C++ * Валидацию данных * Применение значений по умолчанию ### Практическое применение Пример объявления опций: ```cpp options_description desc; desc.add_options() ("help", "показать справку") ("optimization", value()->default_value(10), "уровень оптимизации") ; ``` ### Механизм работы * Функция **value** создаёт экземпляр класса **typed_value** * **typed_value** — производный класс от **value_semantic** * Содержит код для парсинга значений определённого типа * Поддерживает методы для указания дополнительной информации ### Дополнительные возможности Библиотека предоставляет: * Функцию **bool_switch** для булевых значений * Возможность создания пользовательских подклассов **value_semantic** ### Важные аспекты проектирования Разделение на синтаксический и семантический уровни: * Парсеры работают только с синтаксическим уровнем * Ограничивает использование чрезмерно сложных структур * Пример проблемы: ```bash calc --expression=1 + 2/3 # сложно парсить без контекста ``` * Решённая версия: ```bash calc --expression="1 + 2/3" # синтаксис ясен ``` Такое разделение является ключевым элементом дизайна библиотеки, обеспечивая чёткое разделение ответственности между компонентами. ## Синтаксическая информация опций ### Основные компоненты Синтаксическая информация предоставляется классами **boost::program_options::options_description** и методами **boost::program_options::value_semantic**. Она включает: * **Имя опции** — используется для идентификации внутри программы * **Описание опции** — отображается пользователю * **Количество токенов** — определяет, сколько элементов может содержать значение опции при парсинге ### Примеры использования Рассмотрим практический пример объявления опций: ```cpp options_description desc; desc.add_options() ("help", "показать справочное сообщение") ("compression", value(), "уровень сжатия") ("verbose", value()->implicit_value("0"), "уровень детализации") ("email", value()->multitoken(), "email для отправки") ; ``` Особенности каждой опции: * Первая опция (`help`) не требует значения * Вторая опция (`compression`) требует ровно один токен значения * Третья опция (`verbose`) может принимать либо один токен, либо не принимать значение вообще * Четвёртая опция (`email`) может принимать несколько токенов ### Пример командной строки Допустимый вариант использования опций: ```bash test --help --compression 10 --verbose --email beadle@mars beadle2@mars ``` ### Важные характеристики * **Неявные значения** (implicit_value) позволяют опции иметь значение по умолчанию при отсутствии явного указания * **Многокомпонентные значения** (multitoken) позволяют опции принимать несколько токенов * Каждая опция может иметь собственное правило обработки значений Такой подход обеспечивает гибкость при определении правил обработки параметров командной строки и конфигурационных файлов. ## Форматирование описаний опций ### Базовое форматирование Описания опций могут быть достаточно объёмными, особенно когда требуется документировать несколько значений. Библиотека предоставляет простые механизмы форматирования. ### Структура описания * Описание состоит из одного или нескольких параграфов, разделённых символом новой строки (`\n`) * При выводе библиотека автоматически вычисляет отступы для описания опции * Каждый параграф выводится как отдельная строка с рассчитанным отступом * Если параграф не помещается в одну строку, он переносится на несколько строк с одинаковым отступом ### Управление отступами #### Базовый отступ Можно задать дополнительный отступ для первой строки, добавив пробелы в начале параграфа: ```cpp options.add_options() ("help", " Подробное описание опции, которое может быть очень длинным и занимать несколько строк") ; ``` Результат будет выглядеть так: ``` --help Подробное описание опции, которое может быть очень длинным и занимать несколько строк ``` ### Расширенное форматирование Для более сложного форматирования можно использовать символ табуляции (`\t`): ```cpp 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**. Рассмотрим пример: ```cpp options_description desc; desc.add_options() ("compression", value()->default_value(10), "уровень сжатия") ("email", value>() ->composing()->notifier(&your_function), "email") ; ``` Этот код определяет: * Значение по умолчанию для первой опции — 10 * Вторая опция может появляться несколько раз, и все значения будут объединены * После парсинга будет вызвана функция `your_function` с значением опции "email" ### Позиционные опции **Позиционные опции** — это элементы командной строки без явного указания имени опции. Например: ```bash archiver --compression=9 /etc/passwd ``` Здесь `/etc/passwd` — позиционная опция. Для обработки таких опций используется класс **positional_options_description**: ```cpp positional_options_description pd; pd.add("input-file", 1); // Первая позиционная опция будет иметь имя "input-file" ``` Можно задать несколько позиционных опций: ```cpp positional_options_description pd; pd.add("output-file", 2).add("input-file", -1); ``` В этом примере: * Первые две позиционные опции получат имя "output-file" * Все последующие — имя "input-file" **Важное замечание**: класс **positional_options_description** только определяет соответствие позиции имени, само имя должно быть зарегистрировано в экземпляре **options_description**. ### Компонент парсеров **Компонент парсеров** выполняет следующие функции: * Разбивает входные данные на пары (имя, значение) * Проверяет, известны ли опции * Определяет способ указания значений Процесс парсинга: 1. Парсер ищет возможные опции 2. Сверяется с описанием опций 3. Определяет, известно ли имя опции 4. Использует **value_semantic** для обработки значений ### Особенности парсинга Три важных исключения из стандартной модели: 1. **Короткие имена опций**: * Можно использовать сокращённые имена * Поддерживается автоматическое определение по префиксам 2. **Многокомпонентные значения**: * Опция может принимать несколько токенов * Требует явного включения 3. **Позиционные опции**: * Механизм автоматического определения имён * Гибкая настройка через **positional_options_description** ### Использование парсеров Для запуска парсера обычно вызывается функция с параметрами: * Описание опций * Источник данных (командная строка, конфигурационный файл) Результаты парсинга возвращаются в виде объекта класса **parsed_options**, который передаётся компоненту хранения. ## Компонент хранения ### Основные функции Компонент хранения отвечает за: * Хранение конечных значений опций в специальном классе и обычных переменных * Управление приоритетами между различными источниками * Вызов пользовательских функций уведомления с финальными значениями опций ### Пример использования ```cpp 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]` * Символ `#` начинает комментарий до конца строки ### Иерархия опций Имена опций относительны к именам секций. Например: ```ini [gui.accessibility] visual_bell=yes ``` эквивалентно: ```ini gui.accessibility.visual_bell=yes ``` ### Пример описания опции ```cpp options_description desc; desc.add_options() ("gui.accessibility.visual_bell", value(), "визуальный звонок") ; ``` ### Парсер переменных окружения Библиотека также поддерживает парсинг переменных окружения, что позволяет: * Использовать значения из окружения системы * Объединять их с другими источниками конфигурации * Управлять поведением программы через системные переменные Такой подход обеспечивает гибкую систему конфигурации приложения с возможностью комбинирования различных источников настроек. ## Парсер переменных окружения ### Что такое переменные окружения **Переменные окружения** — это строковые переменные, доступные всем программам через функцию `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` #### Булевы значения Есть два способа работы с булевыми значениями: 1. Через явное указание: ```bash example --my-option=true ``` 2. Через переключатель: ```bash example --other-switch ``` Значения, интерпретируемые как true: * `true` * `yes` * `on` * `1` Значения, интерпретируемые как false: * `false` * `no` * `off` * `0` В конфигурационных файлах опция с именем и знаком равенства без значения также интерпретируется как true. ### Важные замечания * Шестнадцатеричные, восьмеричные и двоичные представления не поддерживаются * Преобразование типов выполняется автоматически при обращении к значениям * Рекомендуется явно указывать ожидаемый тип при описании опций