List of Phobos functions that allocate memory?
Jonathan M Davis
jmdavisProg at gmx.com
Fri Feb 7 12:38:59 PST 2014
On Friday, February 07, 2014 14:26:47 Marc Schütz"
<schuetzm at gmx.net>@puremagic.com wrote:
> On Thursday, 6 February 2014 at 22:15:11 UTC, Brad Anderson wrote:
> > Personally I don't think bad user input qualifies as an
> > exceptional case because it's expected to happen and the
> > program is expected to handle it (and let the user know) when
> > it does. That's just a matter of taste though.
>
> Hmm... then what _does_ qualify as exceptional in your opinion?
Honestly, I think that the typical approach of discussing exceptions as being
for "exceptional" circumstances is bad. It inevitably leads to confusion and
debate over what "exceptional" means. Some programmers would consider that to
mean any bad input, whereas others would take it to the extreme that they
should only happen when your program is in an invalid state (essentially what
we use Errors for). I've found rather that when discussing exceptions it works
much better to explain exactly why you'd use them, and I think that that comes
primarily down to three types of circumstances.
1. Code which which should succeed most of the time and which would be far
cleaner if it's written to throw exceptions - particularly when the
alternative would be to check error codes on every function call (which would
be incredibly error-prone). A prime example of this would be a parser. It's
far cleaner to write a parser which assumes that each step succeeds than it is
to constantly check that each one succeeded. It makes it so that only code
that could actually encounter an error has to check for it and so that it can
easily and cleanly propagate the error to the top. Doing that with error codes
would generally be a mess, and unless failure is the norm, efficiency
shouldn't be a problem.
2. Code which you can't actually guarantee will ever succeed. There are some
cases where you can avoid errors by doing validation before proceeding (e.g.
testing strings for Unicode correctness before doing a lot of string
processing), but there are others where you either can't validate ahead of
time or where you could still end up with an error in spite of your
validation. A prime example of this would be operating on files. For,
instance, std.file.isDir will tell you whether a particular file is directory
or not by returning bool. If that file does not actually exist, then what is
isDir supposed to do? All it can do is throw an exception, unless you want to
have a separate out parameter to report whether it succeeded or not or change
it so that it returns an error code and returns the bool as out parameter,
both of which would make it much uglier to use. And isDir can't assert that
the file exists, because that's a runtime condition that cannot be fully
verified ahead of time. You can (and should) check whether the file exists
first
if(file.exists)
{
if(file.isDir)
{}
else if(file.isFile)
{}
else
{}
}
but the file system could actually delete that file right out from under you
between the call to exists and the call to isDir (or between the calls to
isDir and isFile), so validation reduces how often you hit the error case but
cannot eliminate it. It should also be rare that isDir will fail (since you
should be checking that the file exists first). So, throwing an exception
makes perfect sense. You get clean code that's still able to handle error
cases rather than them being ignored (as frequently happens with error codes).
3. Code which should succeed most of the time but where doing validation
essentially requires doing what you're validating for anyway. Again, parsers
are a good example of this. For instance, to validate that
"2013-12-22T01:22:27z" is in the valid ISO extended string format for a
timestamp, you have to do pretty much exactly the same work that you have to
do to parse out all of the values to convert it to something other than a
string (e.g. SysTime). So, if you validated it first, you'd be doing the work
twice. As such, why validate first? Just have it throw an exception when the
parsing fails. And if for some reason, you expect that there's a high chance
that the parsing would fail, then you can have a function which returns an
error code and passed out the result as an out parameter instead, but that
makes the code much uglier and error-prone. So, in most cases, you'd want it
to throw an exception on failure. But regardless, you wouldn't want to
validate it first as that would just be expensive all the time rather than
more expensive in the (hopefully) rare error case.
The areas that you want to normally avoid exceptions are when you're
validating up front or when the error condition is likely. If you're
validating, you're normally asking a question - is this data valid - in which
case, returning bool is the correct thing to do, not throwing on failure
(though if the result is false, the caller could choose to throw if
appropriate). And trying to do something which has a good chance of failing
should probably return whether it succeeded or not, because you don't want
exceptions to be your normal code path.
Also, performance-critical stuff may need to go the error-code path rather
than exceptions simply due to it being performance-critical, but in general,
error conditions which aren't bugs in your program should be reported via
exceptions (not error codes) with validation being used where appropriate to
make it so that the error conditions are infrequent.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list