Проблема
Как сейчас, поддержка исключений для потоков ужасна. Когда библиотека Boost.System была принята в С++ 11, у одного создалось впечатление, что, возможно, исключения будут улучшаться. Все изменения были заменены std::exception
на std::system_error
. Хотя <system_error>
является хорошей библиотекой для разработчиков, стандартный комитет и разработчики стандартных библиотек не предприняли никаких шагов по его использованию для улучшения сообщений об исключениях.
Чтобы дать представление о том, как это ужасно, здесь краткое изложение того, что происходит:
-
Произошла ошибка.
-
setstate
используется для установкиbadbit
илиfailbit
. -
clear
вызываетсяsetstate
. -
Если исключения включены,
clear
выдастios_base::failure
.
Да, это означает, что для ВСЕХ ошибок генерируется одно и то же ненужное сообщение об исключении. Это указано на уровне basic_ios
, поэтому все производные классы страдают от этой проблемы. Оскорбительная цитата:
[iostate.flags]/4 Эффекты: Если
((state | (rdbuf() ? goodbit : badbit)) & exceptions()) == 0
, возвращается. В противном случае функция бросает объектfail
классаbasic_ios::failure
(27.5.3.1.1), построенные с определенными параметрами аргументов.
Здесь приведен пример того, что "значения аргументов, определяемых реализацией":
ios_base::clear: unspecified iostream_category error
Есть ли легкое исправление?
Ни Boost.Filesystem
, ни Boost.Iostreams
не являются заменой для <iostream>
. Первая представляет собой библиотеку для портативного управления файловой системой (и, вероятно, появится в следующей версии С++), в то время как последняя имеет какое-то отношение к... Источники и стоки. В документации указано, что она делегирует исключения в ios_base::failure
в любом случае. Boost.Filesystem
предоставляет <boost/filesystem/fstream.hpp>
, который использует path
вместо аргументов const char*
для open()
. В нем показан пример того, как можно наследовать стандартные классы библиотек:
template < class charT, class traits = std::char_traits<charT> >
class basic_ifstream : public std::basic_ifstream<charT,traits>
{
private: // disallow copying
basic_ifstream(const basic_ifstream&);
const basic_ifstream& operator=(const basic_ifstream&);
public:
basic_ifstream() {}
// use two signatures, rather than one signature with default second
// argument, to workaround VC++ 7.1 bug (ID VSWhidbey 38416)
explicit basic_ifstream(const path& p)
: std::basic_ifstream<charT,traits>(p.BOOST_FILESYSTEM_C_STR, std::ios_base::in) {}
basic_ifstream(const path& p, std::ios_base::openmode mode)
: std::basic_ifstream<charT,traits>(p.BOOST_FILESYSTEM_C_STR, mode) {}
void open(const path& p)
{ std::basic_ifstream<charT,traits>::open(p.BOOST_FILESYSTEM_C_STR, std::ios_base::in); }
void open(const path& p, std::ios_base::openmode mode)
{ std::basic_ifstream<charT,traits>::open(p.BOOST_FILESYSTEM_C_STR, mode); }
virtual ~basic_ifstream() {}
};
Это аккуратный трюк, за исключением того, что наша нарушающая функция не виртуальна и все в basic_ios
, есть комбинаторный взрыв того, что мы должны переопределить:
Я подозреваю, что требуется полная переписывание, потому что просто заменить clear()
будет недостаточно. Поток может завершиться неудачей по нескольким причинам, но есть только один тип исключения. Хотя std::system_error
дает нам лучшие инструменты выражения ошибок, это не помогает, если, опять же, нет способа отличить источник ошибки.
Однако я не писатель библиотеки и не хочу заниматься этой задачей. Есть ли другие варианты, кроме перечисленных мной?