Исключения с Unicode что () - PullRequest
       0

Исключения с Unicode что ()

54 голосов
/ 21 сентября 2010

Или «как россияне бросают исключения?»

Определение std :: exception:

namespace std {
  class exception {
  public:
    exception() throw();
    exception(const exception&) throw();
    exception& operator=(const exception&) throw();
    virtual ~exception() throw();
    virtual const char* what() const throw();
  };
}

A популярная школа мысли для разработки иерархий исключений - это производная от std :: exception:

Как правило, лучше бросать предметы, не встроенные Если возможно, вы должны бросить экземпляры классов, которые выводят (в конечном итоге) из std :: исключение учебный класс. Делая свой класс исключения наследовать (в конечном итоге) от стандарта исключение базового класса, вы делаете жизнь проще для ваших пользователей (у них есть возможность ловить большинство вещей через std :: исключение), плюс вы, вероятно, предоставляя им больше информации (например, тот факт, что ваш конкретный Исключением может быть уточнение std :: runtime_error или любой другой) .std :: runtime_error или любой другой).

Но, учитывая Unicode, кажется, что невозможно разработать иерархию исключений, которая бы обеспечивала оба следующих условия:

  • В конечном итоге извлекается из std :: exception для простоты использования на улове
  • Обеспечивает совместимость с Юникодом, так что диагностика не ломается и не таращится

Создать класс исключений, который может быть сконструирован из строк Unicode, достаточно просто. Но стандарт предписывает, что what () должен возвращать const char *, поэтому в какой-то момент входные строки должны быть преобразованы в ASCII. Независимо от того, делается ли это во время создания или когда вызывается метод what () (если в исходной строке используются символы, не представляемые 7-битным ASCII), может быть невозможно отформатировать сообщение без потери точности.

Как спроектировать иерархию исключений, которая сочетает в себе плавную интеграцию класса, полученного из std :: exception, с диагностикой Unicode без потерь?

Ответы [ 8 ]

34 голосов
/ 21 сентября 2010

char * не означает ASCII. Вы можете использовать 8-битную кодировку Unicode, например UTF-8. Символьный также может быть 16 бит или более, вы можете использовать UTF-16.

8 голосов
/ 21 сентября 2010

Возвращение UTF-8 является очевидным выбором.Если приложение, использующее ваши исключения, использует другую многобайтовую кодировку, может возникнуть проблема с отображением строки.(Он не может знать, что это UTF-8, не так ли?) С другой стороны, для ISO-8859- * 8-битных кодировок (западноевропейский, кириллический и т. Д.) Отображение строки UTF-8 будет "просто" отображать некоторую тарабарщинуи вам (или вашему пользователю) может быть хорошо с этим, если вы не можете устранить неоднозначность между прочим.символ * в наборе символов локали и UTF-8.

Лично я думаю, что только сообщения об ошибках низкого уровня должны идти в строки what (), и лично я думаю, что они все равно должны быть английскими.(Возможно, в сочетании с некоторым номером ошибки или еще чем-то подобным.)

Худшая проблема, с которой я сталкиваюсь при what(), заключается в том, что в сообщение what () нередко включаются некоторые контекстные детали, например имя файла .Имена файлов не ASCII довольно часто, поэтому у вас не остается иного выбора, кроме как использовать UTF-8 в качестве кодировки what().

Обратите также внимание на то, что ваш класс исключений (который получен из std:: исключение), очевидно, может предоставить любые методы доступа, которые вам нравятся, поэтому имеет смысл добавить явный what_utf8() или what_utf16() или what_iso8859_5().

Редактировать: Относительно комментария Джона о том, какчтобы вернуть UTF-8:

Если у вас есть функция const char* what(), эта функция, по сути, возвращает набор байтов.На западноевропейской платформе Windows эти байты обычно кодируются как Win1252 , но в русских окнах это также может быть Win1251 .

Что возвращают байтыЗначение зависит от их кодировки, а их кодирование зависит от того, откуда они «пришли» (и кто их интерпретирует).Кодировка строкового литерала определяется во время компиляции, но во время выполнения все еще зависит от приложения, как их интерпретировать.

Итак, чтобы ваше исключение возвращало строки UTF-8 с what() (или what_utf8()) вы должны убедиться, что:

  • Входное сообщение для вашего исключения имеет четко определенную кодировку
  • У вас есть четко определенная кодировка для строкового члена, который вы используете для хранения сообщения.
  • Вы соответствующим образом конвертируете кодировку, когда вызывается what()

Пример:

struct MyExc : virtual public std::exception {
  MyExc(const char* msg)
  : exception(msg)
  { }
  std::string what_utf8() {
    return convert_iso8859_1_to_utf8( what() );
  }
};

// In a ISO-8859-1 encoded source file
const char* my_err_msg = "ISO-8859-1 ... äöüß ...";
...
throw MyExc(my_err_msg);
...
catch(MyExc const& e) {
  std::string iso8859_1_msg = e.what();
  std::string utf_msg = e.what_utf8();
...

Преобразование также может быть помещено в (переопределено) то, что() функция-член MyExc () или , вы можете определить исключение, которое будет принимать уже закодированную строку UTF-8 или , которую вы можете преобразовать (из ожидаемой входной кодировки, может быть, wchar_t / UTF-16) в ктор.

4 голосов
/ 21 сентября 2010

Первый вопрос: что вы собираетесь делать со строкой what ()?

Планируете ли вы где-нибудь регистрировать информацию?

Если это так, вам не следует использовать содержимое строки what (), вы должны использовать эту строку в качестве ссылки для поиска правильного локального конкретного сообщения журнала. Так что для меня содержимое what () не для целей ведения журнала (или для любой формы отображения), это метод поиска фактической строки журнала (которая может быть любой строкой Unicode).

Теперь; Может быть достаточно, чтобы строка what () содержала удобочитаемое сообщение для разработчиков, чтобы помочь в быстрой отладке (но для этого хорошо читаемого полированного текста не требуется). В результате нет причин поддерживать что-либо большее, чем ASCII. Соблюдайте принцип KISS.

3 голосов
/ 21 сентября 2010

const char * не обязательно должен указывать на строку ASCII;он может быть в многобайтовой кодировке, такой как UTF-8.Один из вариантов - использовать wcstombs() и друзей для преобразования строк wstring, но вам может потребоваться преобразовать результат what() обратно в строку wstring перед печатью.Это также включает в себя больше копирования и выделения памяти, чем вы можете себе представить в обработчике исключений.

Обычно я просто определяю свой собственный базовый класс исключений, который использует wstring вместо строки в конструкторе и возвращает const wstring & fromwhat().Это не так уж важно.Отсутствие стандартного является довольно большим упущением.

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

2 голосов
/ 30 ноября 2010

Это лучший способ добавить юникод при обработке ошибок:

try
{
   // some code
}
catch (std::exception & ex)
{
    report_problem(ex.what())
}

А:

void report_problem(char const * const)
{
   // here we can convert char to wchar_t or do some more else
   // log it, save to file or message to user
}
2 голосов
/ 21 сентября 2010

Абсолютный минимум каждый разработчик программного обеспечения Абсолютно, положительно должен знать о Unicode и наборах символов (без оправданий!) Джоэл Спольски

Редактировать: Сделано CW, комментаторы могут редактировать, почему эта ссылка актуальна, если они хотят

2 голосов
/ 21 сентября 2010

Стандарт не определяет, какая кодировка является строкой, возвращаемой функцией what (), и нет никакого стандарта по умолчанию.Я просто кодирую его как UTF-8 и возвращаюсь из what () в моих проектах.Конечно, может быть несовместимость с другими библиотеками.

См. Также: https://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful, почему UTF-8 является хорошим выбором.

1 голос
/ 21 сентября 2010

what () обычно не предназначено для отображения сообщения пользователю.Среди прочего, текст, который он возвращает, не локализуем (даже если это был Unicode).Я бы просто использовал what (), чтобы показать что-то ценное для вас как разработчика (например, исходный файл и номер строки в месте, где возникло исключение), и для такого рода текста ASCII обычно более чем достаточно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...