Boost logo

Boost :

From: William E. Kempf (wekempf_at_[hidden])
Date: 2003-01-17 00:29:55


Peter Dimov said:
> From: "William E. Kempf" <wekempf_at_[hidden]>
>> Peter Dimov said:
>> >
>> > The fact that who() returns a function name is not important; it is
>> not a "mini call stack". A function name is used as a token that
>> identifies the point of failure. What failed? An attempt to open a
>> file? An attempt to read the contents of a directory? An attempt to
>> copy a file? To move a file? You need this information in order to
>> compose a meaninful error message. "No such file or directory" by
>> itself isn't very helpful to the user. What is the name of the
>> file/directory that wasn't found? What did the program try to do
>> with it?
>>
>> But the "who" is dependent on higher level information than what's
>> available to the library.
>
> No, who() is not dependent on higher level information. It only depends
> on the location of the throw statement where the exception was created.

Take this statement out of context, and I'd agree. Put it back into
context and the point is that the originating function name is not enough
contextual information to be useful, and the information that would be
useful is at a higher level than what's available to the library.

> Every active exception has one and only one associated throw statement.
>
> To supply who() information, you only need to change
>
> throw E(...);
>
> to
>
> throw E("identifier", ...);
>
> with a suitable predefined "identifier" scheme.
>
> The sequence of events that led to that throw statement being executed,
> the execution path, or the call stack might be nice to have, but they
> aren't necessarily easy to obtain, and they aren't the information that
> who() is intended to provide.

And I argue the information that who() is intended to provide is not useful.

>> Knowing that "no such file or directory" was
>> thrown by "open_file" doesn't help the end user when the action he
>> took was to change a configuration option, for instance.
>
> The end user isn't interested in the function name, which is why who()
> is typically not displayed as-is. who() is used as a key into a string
> table. In the above situation, where who() is "open_file" and error() is
> "boost::filesystem::file_not_found", the user would typically see a
> message like

Who says it's open file? Maybe it came from is_directory() called from
open_file(). Now you've lost the USEFUL context because we're two levels
removed from the throw point.

> Could not open "foo.txt": No such file or directory
>
> Had who() been "boost::filesystem::copy_file", the message could have
> been
>
> Could not copy "foo.txt" to "bar": No such file or directory

And if both open_file() and copy_file() call is_directory() which throws a
file_not_found exception... who provides no benefit at all. You are
pre-supposing a lot with who that at least in some cases won't be true
(and we could have quite a debate about how often it's untrue).

>> And it's no use to
>> the programmer to log such information if it's not also logged that
>> the user was trying to change the configuration at the time, otherwise
>> there's likely several points in the application that could have
>> failed.
>
> That's another subject altogether, the famous logic_error. who() is not
> intended to be used as a function name, and runtime errors are something
> that does not indicate a bug in the program, they happen in the course
> of normal operation.
>
> You can mentally replace
>
> std::string who() const; // function name
>
> with
>
> enum action what() const; // action that failed: open, copy, move, ...
>
> if this will make the above clearer.

I understand what purpose you want who() to meet, I just don't think it
can do so. Nor do I see a great need for it to. IF you plan to recover
from an exception, you'll do it as close to the throw point as you
possibly can, where you (not the library) will have enough contextual
information to provide a meaningful error message to the user, and who()
doesn't make much difference here. If you don't plan to recover, then the
information needed is more for the developer to diagnose the problem than
for the end user's benefit, and in that case who() is certainly not
useful.

>> Good error handling will either catch the exception as close to the
>> point of error as possible, where there's enough known context to give
>> a meaningful report,
>
> Catching exceptions close to the throw point (an exception is not an
> error) is typically an indication of bad design. (As is proliferation of
> try blocks.) If the client often needs to handle failures locally, the
> functions should have been made to report them using a return value.

Ahh... that debate wages on. I've got two comments about it. First, not
everyone agrees with this rigid definition, as is evidenced by the
numerous langauges in which an exception IS used for general errors and
even by the names given to many of the C++ standard exception types.
Second, even with a strict usage policy of "only for
unexpected/exceptional conditions", if you plan to recover you need to do
it as close to the throw point as possible, and if you don't plan to
recover the end user hardly needs to be informed of something like "Could
not open file 'foo.txt'." Generally his response would be "So, why did
the application crash because of that?"

> (This is a very general advice, of course.)
>
> If, on the other hand, you are talking about logic_error exceptions,
> that indicate bugs in the program, good error handling will gather the
> necessary information at the point where the bug is detected, before the
> throw (if an exception is thrown at all.)

I do believe I made that point.

>> or will log much more information then is available to
>> the point of exception generation (at least in a portable manner). I
>> can't see how who() is helpful.
>>
>> > Once you accept that the exception needs to provide an unique token
>> that identifies the point of failure, now the question is how to
>> choose its type and value.
>>
>> But I don't accept that, unless the FULL point of failure can be
>> identified (i.e. a call stack).
>
> How would you use a call stack to generate an user friendly error
> message?

I wouldn't. User friendly error messages would only be generated close to
the throw point, where *I* have enough contextual information to generate
an error message with out a who(). The call stack info would be used to
generate a *developer* friendly message (sent to a log, not presented to
the end user).

William E. Kempf
wekempf_at_[hidden]


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk