|
Boost : |
From: Darin Adler (darin_at_[hidden])
Date: 2001-10-26 13:16:26
In previous threads on boost, we've clarified the distinction between the
roles for exceptions and asserts. Let me summarize:
A) Some functions have defined behavior when out-of-domain arguments are
passed in. Typically this behavior is throwing an exception. One case where
this is required in cases where the caller can't be expected to know whether
an argument is in or out of the domain.
An example of this would be a function that converts a string to an
integer. The caller can't be expected to check whether the string has the
proper format, because that's nearly equivalent to converting the string to
the integer in the first place.
B) Other functions have undefined behavior when out-of-domain arguments
are passed in. This allows for more-efficient implementation, since the
function can assume that the argument is good. For debugging purposes, such
functions can still check the validity of the arguments. This is typically
done with an assert, but some programmers prefer to use exceptions. But even
if an exception is thrown, this is not part of the defined behavior of the
function. People often emphasize that the behavior of such a function when
passed a bad argument is a quality of implementation issue.
An example of this is the [] operator for class vector. Implementations
are free to do range checking on the argument passed in, but the behavior is
not defined. Many in the boost community have suggested that the
implementation this function should include an assert.
A minor problem in boost is that we don't make a clear distinction between
which functions are functions of type A and which are functions of type B. A
pedantic distinction can be made as well, because the choice of strategy A
vs. strategy B can be different for different parameters in the same
function, or even for different kinds of "bad" arguments.
================
One reason this is a bit confusing is that the C++ standard, by definition,
doesn't address the "assert vs. exception" choice for functions of type B.
This could lead programmers to think that exceptions are always better than
asserts, since the standard mentions exceptions (when defining functions of
type A), but does not include any example of asserts as part of defined
behavior.
Another issue is that some programmers want to turn all asserts into
exceptions. While this may sound like a good idea, it has many of the same
drawbacks of the Windows practice of turning machine exceptions into C++
exceptions. When it comes to undefined behavior, we need to make programs
that don't rely on it at all. Asserts are tools that we can use to correct
our programs, but if they are turned into exceptions we end up writing code
to "handle" the undefined state. This issue leads some programmers to
suggest eschewing asserts altogether and abandon the "debug vs. release"
build model where the asserts are removed from production programs.
================
In light of this, lets consider the cast.hpp header for a moment.
polymorphic_cast trades one kind of defined behavior (returning 0) for
another kind of defined behavior (throwing an exception). But it seems to me
that the whole point of the function is that it's used when you know the
case will succeed. That seems to put in in category B from above. I'd like
to see this function use an assert rather than an exception.
polymorphic_downcast does an assert for the same reason I'd suggest
using an assert in polymorphic_cast. It's strange to have one member of the
family using an assert and another using an exception.
numeric_cast is a function of category A and always does range checking
and throws bad_numeric_cast if the result of conversion is out of range. But
I'd like to see a numeric_cast of category B, that leaves out the range
checking in NDEBUG builds, for use when the number is known to be in range
and a programmer wants an assert to check this assumption.
-- Darin
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk