Do you have experience with exceptions?

D

DeMarcus

Hi,

If you want to design your own exceptions there are several guidelines
out there. One of them is this from boost.

http://www.boost.org/community/error_handling.html

If you look at their recommendations I would like to skip all items and
just focus on item 6. In short it says; in the exception, don't provide
messages, provide objects involved with the problem.

I claim that this is (almost) completely wrong, and my arguments for
this are the following.

*) A function may throw several different exceptions of the same type,
making it impossible for the receiver to understand what the attached
objects mean. E.g. OutOfBoundsException with an attached integer 42 may
mean that the loadModuleWithID() tried to load an ID that doesn't exist,
or it may mean that a subroutine to loadModuleWithID() tried to read out
of bounds in an XML file.


*) If a short message is created and attached to the exception, an
exception message stack unwinding may be more descriptive than what the
handler can put together on its own. E.g. it would probably be more
descriptive to see the following.

OutOfBoundsException - Tried to read XML section 42. Only 40 exist.
FileReadException - Could not read XML file config.xml.
NoSuchEntryException - Could not load module with ID 4711.

instead of

NoSuchEntryException - Out of bounds: 42. File: config.xml. ID: 4711.


*) Lower coupling promotes program correctness. If the receiver of an
exception has to understand the meaning of the attached objects, that in
turn means that we couple the function and caller. If the creation of
the exception changes slightly in the function, all callers have to be
updated!


I claim that a stack of independent exception messages is more
descriptive than trying to forward objects that an exception handler
shall interpret.

Do you have concrete examples from your experience with exceptions? I
would like to hear your opinion, especially if you don't agree with me.


Thanks,
Daniel
 
A

Alf P. Steinbach

* DeMarcus:
If you want to design your own exceptions there are several guidelines
out there. One of them is this from boost.

http://www.boost.org/community/error_handling.html

If you look at their recommendations I would like to skip all items and
just focus on item 6. In short it says; in the exception, don't provide
messages, provide objects involved with the problem.

I claim that this is (almost) completely wrong, and my arguments for
this are the following.

*) A function may throw several different exceptions of the same type,
making it impossible for the receiver to understand what the attached
objects mean. E.g. OutOfBoundsException with an attached integer 42 may
mean that the loadModuleWithID() tried to load an ID that doesn't exist,
or it may mean that a subroutine to loadModuleWithID() tried to read out
of bounds in an XML file.


*) If a short message is created and attached to the exception, an
exception message stack unwinding may be more descriptive than what the
handler can put together on its own. E.g. it would probably be more
descriptive to see the following.

OutOfBoundsException - Tried to read XML section 42. Only 40 exist.
FileReadException - Could not read XML file config.xml.
NoSuchEntryException - Could not load module with ID 4711.

instead of

NoSuchEntryException - Out of bounds: 42. File: config.xml. ID: 4711.


*) Lower coupling promotes program correctness. If the receiver of an
exception has to understand the meaning of the attached objects, that in
turn means that we couple the function and caller. If the creation of
the exception changes slightly in the function, all callers have to be
updated!

Yeah, agreed. Good points. Especially last one.

I claim that a stack of independent exception messages is more
descriptive than trying to forward objects that an exception handler
shall interpret.

Do you have concrete examples from your experience with exceptions? I
would like to hear your opinion, especially if you don't agree with me.

Constructing an exception object shouldn't throw, or should have very low chance
of throwing. This means that designing your own /safe/ exception class is hard:
you first have to design and implement a reasonable array smart pointer, then a
reasonable non-mutable string class, then exceptions -- and there's a catch 22
for how to deal with exceptions for the string class...

Exceptions need not support chaining but they do need to support cloning and
rethrowing via some virtual method, in order to propagate exceptions safely
through C code.

The focus on providing a lot of information via exception objects is as I see it
wrong-headed for C++ (although it can be meaningful in a scripting context). In
C++ bugs should IMO preferentially trigger asserts, not raise exceptions, and
for a failure-indicating exception the type should be enough (and often is much
more than enough) for the handling code. A decent C++ debugger lets you break on
first throw, and that's where you're headed anyway when it's a bug. All that
information-gathering and forwarding for a "rich" exception is mostly additional
things that can go wrong, and it adds complexity & inefficiency.


Cheers, & hth.,

- Alf
 
D

DeMarcus

* DeMarcus:

Yeah, agreed. Good points. Especially last one.



Constructing an exception object shouldn't throw, or should have very
low chance of throwing. This means that designing your own /safe/
exception class is hard: you first have to design and implement a
reasonable array smart pointer, then a reasonable non-mutable string
class, then exceptions -- and there's a catch 22 for how to deal with
exceptions for the string class...

Exceptions need not support chaining but they do need to support cloning
and rethrowing via some virtual method, in order to propagate exceptions
safely through C code.

The focus on providing a lot of information via exception objects is as
I see it wrong-headed for C++ (although it can be meaningful in a
scripting context). In C++ bugs should IMO preferentially trigger
asserts, not raise exceptions, and for a failure-indicating exception
the type should be enough (and often is much more than enough) for the
handling code. A decent C++ debugger lets you break on first throw, and
that's where you're headed anyway when it's a bug. All that
information-gathering and forwarding for a "rich" exception is mostly
additional things that can go wrong, and it adds complexity & inefficiency.

I agree with you that too much complexity is put in the exceptions. The
problem is the reality. If you ship a product and the customer on the
other side of the globe calls you in the middle of the night screaming;
"It crashed!". Then it's nice to start with the log file instead of
jumping out of the bed, book a flight ticket to go there have a look at
the core dump.

If the log file says; "OutOfBoundsException - Tried to read XML section
42. Only 40 exist.", there may be reason to start up the computer. If
the log file instead says; "FileReadException - Could not read XML file
config.xml. File access denied.", then you could tell the administrator
to grant the application access to the directory, and then you can go
back to sleep.

My point is that a core dump and a debugger are invaluable tools, but
when dealing with customers you want to filter out as many simple bugs
as possible before starting up your debugger.
 
R

Richard

[Please do not mail me a copy of your followup]

In addition to the comments of the original poster and Alf, I'll just
add that I don't end up using exceptions for finding programming
errors. Since I've switched to test-driven development, I find that
my programming errors are all found much, much earlier by tests. I
use exceptions for things like "whoops, the network died while we were
in the middle of talking", not things like array indices out of
bounds.

Having said that, I do use mechanisms that translate error codes into
exceptions when I'm not expecting the underlying code to ever fail.
Occasionally these result in exceptions due to programming errors. In
areas where I code in C++, COM objects and C style APIs are
commonplace.

I map failed COM HRESULTs to exceptions except in the rare occasions
where you are doing something like querying the size of a buffer and the
function normally returns E_MORE_DATA or whatever to indicate that its
telling you the size required. Similarly, when I call something like
::GetDC, I expect it to return me a real HDC. When it doesn't, I throw.
This is a programming error, there aren't any real world situations
where GetDC will return zero unless I made a mistake elsewhere.

This practice has saved me valuable time in the debugger and I mostly
adopted that "map C/COM style error codes to exceptions" habit several
years before I adopted the test-driven development habit. Test-driven
development has saved me even *more* time in the debugger, but the
exception technique helped a lot.
 
Ö

Öö Tiib

The focus on providing a lot of information via exception objects is as I see it
wrong-headed for C++ (although it can be meaningful in a scripting context). In
C++ bugs should IMO preferentially trigger asserts, not raise exceptions, and
for a failure-indicating exception the type should be enough (and often is much
more than enough) for the handling code. A decent C++ debugger lets you break on
first throw, and that's where you're headed anyway when it's a bug. All that
information-gathering and forwarding for a "rich" exception is mostly additional
things that can go wrong, and it adds complexity & inefficiency.

Some product feels like scripting context despite it is fully C++.
Consider:
Group of tough guys are maintaining a framework. Hundreds are sending
plugins for integration from all over the globe. Thousands are using
integrated product. Work is constant, patch, upgrade or new release
per month.

Asserting in such framework is rather pointless. Framework should be
truly unbreakable. It should very clearly blame those plugins from all
channels (trace, log, user interface) but still keep being alive. On
worst case it should kick out bad (lets say hanging) plugin and
restart itself. Otherwise if the product is doing anything mission
critical the above mentioned tough guys can never sleep.
 
I

Ian Collins

In my reading, it says almost the opposite: *in addition* to the message,
provide the relevant data separately for the potential exception handlers
needing that data. This does not mean that one could not include this data
also in the error message returned by what().

I agree. It's pretty clear from (emphasis mine): "If you *only* expose
a textual representation of those numbers in the what() string" that the
guideline is not recommending against messages.
Of course handling of this extra data is more difficult, but this does
concern only exception handlers interested in that data. Other handlers can
just dump the what() string somewhere and be done with it.

I agree again. My default system call exception base class exposes the
name, line, file and errno value of failed system calls as well as
providing a formatted message via what(). So I can either catch and
attempt to recover, or simply call what() and exit.

Another important point made there is to defer the formatting to the
what() member. In order to do that, the relevant data has to be
included in the exception object.
 
D

DeMarcus

[Please do not mail me a copy of your followup]

In addition to the comments of the original poster and Alf, I'll just
add that I don't end up using exceptions for finding programming
errors. Since I've switched to test-driven development, I find that
my programming errors are all found much, much earlier by tests. I
use exceptions for things like "whoops, the network died while we were
in the middle of talking", not things like array indices out of
bounds.

I agree, out of bounds is maybe not the best example, but still, there
could be a bad configuration file composed by the user that goes out of
bounds somewhere. In the end the correct message would be something
like; "Your config file is ill-formed", but it would be nice to see the
stack trace of that.
Having said that, I do use mechanisms that translate error codes into
exceptions when I'm not expecting the underlying code to ever fail.
Occasionally these result in exceptions due to programming errors. In
areas where I code in C++, COM objects and C style APIs are
commonplace.

I map failed COM HRESULTs to exceptions except in the rare occasions
where you are doing something like querying the size of a buffer and the
function normally returns E_MORE_DATA or whatever to indicate that its
telling you the size required. Similarly, when I call something like
::GetDC, I expect it to return me a real HDC. When it doesn't, I throw.
This is a programming error, there aren't any real world situations
where GetDC will return zero unless I made a mistake elsewhere.

This practice has saved me valuable time in the debugger and I mostly
adopted that "map C/COM style error codes to exceptions" habit several
years before I adopted the test-driven development habit. Test-driven
development has saved me even *more* time in the debugger, but the
exception technique helped a lot.

Do you have any favorite sites about test-driven development? I want to
dig even deeper down into that.
 
D

DeMarcus

In my reading, it says almost the opposite: *in addition* to the message,
provide the relevant data separately for the potential exception handlers
needing that data. This does not mean that one could not include this data
also in the error message returned by what().

Ok, you're right there.
Of course handling of this extra data is more difficult, but this does
concern only exception handlers interested in that data. Other handlers can
just dump the what() string somewhere and be done with it.

Now, I do respect the whole boost community, including the boost
exceptions, however, I still don't know whether providing arbitrary data
with a general exception (or any object for that matter) is good design.

As a receiver you should know for sure what data you're getting.
Therefore I would rather see a subclass to the boost exception providing
that extra data in the interface.
To me it seems you are trying to break through an open door.

Don't get me wrong. This discussion is not about whether boost exception
is good or not, I just took boost as an example. The discussion is about
whether it is good design to provide arbitrary data with an object; in
this case an exception.
 
I

Ian Collins

Now, I do respect the whole boost community, including the boost
exceptions, however, I still don't know whether providing arbitrary data
with a general exception (or any object for that matter) is good design.

I don't see anyone advocating providing arbitrary data with a general
exception.
As a receiver you should know for sure what data you're getting.
Therefore I would rather see a subclass to the boost exception providing
that extra data in the interface.


Don't get me wrong. This discussion is not about whether boost exception
is good or not, I just took boost as an example. The discussion is about
whether it is good design to provide arbitrary data with an object; in
this case an exception.

See my other response.

I don't provide arbitrary data, I include enough for the catcher to
either bail, print something meaningful or attempt recovery.

One of the design considerations raised is whether to process the
available information in the exception constructor and store this result
in the "what" string, or store the information in the exception and
perform the processing in the what() member. I prefer the latter.
There's enough information in the exception object to make a choice when
it is caught.
 
D

DeMarcus

I agree. It's pretty clear from (emphasis mine): "If you *only* expose a
textual representation of those numbers in the what() string" that the
guideline is not recommending against messages.


I agree again. My default system call exception base class exposes the
name, line, file and errno value of failed system calls as well as
providing a formatted message via what(). So I can either catch and
attempt to recover, or simply call what() and exit.

I'm not against providing data with an exception, but the receiver has
to be aware that it exists via the interface. In your case I guess you
have something like getFunctionName(), getLine(), getFileName(),
getErrno() in your exception interface? That's perfectly fine, but I
still need to be convinced that the single entry point of retrieving
extra data is good design in this case.

I.e. it's not far from having an exception function like this:

exception.getIntData( "MaxSize" );

it's used in databases but I'm not sure if it's good design for exceptions.
Another important point made there is to defer the formatting to the
what() member. In order to do that, the relevant data has to be included
in the exception object.

That's important but could be solved with getters in the interface.
 
I

Ian Collins

I'm not against providing data with an exception, but the receiver has
to be aware that it exists via the interface. In your case I guess you
have something like getFunctionName(), getLine(), getFileName(),
getErrno() in your exception interface? That's perfectly fine, but I
still need to be convinced that the single entry point of retrieving
extra data is good design in this case.

I.e. it's not far from having an exception function like this:

exception.getIntData( "MaxSize" );

it's used in databases but I'm not sure if it's good design for exceptions.

KISS! All the data in the exception is const, so it's public.

Getters (and setters) are evil!
That's important but could be solved with getters in the interface.

Eh?
 
D

DeMarcus

I don't see anyone advocating providing arbitrary data with a general
exception.


See my other response.

I don't provide arbitrary data, I include enough for the catcher to
either bail, print something meaningful or attempt recovery.

One of the design considerations raised is whether to process the
available information in the exception constructor and store this result
in the "what" string, or store the information in the exception and
perform the processing in the what() member. I prefer the latter.
There's enough information in the exception object to make a choice when
it is caught.

Exactly, that's also a good solution. But from the examples of boost, see

http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/tutorial_transporting_data.html

they want the receiver to understand how to extract the data, and I'm
still not convinced if that is an ok design.

I'm not against optional data; data that are sought but missing. I'm
against optional data in the sense that one error sends some data, and
another error of the same type sends other data.
 
D

DeMarcus

KISS! All the data in the exception is const, so it's public.

Getters (and setters) are evil!


Eh?

Sorry, my mind was set on another thing. Of course data has to be
provided at the point where the exception is thrown, but I guess you
mean that the virtual what() function of the exception knows how to deal
with that data and that's perfect.

What I meant with getters was that if not the what() function takes care
of the data one can provide getters as: getFunctionName(), getLine(),
getFileName(), getErrno(), and maybe getIPAddress() if it's a
NetworkConnectionFailedException.
 
I

Ian Collins

Exactly, that's also a good solution. But from the examples of boost, see

http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/tutorial_transporting_data.html


they want the receiver to understand how to extract the data, and I'm
still not convinced if that is an ok design.

I'm not against optional data; data that are sought but missing. I'm
against optional data in the sense that one error sends some data, and
another error of the same type sends other data.

Although they use the term "Transporting of Arbitrary Data" the data,
while arbitrary in the sense that it can have any value or meaning,
isn't arbitrary in the context of the particular call stack.

All they are providing is a form of run-time polymorphism. Rather than
create a hierarchical family of exceptions, they add contextual data as
required.

In either case, the catcher has to know what to look for; either through
a known derived exception, or through known "Arbitrary Data".
 
I

Ian Collins

Sorry, my mind was set on another thing. Of course data has to be
provided at the point where the exception is thrown, but I guess you
mean that the virtual what() function of the exception knows how to deal
with that data and that's perfect.

What I meant with getters was that if not the what() function takes care
of the data one can provide getters as: getFunctionName(), getLine(),
getFileName(), getErrno(), and maybe getIPAddress() if it's a
NetworkConnectionFailedException.

Or boos::get_error_info! As I just said in another reply, you have to
know what to look for. The only difference is how you look.
 
D

DeMarcus

Although they use the term "Transporting of Arbitrary Data" the data,
while arbitrary in the sense that it can have any value or meaning,
isn't arbitrary in the context of the particular call stack.

All they are providing is a form of run-time polymorphism. Rather than
create a hierarchical family of exceptions, they add contextual data as
required.

In either case, the catcher has to know what to look for; either through
a known derived exception, or through known "Arbitrary Data".

Yes, and 'through known "Arbitrary Data"' sound like type switching to me.
 
D

DeMarcus

Yes, and 'through known "Arbitrary Data"' sound like type switching to me.

All in all: I don't like the "arbitrary data" in boost exception, but
the tag technique they use is innovative and may fit nice somewhere else.
 
I

Ian Collins

All in all: I don't like the "arbitrary data" in boost exception, but
the tag technique they use is innovative and may fit nice somewhere else.

That's really what it boils down to - personal likes and dislikes.
Technically it's sound.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top