Неточный перевод
This commit is contained in:
77
boost/1.89.0/design_discussion.md
Normal file
77
boost/1.89.0/design_discussion.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
## Обсуждение дизайна библиотеки
|
||||||
|
|
||||||
|
### Поддержка Unicode
|
||||||
|
|
||||||
|
**Unicode-поддержка** в библиотеке означает:
|
||||||
|
* Возможность парсинга как char*, так и wchar_t*
|
||||||
|
* Возможность указания типа обработки для каждой опции (ASCII или Unicode)
|
||||||
|
* Гарантии корректной конвертации данных
|
||||||
|
|
||||||
|
### Основные гарантии библиотеки
|
||||||
|
|
||||||
|
* ASCII-вход передается в ASCII-значение без изменений
|
||||||
|
* Unicode-вход передается в Unicode-значение без изменений
|
||||||
|
* При несовпадении типов происходит автоматическая конвертация через codecvt
|
||||||
|
|
||||||
|
### Особенности реализации
|
||||||
|
|
||||||
|
**Смешанная поддержка** ASCII и Unicode:
|
||||||
|
* Возможность использования обоих типов опций одновременно
|
||||||
|
* Отсутствие необходимости писать дополнительный код для Unicode
|
||||||
|
* Совместимость с существующими библиотеками
|
||||||
|
|
||||||
|
### Проблемы реализации
|
||||||
|
|
||||||
|
**Основные сложности**:
|
||||||
|
* Сравнение Unicode-строк
|
||||||
|
* Интернационализация имен опций
|
||||||
|
* Необходимость дополнительных настроек для корректной работы
|
||||||
|
|
||||||
|
### Подход к реализации
|
||||||
|
|
||||||
|
**Варианты реализации**:
|
||||||
|
1. Шаблонное решение с std::basic_string
|
||||||
|
2. Внутренняя кодировка с конвертацией на границах
|
||||||
|
|
||||||
|
**Выбранный подход**:
|
||||||
|
* Использование внутренней кодировки UTF-8
|
||||||
|
* Конвертация только при необходимости
|
||||||
|
* Минимизация накладных расходов
|
||||||
|
|
||||||
|
### Компоненты системы
|
||||||
|
|
||||||
|
**Парсеры**:
|
||||||
|
* Работа с входными строками
|
||||||
|
* Конвертация в internal encoding
|
||||||
|
* Хранение результатов в parsed_options
|
||||||
|
|
||||||
|
**Описания опций**:
|
||||||
|
* Поддержка смешанных типов
|
||||||
|
* Дополнительная информация о кодировке
|
||||||
|
* Гибкая обработка значений
|
||||||
|
|
||||||
|
**Хранение данных**:
|
||||||
|
* Конвертация при сохранении
|
||||||
|
* Информирование о типе кодировки
|
||||||
|
* Корректная работа с разными типами
|
||||||
|
|
||||||
|
### Выбор внутренней кодировки
|
||||||
|
|
||||||
|
**Критерии выбора**:
|
||||||
|
* Скорость обработки
|
||||||
|
* Затраты памяти
|
||||||
|
* Размер кода
|
||||||
|
|
||||||
|
**Принятое решение**: UTF-8 как внутренняя кодировка из-за:
|
||||||
|
* Универсальности
|
||||||
|
* Эффективности для ASCII-данных
|
||||||
|
* Простоты интеграции с существующими компонентами
|
||||||
|
|
||||||
|
### Особые случаи
|
||||||
|
|
||||||
|
**Важные моменты**:
|
||||||
|
* Предполагается ASCII-кодировка для символьных литералов
|
||||||
|
* Учет возможных проблем с комбинирующими символами
|
||||||
|
* Практическая нерелевантность проблем в большинстве случаев
|
||||||
|
|
||||||
|
Такой подход обеспечивает баланс между функциональностью, производительностью и простотой использования библиотеки.
|
||||||
333
boost/1.89.0/how_to.md
Normal file
333
boost/1.89.0/how_to.md
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
## Нестандартные случаи использования библиотеки
|
||||||
|
|
||||||
|
### Нестандартный синтаксис
|
||||||
|
|
||||||
|
Иногда требуется поддержка нестандартного синтаксиса командных строк. Например, как в компиляторе gcc с опциями `-frtti` и `-fno-rtti`.
|
||||||
|
|
||||||
|
Для таких случаев библиотека позволяет предоставить дополнительный парсер — функцию, которая вызывается для каждого элемента командной строки.
|
||||||
|
|
||||||
|
Пример реализации:
|
||||||
|
```cpp
|
||||||
|
pair<string, string> reg_foo(const string& s) {
|
||||||
|
if (s.find("-f") == 0) {
|
||||||
|
if (s.substr(2, 3) == "no-")
|
||||||
|
return make_pair(s.substr(5), string("false"));
|
||||||
|
else
|
||||||
|
return make_pair(s.substr(2), string("true"));
|
||||||
|
} else {
|
||||||
|
return make_pair(string(), string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Использование дополнительного парсера:
|
||||||
|
```cpp
|
||||||
|
store(command_line_parser(ac, av)
|
||||||
|
.options(desc)
|
||||||
|
.extra_parser(reg_foo)
|
||||||
|
.run(), vm);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Файлы ответов (Response Files)
|
||||||
|
|
||||||
|
Для обхода ограничений длины командной строки используются файлы ответов.
|
||||||
|
|
||||||
|
Шаги реализации:
|
||||||
|
|
||||||
|
1. Определение опции для файла ответов:
|
||||||
|
```cpp
|
||||||
|
("response-file", value<string>(), "can be specified with '@name', too")
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Дополнительный парсер для синтаксиса `@file`:
|
||||||
|
```cpp
|
||||||
|
pair<string, string> at_option_parser(string const&s) {
|
||||||
|
if ('@' == s[0])
|
||||||
|
return make_pair(string("response-file"), s.substr(1));
|
||||||
|
else
|
||||||
|
return make_pair(string(), string());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Обработка найденного файла ответов:
|
||||||
|
```cpp
|
||||||
|
if (vm.count("response-file")) {
|
||||||
|
ifstream ifs(vm["response-file"].as<string>().c_str());
|
||||||
|
if (!ifs) {
|
||||||
|
cout << "Could not open the response file\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
stringstream ss;
|
||||||
|
ss << ifs.rdbuf();
|
||||||
|
|
||||||
|
char_separator<char> sep(" \n\r");
|
||||||
|
string ResponsefileContents(ss.str());
|
||||||
|
tokenizer<char_separator<char>> tok(ResponsefileContents, sep);
|
||||||
|
|
||||||
|
vector<string> args;
|
||||||
|
copy(tok.begin(), tok.end(), back_inserter(args));
|
||||||
|
|
||||||
|
store(command_line_parser(args).options(desc).run(), vm);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Другие возможности
|
||||||
|
|
||||||
|
* **Winmain Command Line** — поддержка специфичного синтаксиса Windows
|
||||||
|
* **Группы опций и скрытые опции** — организация опций в логические группы
|
||||||
|
* **Пользовательские валидаторы** — создание собственных правил проверки значений
|
||||||
|
* **Поддержка Unicode** — работа с Unicode-строками
|
||||||
|
* **Неизвестные опции** — возможность разрешать неизвестные опции
|
||||||
|
* **Проверка наличия опций** — механизмы проверки присутствия опций
|
||||||
|
|
||||||
|
Эти возможности позволяют гибко настраивать поведение библиотеки под конкретные требования проекта.
|
||||||
|
|
||||||
|
|
||||||
|
## Работа с командной строкой в Windows
|
||||||
|
|
||||||
|
### Особенности Winmain
|
||||||
|
|
||||||
|
В Windows GUI-приложения получают командную строку как единую строку, а не как массив элементов. Для решения этой проблемы библиотека предоставляет функцию **split_winmain**.
|
||||||
|
|
||||||
|
Пример использования:
|
||||||
|
```cpp
|
||||||
|
vector<string> args = split_winmain(lpCmdLine);
|
||||||
|
store(command_line_parser(args).options(desc).run(), vm);
|
||||||
|
```
|
||||||
|
|
||||||
|
Функция **split_winmain** имеет перегрузку для строк **wchar_t**, что позволяет использовать её в Unicode-приложениях.
|
||||||
|
|
||||||
|
## Группы опций и скрытые опции
|
||||||
|
|
||||||
|
### Проблемы единого описания опций
|
||||||
|
|
||||||
|
При использовании единого экземпляра **options_description** возникают сложности:
|
||||||
|
* Некоторые опции имеют смысл только для определённых источников
|
||||||
|
* Требуется структурированная справка
|
||||||
|
* Некоторые опции не должны отображаться в справке
|
||||||
|
|
||||||
|
### Решение через группы опций
|
||||||
|
|
||||||
|
Библиотека позволяет создавать несколько экземпляров **options_description** и объединять их по необходимости.
|
||||||
|
|
||||||
|
Пример группировки:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Общая группа опций
|
||||||
|
options_description general("Общие опции");
|
||||||
|
general.add_options()
|
||||||
|
("help", "показать справку")
|
||||||
|
("help-module", value<string>(), "показать справку по модулю")
|
||||||
|
("version", "показать версию");
|
||||||
|
|
||||||
|
// Группа опций GUI
|
||||||
|
options_description gui("Опции GUI");
|
||||||
|
gui.add_options()
|
||||||
|
("display", value<string>(), "используемый дисплей");
|
||||||
|
|
||||||
|
// Группа опций бэкенда
|
||||||
|
options_description backend("Опции бэкенда");
|
||||||
|
backend.add_options()
|
||||||
|
("num-threads", value<int>(), "начальное число потоков");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Объединение групп
|
||||||
|
|
||||||
|
Создаём два объединения:
|
||||||
|
* Полное для парсинга
|
||||||
|
* Видимое для справки
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Все опции для парсинга
|
||||||
|
options_description all("Разрешённые опции");
|
||||||
|
all.add(general).add(gui).add(backend);
|
||||||
|
|
||||||
|
// Видимые опции для справки
|
||||||
|
options_description visible("Разрешённые опции");
|
||||||
|
visible.add(general).add(gui);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обработка опций
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
variables_map vm;
|
||||||
|
store(parse_command_line(ac, av, all), vm);
|
||||||
|
|
||||||
|
if (vm.count("help")) {
|
||||||
|
cout << visible;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("help-module")) {
|
||||||
|
const string& s = vm["help-module"].as<string>();
|
||||||
|
if (s == "gui") {
|
||||||
|
cout << gui;
|
||||||
|
} else if (s == "backend") {
|
||||||
|
cout << backend;
|
||||||
|
} else {
|
||||||
|
cout << "Неизвестный модуль '" << s << "'\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Такой подход позволяет:
|
||||||
|
* Структурировать опции по группам
|
||||||
|
* Контролировать видимость опций в справке
|
||||||
|
* Гибко настраивать поведение программы
|
||||||
|
|
||||||
|
|
||||||
|
## Пользовательские валидаторы
|
||||||
|
|
||||||
|
### Настройка конвертации значений
|
||||||
|
|
||||||
|
По умолчанию конвертация значений опций выполняется через iostreams. Библиотека позволяет настроить собственную конвертацию для конкретных классов.
|
||||||
|
|
||||||
|
Пример создания пользовательского валидатора:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct magic_number {
|
||||||
|
public:
|
||||||
|
magic_number(int n) : n(n) {}
|
||||||
|
int n;
|
||||||
|
};
|
||||||
|
|
||||||
|
void validate(boost::any& v,
|
||||||
|
const std::vector<std::string>& values,
|
||||||
|
magic_number* target_type, int)
|
||||||
|
{
|
||||||
|
static regex r("\\d\\d\\d-(\\d\\d\\d)");
|
||||||
|
using namespace boost::program_options;
|
||||||
|
|
||||||
|
// Проверка на множественные присваивания
|
||||||
|
validators::check_first_occurrence(v);
|
||||||
|
|
||||||
|
// Получение единственной строки
|
||||||
|
const string& s = validators::get_single_string(values);
|
||||||
|
|
||||||
|
// Проверка через регулярное выражение
|
||||||
|
smatch match;
|
||||||
|
if (regex_match(s, match, r)) {
|
||||||
|
v = any(magic_number(lexical_cast<int>(match[1])));
|
||||||
|
} else {
|
||||||
|
throw validation_error(validation_error::invalid_option_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Параметры функции валидации:
|
||||||
|
* **v** — хранилище для значения
|
||||||
|
* **values** — список строк
|
||||||
|
* **target_type** — указатель на целевой тип
|
||||||
|
* Дополнительный параметр для совместимости с компиляторами
|
||||||
|
|
||||||
|
### Поддержка Unicode
|
||||||
|
|
||||||
|
Для работы с Unicode необходимо:
|
||||||
|
* Использовать Unicode-совместимые парсеры
|
||||||
|
* Указывать поддержку Unicode для нужных опций
|
||||||
|
|
||||||
|
Особенности работы:
|
||||||
|
* Большинство парсеров имеют Unicode-версии
|
||||||
|
* Для создания Unicode-опций используется **wvalue** вместо **value**
|
||||||
|
* Автоматическое преобразование кодировок между Unicode и локальными
|
||||||
|
|
||||||
|
### Настройка локализаций
|
||||||
|
|
||||||
|
Для корректной работы с Unicode:
|
||||||
|
```cpp
|
||||||
|
locale::global(locale("")); // Настройка конвертации согласно локали пользователя
|
||||||
|
```
|
||||||
|
|
||||||
|
### Тестирование поддержки локализаций
|
||||||
|
|
||||||
|
Проверка включает:
|
||||||
|
1. Сборка теста **test_convert**
|
||||||
|
2. Установка не-ASCII локали:
|
||||||
|
```bash
|
||||||
|
export LC_CTYPE=ru_RU.KOI8-R
|
||||||
|
```
|
||||||
|
3. Запуск теста с не-ASCII строкой
|
||||||
|
|
||||||
|
При успешной работе вы увидите список Unicode-кодов, что подтверждает корректную поддержку локализаций.
|
||||||
|
|
||||||
|
Такой подход позволяет:
|
||||||
|
* Настраивать собственную логику валидации
|
||||||
|
* Работать с Unicode-данными
|
||||||
|
* Обеспечивать корректную конвертацию кодировок
|
||||||
|
|
||||||
|
## Разрешение неизвестных опций
|
||||||
|
|
||||||
|
### Стандартное поведение
|
||||||
|
|
||||||
|
По умолчанию библиотека генерирует исключение при обнаружении неизвестных опций. Однако это поведение можно изменить.
|
||||||
|
|
||||||
|
### Разрешение неизвестных опций
|
||||||
|
|
||||||
|
Для разрешения неизвестных опций:
|
||||||
|
1. Используйте **basic_command_line_parser** вместо **parse_command_line**
|
||||||
|
2. Вызовите метод **allow_unregistered**
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
```cpp
|
||||||
|
parsed_options parsed = command_line_parser(argc, argv)
|
||||||
|
.options(desc)
|
||||||
|
.allow_unregistered()
|
||||||
|
.run();
|
||||||
|
```
|
||||||
|
|
||||||
|
При обнаружении неизвестной опции создается объект **basic_option** с полями:
|
||||||
|
* **string_key** — имя опции
|
||||||
|
* **value** — значение опции
|
||||||
|
* **unregistered** — флаг неизвестной опции
|
||||||
|
* **original_tokens** — исходные токены
|
||||||
|
|
||||||
|
### Передача неизвестных опций
|
||||||
|
|
||||||
|
Для сбора неизвестных опций используйте функцию **collect_unrecognized**:
|
||||||
|
```cpp
|
||||||
|
vector<string> to_pass_further = collect_unrecognized(
|
||||||
|
parsed.options,
|
||||||
|
include_positional
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Проверка наличия опций
|
||||||
|
|
||||||
|
### Традиционный подход
|
||||||
|
|
||||||
|
Ранее проверка осуществлялась через метод **count**:
|
||||||
|
```cpp
|
||||||
|
if (vm.count("compression")) {
|
||||||
|
cout << "Уровень сжатия установлен: " << vm["compression"].as<int>() << endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Улучшенный подход с boost::optional
|
||||||
|
|
||||||
|
Использование **boost::optional** позволяет:
|
||||||
|
* Автоматически инициализировать переменную при наличии опции
|
||||||
|
* Упростить проверку наличия опции
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
```cpp
|
||||||
|
boost::optional<int> compression;
|
||||||
|
desc.add_options()
|
||||||
|
("compression", po::value(&compression), "уровень сжатия");
|
||||||
|
|
||||||
|
// После парсинга и уведомления
|
||||||
|
if (compression) {
|
||||||
|
cout << "Уровень сжатия установлен: " << *compression << endl;
|
||||||
|
} else {
|
||||||
|
cout << "Уровень сжатия не установлен" << endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Преимущества подхода с **boost::optional**:
|
||||||
|
* Меньше вероятность ошибок при переименовании опций
|
||||||
|
* Более чистый код
|
||||||
|
* Автоматическая обработка наличия значения
|
||||||
|
|
||||||
|
Такой подход рекомендуется использовать для проверки наличия опций в программе.
|
||||||
@@ -1,2 +1,7 @@
|
|||||||
|
# Оглавление
|
||||||
|
|
||||||
- [Introducion](introduction.md)
|
- [Введение](introduction.md)
|
||||||
|
- [Туториал](tutorial.md)
|
||||||
|
- [Обзор библиотеки](library_overview.md)
|
||||||
|
- [How To](how_to.md)
|
||||||
|
- [Обсуждение дизайна](design_discussion.md)
|
||||||
|
|||||||
451
boost/1.89.0/library_overview.md
Normal file
451
boost/1.89.0/library_overview.md
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
## Общая архитектура библиотеки 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<int>()->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<string>(), "уровень сжатия")
|
||||||
|
("verbose", value<string>()->implicit_value("0"), "уровень детализации")
|
||||||
|
("email", value<string>()->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<int>()->default_value(10), "уровень сжатия")
|
||||||
|
("email", value<vector<string>>()
|
||||||
|
->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<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`
|
||||||
|
|
||||||
|
#### Булевы значения
|
||||||
|
|
||||||
|
Есть два способа работы с булевыми значениями:
|
||||||
|
|
||||||
|
1. Через явное указание:
|
||||||
|
```bash
|
||||||
|
example --my-option=true
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Через переключатель:
|
||||||
|
```bash
|
||||||
|
example --other-switch
|
||||||
|
```
|
||||||
|
|
||||||
|
Значения, интерпретируемые как true:
|
||||||
|
* `true`
|
||||||
|
* `yes`
|
||||||
|
* `on`
|
||||||
|
* `1`
|
||||||
|
|
||||||
|
Значения, интерпретируемые как false:
|
||||||
|
* `false`
|
||||||
|
* `no`
|
||||||
|
* `off`
|
||||||
|
* `0`
|
||||||
|
|
||||||
|
В конфигурационных файлах опция с именем и знаком равенства без значения также интерпретируется как true.
|
||||||
|
|
||||||
|
### Важные замечания
|
||||||
|
|
||||||
|
* Шестнадцатеричные, восьмеричные и двоичные представления не поддерживаются
|
||||||
|
* Преобразование типов выполняется автоматически при обращении к значениям
|
||||||
|
* Рекомендуется явно указывать ожидаемый тип при описании опций
|
||||||
118
boost/1.89.0/tutorial.md
Normal file
118
boost/1.89.0/tutorial.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
## Основные сценарии использования библиотеки program_options
|
||||||
|
|
||||||
|
### Начало работы
|
||||||
|
|
||||||
|
В этом разделе мы рассмотрим простейший пример использования библиотеки. Предполагается, что действует следующий алиас пространства имён:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Пример базового использования
|
||||||
|
|
||||||
|
Простейший пример, обрабатывающий два параметра:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Объявление поддерживаемых параметров
|
||||||
|
po::options_description desc("Допустимые параметры");
|
||||||
|
desc.add_options()
|
||||||
|
("help", "показать справочную информацию")
|
||||||
|
("compression", po::value<int>(), "установить уровень сжатия");
|
||||||
|
|
||||||
|
po::variables_map vm;
|
||||||
|
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||||
|
po::notify(vm);
|
||||||
|
|
||||||
|
if (vm.count("help")) {
|
||||||
|
cout << desc << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("compression")) {
|
||||||
|
cout << "Уровень сжатия установлен на "
|
||||||
|
<< vm["compression"].as<int>() << ".\n";
|
||||||
|
} else {
|
||||||
|
cout << "Уровень сжатия не установлен.\n";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Детали работы с параметрами
|
||||||
|
|
||||||
|
#### Типы и свойства параметров
|
||||||
|
|
||||||
|
Рассмотрим более сложный пример на основе компилятора:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int opt;
|
||||||
|
po::options_description desc("Допустимые параметры");
|
||||||
|
desc.add_options()
|
||||||
|
("help", "показать справочную информацию")
|
||||||
|
("optimization", po::value<int>(&opt)->default_value(10), "уровень оптимизации")
|
||||||
|
("include-path,I", po::value<vector<string>>(), "путь включения")
|
||||||
|
("input-file", po::value<vector<string>>(), "входной файл");
|
||||||
|
```
|
||||||
|
|
||||||
|
Особенности этого примера:
|
||||||
|
* Параметр `optimization` имеет значение по умолчанию 10
|
||||||
|
* Параметр `include-path` поддерживает короткое имя `-I`
|
||||||
|
* Векторные типы позволяют указывать параметр несколько раз
|
||||||
|
|
||||||
|
#### Позиционные параметры
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
po::positional_options_description p;
|
||||||
|
p.add("input-file", -1);
|
||||||
|
|
||||||
|
po::variables_map vm;
|
||||||
|
po::store(po::command_line_parser(ac, av)
|
||||||
|
.options(desc).positional(p).run(), vm);
|
||||||
|
po::notify(vm);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Использование нескольких источников
|
||||||
|
|
||||||
|
#### Группировка параметров
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Общие параметры командной строки
|
||||||
|
po::options_description generic("Общие параметры");
|
||||||
|
generic.add_options()
|
||||||
|
("version,v", "показать версию")
|
||||||
|
("help", "показать справку");
|
||||||
|
|
||||||
|
// Параметры конфигурации
|
||||||
|
po::options_description config("Конфигурация");
|
||||||
|
config.add_options()
|
||||||
|
("optimization", po::value<int>(&opt)->default_value(10), "уровень оптимизации")
|
||||||
|
("include-path,I", po::value<vector<string>>()->composing(), "путь включения");
|
||||||
|
|
||||||
|
// Скрытые параметры
|
||||||
|
po::options_description hidden("Скрытые параметры");
|
||||||
|
hidden.add_options()
|
||||||
|
("input-file", po::value<vector<string>>(), "входной файл");
|
||||||
|
```
|
||||||
|
|
||||||
|
Пример использования:
|
||||||
|
```bash
|
||||||
|
$ bin/gcc/debug/multiple_sources
|
||||||
|
Пути включения: /opt
|
||||||
|
Уровень оптимизации: 1
|
||||||
|
|
||||||
|
$ bin/gcc/debug/multiple_sources --help
|
||||||
|
Допустимые параметры:
|
||||||
|
|
||||||
|
Общие параметры:
|
||||||
|
-v [ --версия ] : показать версию
|
||||||
|
--help : показать справку
|
||||||
|
|
||||||
|
Конфигурация:
|
||||||
|
--оптимизация n : уровень оптимизации
|
||||||
|
-I [ --путь-включения ] путь : путь включения
|
||||||
|
|
||||||
|
$ bin/gcc/debug/multiple_sources --оптимизация=4 -I foo a.cpp b.cpp
|
||||||
|
Пути включения: foo /opt
|
||||||
|
Входные файлы: a.cpp b.cpp
|
||||||
|
Уровень оптимизации: 4
|
||||||
|
```
|
||||||
|
|
||||||
|
В этом примере значения из конфигурационного файла и командной строки корректно объединяются, при этом значения из командной строки имеют приоритет.
|
||||||
Reference in New Issue
Block a user