Arguments *Against* Exception Use

  • Thread starter Scott Brady Drummonds
  • Start date
T

Tom Widmer

And if your "program" is actually a library which other (people's)
programs are going to link to...?

Another viewpoint is that if your program is incapable of continuing -
at least for any other reason than physical limitations (head crash,
CPU explodes, etc.) - then that is programmer error.

Right, and that's what assert helps with, by providing core dumps,
etc. Obviously, an application might want to have its own version of
assert that displays the error to the user before terminating, or
whatever.
As an example, I have written a number of C++ library routines to be
called from the Gauss linear algebra system (think Matlab but
clunkier). If my library code aborts... so does Gauss! My users do not
appreciate this. So I use exceptions to recover elegantly from
(unforseeable) situations such as resource exhaustion, then deliver an
informative explanatory message to the user ("Out of memory. Yes YOU
used it all up. Pig.", etc.).

Yes, that's what exceptions are for - *expected* exceptional
circumstances, not bugs. For example, resource exhaustion, invalid
input from files you don't control, etc. For a robust library
component such as you describe, this may include invalid parameters.

Tom
 
O

Old Wolf

Gianni Mariani said:
The biggest problem I have with exception use is that it's nigh
impossible to figure out what went wrong when somthing bad happens.

Look at the what() member function of the exception.
It returns a pointer to a string that describes exactly
what went wrong.
e.g. consider this mock up code (just one example)

struct X
{
void F1( X * x ) throw()
{
x->F2(); // somwhere we need to call F2.
}
virtual void F2() = 0;
};
The program dumps core because the default "unexpected()" is called in
the context of F1 where it promptly dumps core. All the knowledge of
what happened is lost. No stack frames, no thrown object, NADA.

In the definition of F1 you promised that it would not throw.
Then you perform an operation that could throw. This is
entirely a case of programmer error. Would you also say
that one should not use "const" because of:
const int x = 1;
*(int *)&x = 2;
causing the program to abort?

Some guidelines I find useful:
- don't use throw specifications
- in main(): try { do_everything(); }
catch(std::exception const &e) { ...e.what()...... }
catch(...) { ......... }
 
L

Lionel B

Tom said:
Right, and that's what assert helps with, by providing core dumps,
etc. Obviously, an application might want to have its own version of
assert that displays the error to the user before terminating, or
whatever.

Right. As I say my library routine simply cannot afford to terminate...
so the easiest resolution is that in any section of code where I might
be tempted to use assert, I would rather throw an exception on the
"bad" condition (I won't call it a "bug", as a bug is by [my]
definition a bad condition that you *didn't* anticipate) and catch it
in the library function called by the user. Ok, I don't get a core
dump, but as I say my program simply may not terminate. Put another
way, my "version of assert" [at least in non-debug mode] throws an
exception...

Of course there may still be bugs - bad conditions that I failed to
anticipate - that may cause my routine to abort.
 
T

Tom Widmer

Tom said:
Right, and that's what assert helps with, by providing core dumps,
etc. Obviously, an application might want to have its own version of
assert that displays the error to the user before terminating, or
whatever.

Right. As I say my library routine simply cannot afford to terminate...
so the easiest resolution is that in any section of code where I might
be tempted to use assert, I would rather throw an exception on the
"bad" condition (I won't call it a "bug", as a bug is by [my]
definition a bad condition that you *didn't* anticipate) and catch it
in the library function called by the user.

asserts aren't conditions that you anticipate will happen, but rather
conditions that you know will be true. If they are false, then
something has gone horribly wrong, and the software contains a bug.

Ok, I don't get a core
dump, but as I say my program simply may not terminate.

This does make it harder to find bugs - users may not report the
problem if it doesn't inconvenience them, and they won't have an kind
of core dump to send you. Obviously, minimizing the problem through
testing is the key.

Put another
way, my "version of assert" [at least in non-debug mode] throws an
exception...

Of course there may still be bugs - bad conditions that I failed to
anticipate - that may cause my routine to abort.

If you are using Win32, you could use SEH to try to "catch" these too.

Tom
 
M

Markus Elfring

And if you avoid exception specifications (which are basically useless
anyway) they don't have much overhead at all.

I disagree.
[...]
http://www.gotw.ca/publications/mill22.htm

1. Herb Sutter describes some strong arguments in the article "A
Pragmatic Look at Exception Specifications".
I see a problem in the level of support for the enforcement of
exception specifications. There seems to be a gap between the
expectations of the standard and the quality of current compiler
implementations.

2. Would you like to look at the assembler instructions to examine how
much this topic makes a difference in code size for your compiler?

Regards,
Markus
 
M

Markus Elfring

- An interface designer can specify the expected error values so that
the developer or programmer must adhere to such restrictions in the
implementation.

This checking is performed at run time.

- If the an exception specification is omitted, the compiler (that
does not perform inter-procedural analysis) must assume that every
possible exception can be thrown and must be prepared to generate code
for the handling of stack unwinding and related stuff. This detail can
be optimized if you add "throw()" to your functions and methods
because no exceptions will be thrown.

Exception specifications add generated code to the function or method
implementation to handle unexpected exceptions.
The caller can try to benefit from a "throws nothing" contract at
compile time.

=> You must consider at which time you would like to use the services
and to adjust your expectations.
 
R

Ron House

Cy said:
You have to compare

try{
dosomething();
} catch(...) {
fixit();
}

with

err_code = dosomething();
if (error_code)
fixit();

The big hit comes when dosomething() is not the raw routine that
discovered the error. Then the err_code method has to pass the failure
all the way out, testing stuff at every level. In that case the former,
as you say, wins big time.
 
I

Igor Okulist

Ron House said:
The big hit comes when dosomething() is not the raw routine that
discovered the error. Then the err_code method has to pass the failure
all the way out, testing stuff at every level. In that case the former,
as you say, wins big time.

Actually in the example :

I often don't see possibility to "fix" much with a "fixit()", basically
just clean up and pass the same error code up to the calling function.

So, on error just clean up resource and pass it back, to let the calling
function try again, hopefully with better input parameters or bail out.

Most of the my resource are reference counted (and if not then I write a
small wrapper around them that would clean up, once it goes out of
scope). In such case
becomes:

err_code = dosomething();
if (error_code) return error_code;

and i have a habit of having all return codes of less than 0 fatal error
codes, so with a macro the code becomes:
err_code = dosomething();
RETURN_ON_FAILED_MACRO(error_code);

The macro also brings in __FILE__ and __LINE__ and prints with printf or
custom log function. Now, if error happens I get a very nice call stack on
the log output. And once the __FUNCTION__ macro becomes available
the log will be even better. In this way I don't really see need to use
try/catch
constructs everywhere (yes, there ARE case when the are needed).

Any thoughts?

Regards,
Igor
 
H

Herb Sutter

I just got out of a meeting with a team of software developers that I
recently joined as they are staffing to create a medium-sized project
(potentially all of which will be written in C++). From my experience and
readings, I've come to this group indoctrinated to use exceptions for error
handling. The experience in this group that leads them to believe that
exceptions should only be used where severe runtime errors require program
termination is mainly based on the notion that the performance impact of
exceptions is prohibitive.

Besides the Meyers books and my trusty copy of "C++ FAQs", I'm going on my
intuition based on years of experience. Am I over-indoctrinated in the use
of exceptions? Are there some warnings that I'm not heeding? Can
exceptions impact performance to the degree that my coworkers claim? I'd
like to hear some anti-exception thoughts, if they exist.

Several responders already noted my article "When and How to Use
Exceptions" (C/C++ Users Journal, 22(8), August 2004). It's drawn from
Items 70 to 72 of C++ Coding Standards
(www.gotw.ca/publications/c++cs.htm).

In particular, Item 72 argues "Prefer to use exceptions to report errors."
And in particular it argues that exceptions are not for normal processing
(stuff that doesn't cause a function to fail to achieve its documented
results), which is why 72 builds on Item 70 "Distinguish between errors
and non-errors."

Herb


---
Herb Sutter (www.gotw.ca) (www.pluralsight.com/blogs/hsutter)

Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Architect, Developer Division, Microsoft (www.gotw.ca/microsoft)
 

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
474,183
Messages
2,570,970
Members
47,527
Latest member
RoxanneTos

Latest Threads

Top