The problem
As of right now, the exception support for streams are terrible. When the Boost.System library was adopted into C++11, one was given the impression that maybe exceptions would improve. All the change did was replace std::exception
with std::system_error
. While <system_error>
is a good library on its own for developers, the standard committee and standard library implementors have not taken any steps towards using it to improve exception messages.
To give an idea of how terrible it is, here's a brief summary of what goes down:
-
An error occurs.
-
setstate
is used to setbadbit
orfailbit
. -
clear
is called bysetstate
. -
If exceptions are enabled,
clear
will throw anios_base::failure
.
Yes, that means for ALL errors the same useless exception message is thrown. This is specified at the basic_ios
level, so all derived classes suffer from this issue. The offending quote:
[iostate.flags]/4 Effects: If
((state | (rdbuf() ? goodbit : badbit)) & exceptions()) == 0
, returns. Otherwise, the function throws an objectfail
of classbasic_ios::failure
(27.5.3.1.1), constructed with implementation-defined argument values.
Here's an example of what "implementation-defined argument values" gives us:
ios_base::clear: unspecified iostream_category error
Is there an easy fix?
Neither Boost.Filesystem
nor Boost.Iostreams
are replacements for <iostream>
. The former is a library for portably dealing with a filesystem (and is likely to appear in the next revision of C++) while the latter has something to do with..Sources and Sinks. The documentation states that it delegates exceptions to ios_base::failure
anyways. Boost.Filesystem
does provide <boost/filesystem/fstream.hpp>
which uses path
instead of const char*
arguments to open()
. It shows an example of how one might inherit from standard library classes:
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() {}
};
This is a neat trick, except since our offending function is non-virtual and all the way up in basic_ios
, there's a combinatorial explosion of what we have to reimplement:
I can't post images, so remove the space.
I suspect an entire rewrite is needed because simply replacing clear()
won't be enough. The stream can fail for multiple reasons but there's only one type of exception thrown. While std::system_error
gives us better tools of expressing errors, that doesn't help if, again, there's no way to distinguish the source of the error.
However I'm not a library writer and don't feel like taking on this task. Are there any other options than the ones I listed?
Aucun commentaire:
Enregistrer un commentaire