Этот раздел посвящен некоторым вопросам дизайна.
Поддержка Unicode была одной из функций, специально запрошенных во время официального обзора. На протяжении всего этого документа «Unicode support» является синонимом «wchar_t» поддержки, предполагая, что «wchar_t» всегда использует кодировку Unicode. Кроме того, говоря о «ascii» (в нижнем регистре), мы имеем в виду не строгое 7-битное кодирование ASCII, а скорее «char» строки в локальном 8-битном кодировании.
Как правило, «поддержка Unicode» может означать много вещей, но для библиотеки Program_options это означает, что:
Каждый парсер должен принять либо<char*
>, либо<wchar_t*
>, правильно разделить ввод на имена и значения опций и вернуть данные.
Для каждой опции должна быть возможность указать, использует ли преобразование из строки в значение ascii или Unicode.
Библиотека гарантирует, что:
ввод acii передается на значение acii без изменения.
Ввод Unicode передается на значение Unicode без изменения
ввод ascii передается значению Unicode, а ввод Unicode, передаваемый значению ascii, преобразуется с использованием аспекта codecvt (который может быть указан пользователем).
Важный момент заключается в том, что можно иметь некоторые «варианты ascii» вместе с «вариантами Unicode». Для этого есть две причины. Во-первых, для данного типа у вас может не быть кода для извлечения значения из строки Unicode, и не стоит требовать, чтобы такой код был написан. Во-вторых, представьте библиотеку многоразового использования, которая имеет некоторые опции и предоставляет описание опций в своем интерфейсе. Есливсеопции являются либо ascii, либо Unicode, и библиотека не использует никаких строк Unicode, то автор, вероятно, будет использовать опции ascii, делая библиотеку непригодной для использования внутри приложений Unicode. По сути, необходимо было бы предоставить две версии библиотеки — Ascii и Unicode.
Еще один важный момент заключается в том, что струны асци передаются без изменений. Другими словами, невозможно просто преобразовать асции в Unicode и обрабатывать Unicode дальше. Проблема в том, что механизм преобразования по умолчанию — аспект<codecvt
>— может не работать с 8-битным входом без дополнительной настройки.
Поддержка Unicode, описанная выше, не является полной. Например, мы не поддерживаем имена опций Unicode. Поддержка Unicode является сложной и требует решения Boost. Даже сравнение двух произвольных строк Unicode нетривиально. Наконец, использование Unicode в именах опций связано с интернационализацией, которая имеет свои сложности. Например, если имена опций зависят от текущего местоположения, то все части программы и другие части, которые используют название, также должны быть интернационализированы.
Основной вопрос при реализации поддержки Unicode заключается в том, использовать ли шаблоны и<std::basic_string
>или использовать некоторое внутреннее кодирование и преобразовывать между внутренним и внешним кодированием на границах интерфейса.
Выбор, в основном, между размером кода и скоростью выполнения. Шаблонное решение будет либо связывать библиотечный код с каждым приложением, которое использует библиотеку (тем самым делая общую библиотеку невозможной), либо предоставлять явные инстанциации в общей библиотеке (увеличивая ее размер). Решение, основанное на внутренней кодировке, обязательно сделает конверсии в нескольких местах и будет несколько медленнее. Поскольку скорость обычно не является проблемой для этой библиотеки, второе решение выглядит более привлекательным, но мы рассмотрим отдельные компоненты.
Для компонента парсеров у нас есть три варианта:
Используйте полностью шаблонную реализацию: при заданной строке определенного типа парсер вернет экземпляр<parsed_options
>со строками того же типа (т.е. класс<parsed_options
>будет шаблонизирован).
Используйте внутреннее кодирование: то же самое, что и выше, но строки будут преобразованы во внутреннее кодирование и из него.
Используйте и частично раскройте внутреннее кодирование: то же самое, что и выше, но строки в<parsed_options
>экземпляре будут во внутреннем кодировании. Это позволяет избежать конверсии, если экземпляр<parsed_options
>передается непосредственно другим компонентам, но также может быть опасным или запутанным для пользователя.
Второе решение кажется лучшим — оно не сильно увеличивает размер кода и чище третьего. Чтобы избежать дополнительных конверсий, версия Unicode<parsed_options
>также может хранить строки во внутреннем кодировании.
Для компонента описания опций у нас нет особого выбора. Поскольку не желательно, чтобы все опции использовали асции или все из них использовали Unicode, а скорее имели некоторые асции и некоторые опции Unicode, интерфейс<value_semantic
>должен работать с обоими. Единственный способ — передать дополнительный флаг, указывающий, используют ли строки асци или внутреннюю кодировку. При необходимости<value_semantic
>можно преобразовать в другое кодирование.
Для компонента хранения единственной функцией является<store
>. Для ввода Unicode функция<store
>должна преобразовывать значение во внутреннее кодирование. Он также должен информировать класс<value_semantic
>об используемом кодировании.
Наконец, какую внутреннюю кодировку мы должны использовать? Альтернативы:<std::wstring
>(с использованием кодирования UCS-4) и<std::string
>(с использованием кодирования UTF-8). Разница между альтернативами заключается в:
Скорость: UTF-8 немного медленнее
Пространство: UTF-8 занимает меньше места, когда вход асций
Размер кода: UTF-8 требует дополнительного кода преобразования. Однако он позволяет использовать существующие парсеры без их преобразования в<std::wstring
>, и такое преобразование, вероятно, создаст ряд новых инстанциаций.
Явного лидера нет, но последний пункт кажется важным, поэтому UTF-8 будет использоваться.
Выбор кодирования UTF-8 позволяет использовать существующие парсеры, поскольку 7-битные асции сохраняют свои значения в UTF-8, поэтому поиск 7-битных строк прост. Однако есть две тонкие проблемы:
Мы должны предположить, что буквальные символы используют кодирование асци и что входы используют кодирование Unicode.
За символом Unicode (скажем, «=») может следовать «составляющий символ», и комбинация не совпадает с просто «=», поэтому простой поиск «=» может найти неправильный символ.
Ни одна из этих проблем не является критической на практике, так как асции являются почти универсальным кодированием, и поскольку создание символов, следующих за «=» (и других символов, имеющих особое значение для библиотеки), вряд ли появится.