The C++ article in April issue of DDJ
Hello everybody,
It has been a while since I last posted to this newsgroup, but the
recent article in DDJ by Gigi Sayfan entitled "Practical C++ Error
Handling in Hybrid Environments" (available at
http://www.ddj.com/dept/cpp/197003350)
left me no choice but to react.
What caught my attention is the "cunning" StreamingException class and
some of the author's comments about its design that are plainly
inaccurate. Here's the class
class StreamingException : public std::runtime_error
{
public:
StreamingException() :
std::runtime_error(""),
ss_(std::auto_ptr<std::stringstream>
(new std::stringstream()))
{
}
~StreamingException() throw()
{
}
template <typename T>
StreamingException & operator << (const T & t)
{
(*ss_) << t;
return *this;
}
virtual const char * what() const throw()
{
s_ = ss_->str();
return s_.c_str();
}
private:
mutable std::auto_ptr<std::stringstream> ss_;
mutable std::string s_;
};
int main (int argc, char * const argv[])
{
try
{
if (5 != 3)
throw StreamingException() << 5 << " is not equal to " << 3;
}
catch (const StreamingException & e)
{
cout << e.what() << endl;
}
return 0;
}
Here are the comments that are inaccurate:
"The destructor is quite empty, but it can't be dropped. The compiler
will indeed generate a default destructor for you, but the default
destructor doesn't come with an empty throw() exception specification.
This is required because std::runtime_error defines such a virtual
destructor. Exception specifications are an annoying misfeature of C++
that specifies what exceptions a method may throw and are part of the
method signature. Thankfully, they are optional so you don't see them
a lot in the wild. "
This comment is specious as there is no point in overriding the
destructor for the purpose of obtaining an exception specification
that doesn't allow exceptions. This is because:
1. The destructor in std::exception is defined as follows (See Section
18.6.1):
virtual ~exception() throw();
2. std::runtime_error publicly inherits from std::exception and relies
on the compiler to implicitly declare the destructor.
3. The language is clear that an implicitly-declared destructor "shall
allow no exceptions if every function it directly invokes allows no
exceptions" (See 15.4/11-13)
The second inaccurate comment concerns the mutability of the 'ss_'
data mameber. Here it goes:
"Okay, so why mutable? Well, the caught exception is a const reference
because the catching code is not supposed to modify the internal state
of the exception. However, auto_ptr with its ownership transfer
semantics does require a change of state. The mutable modifier was
invented exactly for this purpose-being able to modify the internal
state of an object while preserving its conceptual constness."
According to the rules of the language the 'const StreamingException&'
reference in the catch clause is to be either bound directly to the
exception object or to a const temporary object (See 8.5.3/5). In the
first case the data member's constness is not a concern at all as
nothing is copied. In the second (when the temporary object needs to
be created) it is not a concern either because const semantics are of
no meaning when an object is being constructed. To quote from the C++
standard (See 12.1/4):
const and volatile semantics are not applied on an object under
construction. Such semantics only come into effect once the
constructor for the most derived object ends.
I find it quite sad that a magazine that used to be renowned for the
quality of its articles, didn't put enough effort to catch such
blatant omissions during the article's editing.
Regards,
Andrei Iltchenko.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]