From: Brey, Edward D (EdwardDBrey_at_[hidden])
Date: 2002-03-21 10:58:55
> From: Stewart, Robert [mailto:stewart_at_[hidden]]
> > call. That way the
> > algorithm is simply Call A, Call B, Delete File. No
> > intermediate checks are
> > needed, but there is still good diagnostics.
> Approach 2: remove() throws an exception if the file didn't exist
> create script
> run program B
> catch (???)
> if (exception means that script was missing)
> report missing script
> diagnose problems creating script or running B
There's yet another approach, more like what I use in practice for scripting
on internal projects:
run program B
dump rich exception information
This simple catch-all approach has proven more than adequate for me in
writing Python scripts, and it comes with the important benefit that I don't
need to write all sorts of error handling or detection in the scripts. It
is one of the reasons that Python is such a productive tool for scripting.
However, I see one big obstacle that could prevent C++ from easily being
equally useful (from an error handling point of view anyway): C++ doesn't
provide a call stack dump. One reason that a catch-all is so useful in
Python is that the stack dump shows you the context of the exception info.
Without it, I suppose that more selective handling might be required to
provide good diagnostics, depending on the complexity of the program.
Alternatively, the program would need to record checkpoints to provide the
> > live without
> > this, just like one can live with a remove that always throws
> > on any error
> > and lot of try/catch blocks.
> Writing a "lot of try/catch blocks" is not a sign of good
> design. Throwing
> exceptions is expensive. If a condition is not uncommon and
> is benign, then
> throwing an exception is unwarranted.
Agreed. My point is indicate that just as avoidable try-catch blocks are
problematic, so are avoidable return-value checks. Each is inefficient
(although exceptions more so) and requires more code.
> > Presume that you are making a utility to wipe files. You
> > overwrite the file
> > many times and then delete it. If for whatever reason the
> > delete fails, the
> > user definitely should be notified of it.
> If you cannot delete the file, that's a problem. If the file
> doesn't exist,
> there's no problem. Before you ever got to the remove()
> call, you would
> have overwritten the file "many times." The code that
> overwrites the file
> will have error handling code to notice that the file
> disappeared while
> trying to overwrite it. Provided that code successfully
> overwrote the file
> the requisite number of times, it doesn't matter if the file
> is gone by the
> time remove() is called. The user doesn't need to know about that
This is true in a perfect world. However, clearly, if the file disappeared
unexpectedly, something has gone wrong. If the point of the operation was
to destroy sensitive data, it is reasonable to expect that someone would
want to know that there is something fishy going on.
> > I'd take this a step further, and say that any time the user
> > asks to specify
> > delete a file, he should be notified if the delete fails.
> > That gives him a
> > heads up that his view of the file system is out of date
> > and/or there is
> > some other entity (possibly previously unknown to him)
> > working with the same
> > files as he.
> If the user types a command that triggers a call to remove(),
> then yes, the
> program will want to notify the user that the file was
> already gone. If the
> user selects a file in a GUI display of the filesystem and
> asks to delete
> it, and by the time the GUI reacts to the UI events and makes
> the call to
> remove() the file is gone, then no, I don't think you'd want
> to notify the
> user. Since the file was visible in the GUI, then the UI and the user
> thought it existed. When the "delete" operation is complete,
> if the file is
> no longer in the filesystem, then the operation was
> successful, regardless
> of whether the call to remove() actually removed the file or not.
It's interesting that the implementation of the shell of the world's best
selling operating system doesn't happen to follow this philosophy. So I'm
not alone in disagreeing with you.
> I still see no compelling example requiring that remove()
> throw an exception
> if the file does not exist. The few examples that need to
> know that the
> file didn't exist when remove() tried to remove it can simply
> check the
> return value. If it isn't important to the application to
> know about that
> condition, then the application can ignore remove()'s return
> value safely.
But not quite a simple as the linear approach, which is that a function does
what it says it does or it throws an exception. One key motivation is to
avoid a special case that is inconsistent with other IO functions. For the
other functions, it isn't just the end (postcondition) that matters, it is
also the means. If you move a file from src to dest, and the happens to
already be at the dest and no the src, does move silently succeed? No. But
if you use remove() to logically do the same thing (only consider the dest
as /dev/null), should we make a special case? I'd rather not.
Of course, just because there are applications where a throwing remove() is
desirable and just because we want to avoid special cases, doesn't mean that
we should ignore the opportunity to simplify code that does want to ignore a
failed remove. While it is easy to overestimate the fraction of cases where
ignoring the problem is truly the best answer, I suspect that there are
easily enough to justify making these situations easy to code. I see a
couple of opportunities. One is to notice how opening a file has to deal
with a similar issue: fail or truncate on existing file? The other is to
provide a error handling context. I think that we can easily come to a
solution that makes the library easy to use for just about any kind of
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk