Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

How To

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 27. Boost.Program_options

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

How To

В этом разделе описывается, как библиотека может использоваться в определенных ситуациях.

Non-conventional Syntax

Иногда стандартных синтаксисов командной строки недостаточно. Например, компилятор gcc имеет опции «-frtti» и «-fno-rtti», и этот синтаксис не поддерживается напрямую.

Для таких случаев библиотека позволяет пользователю предоставить дополнительный парсер— функцию, которая будет вызываться на каждом элементе командной строки перед любой обработкой библиотекой. Если дополнительный парсер распознает синтаксис, он возвращает имя и значение опции, которые используются непосредственно. Вышеприведенный пример может быть обработан следующим кодом:

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());
    }
}

Вот определение дополнительного парсера. При разборе командной строки мы проходим дополнительный парсер:

store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
        .run(), vm);

Полный пример можно найти в файле "example/custom_syntax.cpp".

Response Files

Некоторые операционные системы имеют очень низкие пределы длины командной строки. Обычным способом обойти эти ограничения является использование файлов ответов.. Файл ответа — это просто файл конфигурации, который использует тот же синтаксис, что и командная строка. Если командная строка указывает имя файла ответа для использования, он загружается и анализируется в дополнение к командной строке. Библиотека не предоставляет прямой поддержки файлов ответов, поэтому вам нужно будет написать дополнительный код.

Для начала нужно определить опцию для файла ответа:

("response-file", value<string>(),
     "can be specified with '@name', too")

Во-вторых, вам понадобится дополнительный парсер для поддержки стандартного синтаксиса для указания файлов ответа: «@file»:

pair<string, string> at_option_parser(string const&s)
{
    if ('@' == s[0])
        return std::make_pair(string("response-file"), s.substr(1));
    else
        return pair<string, string>();
}

Наконец, когда будет найдена опция «ответ-файл», вам придется загрузить этот файл и передать его в парсер командной строки. Эта часть самая сложная. Будем использовать буст. Библиотека Токенайзера, которая работает, но имеет некоторые ограничения. Вы также можете рассмотреть Boost. СтрингАлго. Код такой:

if (vm.count("response-file")) {
     // Load the file and tokenize it
     ifstream ifs(vm["response-file"].as<string>().c_str());
     if (!ifs) {
         cout << "Could not open the response file\n";
         return 1;
     }
     // Read the whole file into a string
     stringstream ss;
     ss << ifs.rdbuf();
     // Split the file content
     char_separator<char> sep(" \n\r");
     std::string ResponsefileContents( ss.str() );
     tokenizer<char_separator<char> > tok(ResponsefileContents, sep);
     vector<string> args;
     copy(tok.begin(), tok.end(), back_inserter(args));
     // Parse the file and store the options
     store(command_line_parser(args).options(desc).run(), vm);
}

Полный пример можно найти в файле "example/response_file.cpp".

Winmain Command Line

В операционной системе Windows приложения GUI получают командную строку как единую строку, не разделенную на элементы. По этой причине парсер командной строки не может использоваться напрямую. По крайней мере, на некоторых компиляторах можно получить разделенную командную строку, но неясно, поддерживают ли все компиляторы один и тот же механизм на всех версиях операционной системы. Функция<split_winmain>является переносным механизмом, предоставляемым библиотекой.

Вот пример использования:

vector<string> args = split_winmain(lpCmdLine);
store(command_line_parser(args).options(desc).run(), vm);

Функция<split_winmain>перегружена для<wchar_t>строк, поэтому также может использоваться в приложениях Unicode.

Option Groups and Hidden Options

Наличие одного экземпляра класса<options_description>со всеми вариантами программы может быть проблематичным:

  • Некоторые опции имеют смысл только для конкретного источника, например, конфигурационных файлов.

  • Пользователь предпочёл бы некоторую структуру в генерируемом сообщении справки.

  • Некоторые опции вообще не должны появляться в генерируемом сообщении о помощи.

Для решения вышеуказанных задач библиотека позволяет программисту создать несколько экземпляров класса<options_description>, которые могут быть объединены в разные комбинации. В следующем примере будут определены три группы опций: специфичная командная строка и две группы опций для конкретных программных модулей, только одна из которых показана в генерируемом сообщении справки.

Каждая группа определяется с использованием стандартного синтаксиса. Однако для каждого случая следует использовать разумные имена<options_description>:

options_description general("General options");
general.add_options()
    ("help", "produce a help message")
    ("help-module", value<string>(),
        "produce a help for a given module")
    ("version", "output the version number")
    ;
options_description gui("GUI options");
gui.add_options()
    ("display", value<string>(), "display to use")
    ;
options_description backend("Backend options");
backend.add_options()
    ("num-threads", value<int>(), "the initial number of threads")
    ;

После объявления групп опционов мы объединяем их в две комбинации. Первый будет включать все варианты и использоваться для разбора. Второй вариант будет использоваться для «помощи».

// Declare an options description instance which will include
// all the options
options_description all("Allowed options");
all.add(general).add(gui).add(backend);
// Declare an options description instance which will be shown
// to the user
options_description visible("Allowed options");
visible.add(general).add(gui);

Остается только разобрать и обработать варианты:

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 << "Unknown module '"
             << s << "' in the --help-module option\n";
        return 1;
    }
    return 0;
}
if (vm.count("num-threads")) {
    cout << "The 'num-threads' options was set to "
         << vm["num-threads"].as<int>() << "\n";
}

При разборе командной строки допускаются все варианты. Сообщение «--помощь», однако, не включает группу «Backend options» — варианты в этой группе скрыты. Пользователь может явно форсировать отображение этой группы опций, передав опцию «--модуля поддержки backend». Полный пример можно найти в файле "example/option_groups.cpp".

Custom Validators

По умолчанию преобразование значения опции из строки в тип C++ осуществляется с помощью iostreams, что иногда не удобно. Библиотека позволяет пользователю настроить конверсию для конкретных классов. Для этого пользователь должен обеспечить соответствующую перегрузку функции<validate>.

Для начала определим простой класс:

struct magic_number {
public:
    magic_number(int n) : n(n) {}
    int n;
};

А затем перегрузить<validate>функцию:

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;
    // Make sure no previous assignment to 'a' was made.
    validators::check_first_occurrence(v);
    // Extract the first string from 'values'. If there is more than
    // one string, it's an error, and exception will be thrown.
    const string& s = validators::get_single_string(values);
    // Do regex match and convert the interesting part to
    // int.
    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);
    }
}

Функция имеет четыре параметра. Первое — это хранилище для значения, и в этом случае оно либо пусто, либо содержит экземпляр класса<magic_number>. Второй - список строк, найденных в следующем случае опции. Оставшиеся два параметра необходимы для обхода отсутствия частичной специализации шаблона и частичного заказа шаблона функции на некоторых компиляторах.

Функция сначала проверяет, что мы не пытаемся назначить одну и ту же опцию дважды. Затем он проверяет, что была пропущена только одна строка. Затем струна проверяется с помощью Boost. Библиотека Regex. Если этот тест пройден, парсинговое значение сохраняется в переменной<v>.

Полный пример можно найти в файле "example/regex.cpp".

Unicode Support

Чтобы использовать библиотеку с Unicode, вам нужно:

  • Используйте Unicode-aware парсеры для ввода Unicode

  • Требуется поддержка Unicode для опций, которые в ней нуждаются

Большинство парсеров имеют версии Unicode. Например, функция<parse_command_line>имеет перегрузку, которая принимает<wchar_t>строк, вместо обычной<char>.

Даже если некоторые из парсеров знакомы с Unicode, это не означает, что вам нужно изменить определение всех вариантов. На самом деле, для многих вариантов, таких как целые, это не имеет смысла. Для использования Unicode вам понадобитсяУникальные варианты. Они отличаются от обычных опций тем, что принимают<wstring>вход и обрабатывают его с помощью широких потоков символов. Создать опцию Unicode-aware легко: просто используйте функцию<wvalue>вместо обычной<value>.

Когда парсер асции передает данные опции асции, или парсер Unicode передает данные опции Unicode, данные не изменяются вообще. Таким образом, опция ascii будет видеть строку в локальном 8-битном кодировании, а опция Unicode будет видеть любую строку, пропущенную в качестве входа Unicode.

Что происходит, когда данные Unicode передаются в асции, и наоборот? Библиотека автоматически выполняет преобразование из Unicode в локальное 8-битное кодирование. Например, если командная строка находится в ascii, но вы используете<wstring>опции, то вход ascii будет преобразован в Unicode.

Чтобы выполнить преобразование, библиотека использует грань<codecvt<wchar_t, char>>от глобальной локации. Если вы хотите работать со строками, которые используют локальное 8-битное кодирование (в отличие от 7-битного подмножества), ваше приложение должно начинаться с:

locale::global(locale(""));
      

который настраивает грань конверсии в соответствии с выбранным пользователем местоположением.

Тем не менее, целесообразно проверить статус поддержки локализации C++ в вашей реализации. Быстрый тест включает три шага:

  1. Перейдите в каталог «test» и создайте двоичный файл «test_convert».

  2. Установите некоторое неасциальное место в окружающей среде. На Linux можно запускать, например:

    <
    $ export LC_CTYPE=ru_RU.KOI8-R
    
    >

  3. Запустите двоичную строку «test_convert» с любой неасциальной строкой в выбранной кодировке в качестве ее параметра. Если вы видите список кодовых точек Unicode, все в порядке. В противном случае локальная поддержка вашей системы может быть нарушена.

Allowing Unknown Options

Обычно библиотека выкладывает исключение на неизвестные имена вариантов. Такое поведение можно изменить. Например, только некоторая часть вашего приложения используетProgram_options, и вы хотите передать непризнанные варианты другой части программы или даже другому приложению.

Чтобы разрешить незарегистрированные опции в командной строке, вам нужно использовать класс<basic_command_line_parser>для разбора (не<parse_command_line>) и вызвать метод<allow_unregistered>этого класса:

parsed_options parsed =
    command_line_parser(argc, argv).options(desc).allow_unregistered().run();
      

Для каждого токена, который выглядит как опция, но не имеет известного имени, в результат будет добавлен экземпляр<basic_option>. Поля<string_key>и<value>экземпляра будут содержать результаты синтаксического разбора токена, поле<unregistered>будет установлено на<true>, а поле<original_tokens>будет содержать токен, как он появился в командной строке.

Если вы хотите пройти непризнанные опции дальше, можно использовать функцию<collect_unrecognized>. Функция будет собирать оригинальные токены для всех непризнанных значений и, необязательно, для всех найденных позиционных опций. Скажем, если ваш код обрабатывает несколько опций, но не обрабатывает позиционные опции вообще, вы можете использовать такую функцию:

vector<string> to_pass_further = collect_unrecognized(parsed.options, include_positional);
      


PrevUpHomeNext

Статья How To раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 27. Boost.Program_options может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 27. Boost.Program_options ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 17:10:28/0.031419038772583/1