Подтвердить что ты не робот

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

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

Определение исключения std:

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:: exception), плюс вы, вероятно, предоставление им большей информации (например, тот факт, что ваш конкретный исключение может быть уточнением std:: runtime_error или что-то еще).std:: runtime_error или что-то еще).

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

Обратите также внимание, что ваш класс исключений (полученный из std:: exception) может, очевидно, предоставить любые методы доступа, которые вам нравятся, и поэтому имеет смысл добавить явные 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();
...

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

Изменить: Сделано CW, комментаторы могут редактировать, почему эта ссылка имеет значение, если они пожелают

Ответ 7

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

Ответ 8

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

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
}