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

Почему С++ STL iostreams не "исключение"?

Я привык к Delphi VCL Framework, где TStreams генерируют исключения из ошибок (например, файл не найден, полный диск). Я портирую некоторый код, чтобы использовать С++ STL вместо этого, и были пойманы iostreams, не выбрасывая исключения по умолчанию, но устанавливая флаги badbit/failbit вместо этого.

Два вопроса...

a: Почему это? Кажется, странное дизайнерское решение для языка, построенного с исключениями в нем с первого дня?

b: Как лучше избегать этого? Я мог бы создавать классы прокладок, которые бросаются так, как я ожидал, но это похоже на переосмысление колеса. Может быть, есть библиотека BOOST, которая делает это более здраво?

4b9b3361

Ответ 1

а. C++ не был построен с исключениями с первого дня. "C с классами" был запущен в 1979 году, а исключения были добавлены в 1989 году. Между тем библиотека streams была написана еще в 1984 году (позже она стала iostreams в 1989 году (позже переиздана GNU в 1991 году)), она просто не может использовать обработку исключений в начало.

Ref:

б. Вы можете включить исключения с .exceptions метода .exceptions.

// ios::exceptions
#include <iostream>
#include <fstream>
#include <string>

int main () {
  std::ifstream file;
  file.exceptions ( ifstream::failbit | ifstream::badbit );
  try {
    file.open ("test.txt");
    std::string buf;
    while (std::getline(file, buf))
      std::cout << "Read> " << buf << "\n";
  }
  catch (ifstream::failure e) {
    std::cout << "Exception opening/reading file\n";
  }
  std::cout.flush();

  file.close();

  return 0;
}

Ответ 2

Как говорит Кенни, вы можете включить исключения, если хотите. Но обычно для ввода/вывода требуется какой-то стиль возобновления программирования при возникновении ошибки, что нелегко поддерживать с помощью исключений - тестирование состояния потока после операции ввода намного проще. Я никогда не видел кода на С++, который использует исключения для ввода-вывода.

Ответ 3

ОК, это "Ответ на мой вопрос"...

Во-первых, благодаря KennyTM для истории. По его словам, С++ был НЕ, разработанный с исключениями с первого дня, поэтому неудивительно, что обработка исключений iostreams была включена позже.

Во-вторых, как указывает Нейл Б, наличие исключений при ошибках преобразования входного формата было бы значительной болью. Это меня удивило, потому что я рассматривал iostreams как простой слой оболочки файловой системы, и я вообще не рассматривал этот случай.

В-третьих, похоже, BOOST приносит что-то в сторону: Boost.IOStreams. Если я правильно понимаю, они обрабатывают низкоуровневый аспект ввода-вывода и буферизации потоков, оставляя обычную библиотеку IOStreams С++ для обработки проблем с преобразованием. Boost.IOStreams использует исключения так, как я ожидал. Если я правильно понимаю, пример Кенни также может выглядеть так:

#include <ostream>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/stream.hpp>

int main () {
  boost::iostreams::stream_buffer <boost::iostreams::file_source> buf("test.txt");
  std::istream file(&buf);

  try {
    std::string buf;
    while (std::getline(file, buf))
      std::cout << "Read> " << buf << "\n";
  }
  catch (std::ios_base::failure::failure e) {
    std::cout << "Exception opening/reading file\n";
  }
  std::cout.flush();

  file.close();

  return 0;
}

Я думаю, что с этой версией такие вещи, как "файл не найден", должны бросаться, но ошибки "istream" будут сообщаться с помощью badbit/failbit.

Ответ 4

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

  • Iostreams также поддерживает исключения. Но исключение является необязательным. Вы можете включить исключение, установив exceptions (failbit | badbit | eofbit)

  • Iostreams позволяет вам учитывать как исключение, так и поведение бездействия.

Ответ 5

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

При нормальной обработке ввода-вывода такие вещи, как eof, не являются действительно исключительными - они ожидаются и, как правило, более тщательно обрабатываются с использованием методов, отличных от исключений. Аналогичным образом, при поиске контейнера отказ найти конкретный ключ необязательно является неожиданным или исключительным, поэтому он подходит, например, для того, std:: find, чтобы вернуть значение конечного предела для "отказа".

С другой стороны, в то время как многие не согласятся, я считаю, что целесообразно использовать исключение для успеха в рекурсивном поиске. За вызов неудача - это нормальный случай - и использование исключения для успеха может значительно облегчить структуру рекурсивной функции. Хотя я не уверен, что буду использовать его в реальном мире, бросая исключение для уменьшения и возвращающегося только для поиска без соответствия, дает довольно чистый и простой в использовании парсер LR с обратной записью.

BTW - Мне не нравится полагаться на iostream для ввода в любом случае, за исключением простого массива "give-me-the-whole-file-content" (или блока фиксированного размера за раз). Мне легче и надежнее использовать другие методы для проверки и интерпретации этого ввода.