Почему С++ STL iostreams не "исключение"?
Я привык к Delphi VCL Framework, где TStreams генерируют исключения из ошибок (например, файл не найден, полный диск). Я портирую некоторый код, чтобы использовать С++ STL вместо этого, и были пойманы iostreams, не выбрасывая исключения по умолчанию, но устанавливая флаги badbit/failbit вместо этого.
Два вопроса...
a: Почему это? Кажется, странное дизайнерское решение для языка, построенного с исключениями в нем с первого дня?
b: Как лучше избегать этого? Я мог бы создавать классы прокладок, которые бросаются так, как я ожидал, но это похоже на переосмысление колеса. Может быть, есть библиотека BOOST, которая делает это более здраво?
Ответы
Ответ 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" (или блока фиксированного размера за раз). Мне легче и надежнее использовать другие методы для проверки и интерпретации этого ввода.