## Нестандартные случаи использования библиотеки ### Нестандартный синтаксис Иногда требуется поддержка нестандартного синтаксиса командных строк. Например, как в компиляторе gcc с опциями `-frtti` и `-fno-rtti`. Для таких случаев библиотека позволяет предоставить дополнительный парсер — функцию, которая вызывается для каждого элемента командной строки. Пример реализации: ```cpp pair 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(), "can be specified with '@name', too") ``` 2. Дополнительный парсер для синтаксиса `@file`: ```cpp pair 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().c_str()); if (!ifs) { cout << "Could not open the response file\n"; return 1; } stringstream ss; ss << ifs.rdbuf(); char_separator sep(" \n\r"); string ResponsefileContents(ss.str()); tokenizer> tok(ResponsefileContents, sep); vector 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 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(), "показать справку по модулю") ("version", "показать версию"); // Группа опций GUI options_description gui("Опции GUI"); gui.add_options() ("display", value(), "используемый дисплей"); // Группа опций бэкенда options_description backend("Опции бэкенда"); backend.add_options() ("num-threads", value(), "начальное число потоков"); ``` ### Объединение групп Создаём два объединения: * Полное для парсинга * Видимое для справки ```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(); 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& 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(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 to_pass_further = collect_unrecognized( parsed.options, include_positional ); ``` ## Проверка наличия опций ### Традиционный подход Ранее проверка осуществлялась через метод **count**: ```cpp if (vm.count("compression")) { cout << "Уровень сжатия установлен: " << vm["compression"].as() << endl; } ``` ### Улучшенный подход с boost::optional Использование **boost::optional** позволяет: * Автоматически инициализировать переменную при наличии опции * Упростить проверку наличия опции Пример: ```cpp boost::optional compression; desc.add_options() ("compression", po::value(&compression), "уровень сжатия"); // После парсинга и уведомления if (compression) { cout << "Уровень сжатия установлен: " << *compression << endl; } else { cout << "Уровень сжатия не установлен" << endl; } ``` Преимущества подхода с **boost::optional**: * Меньше вероятность ошибок при переименовании опций * Более чистый код * Автоматическая обработка наличия значения Такой подход рекомендуется использовать для проверки наличия опций в программе.