By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,929 Members | 1,245 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,929 IT Pros & Developers. It's quick & easy.

Exception handling

P: n/a
In C++ what kind of unexpected conditions should be handled as exceptions?
Besides dividing by 0, bad memory allocation, what're the most popular
exceptions?

When should not use exception, instead, use regular function returns?

Thanks!
Jul 22 '05 #1
Share this Question
Share on Google+
27 Replies


P: n/a

"garyolsen" <ga*******@yahoo.com> wrote in message
news:H2***********************@bgtnsc04-news.ops.worldnet.att.net...
In C++ what kind of unexpected conditions should be handled as exceptions?
Besides dividing by 0,
AFAIK, divide by zero does not cause an exception to be thrown.
Of course you could check for it beforehand, and throw your
own exception.
bad memory allocation,
C++'s operator new throws a 'std::bad_alloc' exception if
unable to satisfy an allocation request.
what're the most popular
exceptions?
Popular? C++ defines certain circumstances upon which an
exception is thrown, and which one. I suppose this behavior
will be 'popular' with some folks, and not so with others.

When should not use exception, instead, use regular function returns?
I use the general 'rule of thumb' that if a condition is
truly 'exceptional' (i.e. it's not possible for the program
to continue in a reasonable manner), then I throw (or propagate
an already thrown) an exception, but if it's possible to correct
a 'error' condition (either programmatically or with via user
intervention), I use a return value to indicate an error, allowing
the caller to retry the operation that failed.

-Mike


Thanks!

Jul 22 '05 #2

P: n/a
garyolsen wrote:
In C++ what kind of unexpected conditions should be handled as exceptions?
Besides dividing by 0, bad memory allocation, what're the most popular
exceptions?

When should not use exception, instead, use regular function returns?


The answer to this question varies greatly between very experienced C++
programmers.

Paradigm 1.

Use exceptions to manage situations where somthing that is unusual
happens. For example, if you have a "file object" and the file fails to
be created (on disk) then throw an exception to indicate that the object
is not constructed - btw throwing an exception is the only way to
indicate that a constructor failed. In other words, if you were writing
some code e.g.
file d_file( "file.name" ); // throws on open failure

d_file.write( bunch_o_bytes ); // throws when outa space

file d_copy( "file2.name" );

d_copy = d_file; // throws when copy failed.

Notice that all the error handing is managed for you.

However, who deletes file2.name when the copy failed or what happens to
file.name when bunch_o_bytes failed to write all the bytes - does it
roll-back ?
Paradigm 2.

Use exceptions only in EXCEPTIONAL circumstances. This makes some of
the code more mundane, however it forces the programmer to consider the
situation and ends up being cleaner code. (that's the theory). Error
conditions are returned or available as object state. Only "failure"
end up throwing an exception.

The code however is awash with if statements and lots of code to deal
with "failure" return values.
Personally, I subscribe to Paradigm 2 for somewhat historical reasons as
well as experience. Originally, compilers did nasty stuff with
exceptions and debuggers made it difficult to diagnose the actual error.
Also, for example, consider the code in View 1 above, I have seen
sloppy code were either the error is not written anywhere and you have
NO idea why the code just exited OR it comes back and says "failed to
create file" - well - which file ? - what was the error ? So, some of
my issues have been addressed but the potential for programmers to write
sloppy code in Paradigm 1 is really my biggest concern. It's alot
easier to inspect that someone has done the right thing when all the
error handling is explicit. I'm willing to be persuaded though.

Jul 22 '05 #3

P: n/a

"garyolsen" <ga*******@yahoo.com> wrote in message
news:H2***********************@bgtnsc04-news.ops.worldnet.att.net...
In C++ what kind of unexpected conditions should be handled as exceptions?
Besides dividing by 0, bad memory allocation, what're the most popular
exceptions?

When should not use exception, instead, use regular function returns?


Basic rule of thumb is: if the function can NOT do what it was asked to do,
and can't find any kind of reasonable workaround, then throw an exception.
Otherwise, use regular function returns.
Jul 22 '05 #4

P: n/a
jeffc wrote:
"garyolsen" <ga*******@yahoo.com> wrote in message
news:H2***********************@bgtnsc04-news.ops.worldnet.att.net...
In C++ what kind of unexpected conditions should be handled as exceptions?
Besides dividing by 0, bad memory allocation, what're the most popular
exceptions?

When should not use exception, instead, use regular function returns?

Basic rule of thumb is: if the function can NOT do what it was asked to do,
and can't find any kind of reasonable workaround, then throw an exception.
Otherwise, use regular function returns.


Hmmm!

This is a little bit of fun. Say I have a Point class which
I want to have a default constructor so I can do things like:

Point start,end;
curve.end_points(start,end);

Now the problem with a default constructor for something
like Point is that there is no valid default you can give
it. So code like:

Point a,b;
Vector vec(a-b);

is as bad as:

int a,b
int c = a-b;

we'd like to detect when a Point's value is being used
before it is properly set. One way to do this would be to
add some validity flag to the Point class which is false
when default constructed and set true once the point has
been properly initialized. Another way is to encode some
impossible value into the the z value (1.79769e+308). I'll
take the later approach and add a method to check the
validity too:
#include <iostream>
#include <ctime>
#include <cmath>
#include <limits>
#include <cassert>

using namespace std;

class Point{
double m_x;
double m_y;
double m_z;
public:
Point() {
m_z = numeric_limits<double>::max();
}
Point(double x, double y, double z) {set(x,y,z);}
void set(double x, double y, double z) {m_x = x, m_y =
y; m_z = z;}
const double& x() const { return m_x;}
const double& y() const { return m_y;}
const double& z() const { return m_y;}
bool coincident(const Point& a, double eps) const;
bool is_valid() const {
return m_z != numeric_limits<double>::max();
}
};
Now lets consider the function Point::coincident() which
returns true if two points are concident to some tolerance.
However comparing points that haven't been properly
initialized is something we ought to guard against. So I'll
present two versions one using assertions and one using
exceptions, a main to test it:
#ifdef USE_EXCEPTIONS
bool Point::coincident(const Point& a, double eps) const
{
if (!is_valid()) {
throw int(1);
}
return abs(x()-a.x()) < eps &&
abs(y()-a.y()) < eps &&
abs(z()-a.z()) < eps;
}

void test(const Point& a, const Point& b)
{
try {
a.coincident(b, 1e-5);
}
catch (int) {
cout << "caught exception" << endl;
}
}
#else
bool Point::coincident(const Point& a, double eps) const
{
assert(is_valid());
return abs(x()-a.x()) < eps &&
abs(y()-a.y()) < eps &&
abs(z()-a.z()) < eps;
}

void test(const Point& a, const Point& b)
{
a.coincident(b, 1e-5);
}
#endif
int main()
{
time_t start = time(0);
Point a(10.0,10.0,20.0);
Point b(10.0,10.0,20.0);
for (int i = 0; i < 10000000; ++i) {
test(a,b);
}

cout << time(0) - start << endl;
return 0;
}

ideally each of the Point::x(), Point::y() and Point::z()
functions should also check that their instance is valid too
but if you compile and run the program you'll see why I
haven't done that.

Using GCC-3.3.1 on my steam powered PC I get the following
results:

g++ point.C -O2
../a.exe
lapsed time: 6
g++ point.C -O2 -DUSE_EXCEPTIONS
../a.exe
lapsed time: 35

Draw your own moral!

Jul 22 '05 #5

P: n/a
lilburne wrote:
<snip>

we'd like to detect when a Point's value is being used before it is
properly set.

<snip>

This situation is not an "exceptional" situation at all. This is a bug,
in which case neither exceptions or return codes are appropriate.
assert() is the way to go here.

An attempt to use an uninitialized _anything_ is a bug, and you should
not try to handle bugs gracefully. Your program should crash, and crash
loudly, so you can start fixing the problem. In a real program, perhaps
this indicates a race condition or a problem in an input validation
routine. Whatever the cause may be, you certainly don't want to handle
it gracefully; you want it to crash immediately.
I realize lilburne was pointing out the overhead associated with
exceptions. However, I've seen too many developers deliberating on
using exceptions or return values what a simple assert() is far superior
to either given the situation.

- Adam

--
Reverse domain name to reply.

Jul 22 '05 #6

P: n/a
Adam Fineman wrote:
lilburne wrote:
<snip>

we'd like to detect when a Point's value is being used before it is
properly set.
<snip>

This situation is not an "exceptional" situation at all. This is a bug,
in which case neither exceptions or return codes are appropriate.
assert() is the way to go here.

An attempt to use an uninitialized _anything_ is a bug, and you should
not try to handle bugs gracefully. Your program should crash, and crash
loudly, so you can start fixing the problem. In a real program, perhaps
this indicates a race condition or a problem in an input validation
routine. Whatever the cause may be, you certainly don't want to handle
it gracefully; you want it to crash immediately.


You'd love our code. Very agressive, minimal defensive
coding, more assertions than 'real' code. Not a single throw
in sight.

I realize lilburne was pointing out the overhead associated with
exceptions. However, I've seen too many developers deliberating on
using exceptions or return values what a simple assert() is far superior
to either given the situation.


Sadly too true. I blame java.

Jul 22 '05 #7

P: n/a

"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...
it. So code like:

Point a,b;
Vector vec(a-b);

is as bad as:

int a,b
int c = a-b;


I can't say I ran your code, but in general I don't consider this relevant.
This is just bad code. If we "took exception" to all potential occurrences
of bad code (presumably you don't *know* it's bad, or you wouldn't have done
it), then our code would be 90% error checking and exceptions, and then we'd
have to get into the error checking and exceptions on our error checking and
exceptions, at which time the amount of actual work done in our program
approaches 0 percent.
Jul 22 '05 #8

P: n/a

"Adam Fineman" <af******@retupmoc.org> wrote in message
news:hE*****************@news.uswest.net...

I realize lilburne was pointing out the overhead associated with
exceptions. However, I've seen too many developers deliberating on
using exceptions or return values what a simple assert() is far superior
to either given the situation.


I hardly ever use assert, because it's debug only. I need error checking I
can depend on "in the field" as well as "in house". Why use 2 different
systems?
Jul 22 '05 #9

P: n/a
jeffc wrote:
"Adam Fineman" <af******@retupmoc.org> wrote in message
news:hE*****************@news.uswest.net...
I realize lilburne was pointing out the overhead associated with
exceptions. However, I've seen too many developers deliberating on
using exceptions or return values what a simple assert() is far superior
to either given the situation.

I hardly ever use assert, because it's debug only. I need error checking I
can depend on "in the field" as well as "in house". Why use 2 different
systems?

I also don't think that it's generally a good idea to have "release" and
"debug" versions of anything, unless it's proven through profiling that
it's necessary.

Look at it this way: When (not if) an end user finds a bug in your
application, what do you want him to report? That he has a core dump
and an error message from the OS, which will allow you to debug it -- or
that he got a message stating, "This program cannot continue due to an
uninitialized variable. Please call customer support."

Bugs are easier to find if they crash the program. Why would anyone
want to handle such a bug gracefully, if it makes it harder to fix?

Of course, I am not referring to life-critical or fault-tolerant
applications.

- Adam

--
Reverse domain name to reply.

Jul 22 '05 #10

P: n/a

"Adam Fineman" <af******@retupmoc.org> wrote in message
news:rt*****************@news.uswest.net...

I also don't think that it's generally a good idea to have "release" and
"debug" versions of anything, unless it's proven through profiling that
it's necessary.

Look at it this way: When (not if) an end user finds a bug in your
application, what do you want him to report? That he has a core dump
and an error message from the OS, which will allow you to debug it -- or
that he got a message stating, "This program cannot continue due to an
uninitialized variable. Please call customer support."

Bugs are easier to find if they crash the program. Why would anyone
want to handle such a bug gracefully, if it makes it harder to fix?


Those are not mutually exclusive things, as you assume. It's not much
harder to say "This program cannot continue due to a null pointer on line 51
of module xyz."
Jul 22 '05 #11

P: n/a
jeffc wrote:
"Adam Fineman" <af******@retupmoc.org> wrote in message
news:rt*****************@news.uswest.net...

Bugs are easier to find if they crash the program. Why would anyone
want to handle such a bug gracefully, if it makes it harder to fix?

Those are not mutually exclusive things, as you assume. It's not much
harder to say "This program cannot continue due to a null pointer on line 51
of module xyz."


Well to use your phrase "That is just bad code." You say you
wouldn't test for such so just:

If we "took exception" to all potential occurrences
of bad code (presumably you don't *know* it's bad, or you
wouldn't have done it), then our code would be 90% error
checking and exceptions,

so just how are you going to get "This program cannot
continue due to a null pointer on line 51 of module xyz."
displayed?

Jul 22 '05 #12

P: n/a
jeffc wrote:

I can't say I ran your code, but in general I don't consider this relevant.
This is just bad code. If we "took exception" to all potential occurrences
of bad code (presumably you don't *know* it's bad, or you wouldn't have done
it), then our code would be 90% error checking and exceptions, and then we'd
have to get into the error checking and exceptions on our error checking and
exceptions, at which time the amount of actual work done in our program
approaches 0 percent.


That is exactly what you want. We have up to 95% overhead in
our debug builds due to assertions of the type described.
Many 'one' or 'two' line functions will have a dozen lines
of assertions.

And yes you test the asserts - that given bad inputs the
methods assert.

Jul 22 '05 #13

P: n/a
jeffc wrote:
"Adam Fineman" <af******@retupmoc.org> wrote in message
news:hE*****************@news.uswest.net...
I realize lilburne was pointing out the overhead associated with
exceptions. However, I've seen too many developers deliberating on
using exceptions or return values what a simple assert() is far superior
to either given the situation.

I hardly ever use assert, because it's debug only. I need error checking I
can depend on "in the field" as well as "in house". Why use 2 different
systems?


Each method of our Point class asserts is_valid(), anyone
using a Point that they haven't properly initialized will
find the application program and test program assert the
moment the Point is used. The result is that uninitialized
Points in exercised code simply don't survive. The only way
a developer can get code containing an uninitialized Point
accepted by integration is if it is in some obscure pathway
that isn't exercised by any test. What happens when an
asssertion fires? Well in our development builds the
debugger is fired up and the developer is looking at
location of first use with a full stack trace. No hunting
around trying to find out where.
Jul 22 '05 #14

P: n/a
jeffc wrote:
"Adam Fineman" <af******@retupmoc.org> wrote in message
news:rt*****************@news.uswest.net...
I also don't think that it's generally a good idea to have "release" and
"debug" versions of anything, unless it's proven through profiling that
it's necessary.

Look at it this way: When (not if) an end user finds a bug in your
application, what do you want him to report? That he has a core dump
and an error message from the OS, which will allow you to debug it -- or
that he got a message stating, "This program cannot continue due to an
uninitialized variable. Please call customer support."

Bugs are easier to find if they crash the program. Why would anyone
want to handle such a bug gracefully, if it makes it harder to fix?


Those are not mutually exclusive things, as you assume. It's not much
harder to say "This program cannot continue due to a null pointer on line 51
of module xyz."

It is significantly more effort to code and maintain that kind of
message vs. a simple assert(). Moreover, the failed assert() can also
give you a core dump, which is useful for figuring out *why* there is a
null pointer on line 51.

- Adam

--
Reverse domain name to reply.

Jul 22 '05 #15

P: n/a

"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...
jeffc wrote:

I can't say I ran your code, but in general I don't consider this relevant. This is just bad code. If we "took exception" to all potential occurrences of bad code (presumably you don't *know* it's bad, or you wouldn't have done it), then our code would be 90% error checking and exceptions, and then we'd have to get into the error checking and exceptions on our error checking and exceptions, at which time the amount of actual work done in our program
approaches 0 percent.


That is exactly what you want. We have up to 95% overhead in
our debug builds due to assertions of the type described.


I didn't say anything about assertions.
Jul 22 '05 #16

P: n/a

"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...


Well to use your phrase "That is just bad code." You say you
wouldn't test for such so just:

If we "took exception" to all potential occurrences
of bad code (presumably you don't *know* it's bad, or you
wouldn't have done it), then our code would be 90% error
checking and exceptions,

so just how are you going to get "This program cannot
continue due to a null pointer on line 51 of module xyz."
displayed?


You've got code there anyway to say there's an error. Rather than
preferring an abend (which can NOT necessarily be reproduced, regardless of
Adam's premise), just add a macro for line and file to the error statement
that already exists.
Jul 22 '05 #17

P: n/a

"Adam Fineman" <af******@retupmoc.org> wrote in message
news:fA*****************@news.uswest.net...

Those are not mutually exclusive things, as you assume. It's not much
harder to say "This program cannot continue due to a null pointer on line 51 of module xyz."

It is significantly more effort to code and maintain that kind of
message vs. a simple assert().


That's beside the point. The assert never happens in production code, so
you get NOTHING "in the field".
Jul 22 '05 #18

P: n/a
jeffc wrote:
"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...
Well to use your phrase "That is just bad code." You say you
wouldn't test for such so just:

If we "took exception" to all potential occurrences
of bad code (presumably you don't *know* it's bad, or you
wouldn't have done it), then our code would be 90% error
checking and exceptions,

so just how are you going to get "This program cannot
continue due to a null pointer on line 51 of module xyz."
displayed?

You've got code there anyway to say there's an error. Rather than
preferring an abend (which can NOT necessarily be reproduced, regardless of
Adam's premise), just add a macro for line and file to the error statement
that already exists.


What code? Where? Why should there be code slowing down
customers application simply to detect bugs at ought to have
been caught during basic devvie testing?

Jul 22 '05 #19

P: n/a
jeffc wrote:
"Adam Fineman" <af******@retupmoc.org> wrote in message
news:fA*****************@news.uswest.net...
Those are not mutually exclusive things, as you assume. It's not much
harder to say "This program cannot continue due to a null pointer on
line 51
of module xyz."


It is significantly more effort to code and maintain that kind of
message vs. a simple assert().

That's beside the point. The assert never happens in production code, so
you get NOTHING "in the field".


What are you doing "in the field"? Isn't it wet, windy, and
cold?

A message like:

"This program cannot continue due to a null pointer on
line 51 of module xyz."

is practically useless. The question you need to ask is how
did it get to line51 of module xyz with a null pointer? What
was happening at the time?

Customer reports "When I apply X to this model of a turbine
blade the result is incorrect." Devvie loads customers model
into debug application and applies X. Debugger pops up with
assertion and stack trace at the point that things starting
to go wrong. Usually takes less than 30 minutes, from the
start of a devvie taking an interest to the cause being
discovered.

Jul 22 '05 #20

P: n/a
garyolsen wrote:
In C++ what kind of unexpected conditions should be handled as exceptions?
None!

An exception is an *expected* but unpredictable [random] event
that cannot be prevented but but must be "handled" at run time.
Programming errors (bugs) are *not* exceptions.
Bugs cause *unexpected* but predicable events once detected
and can only be "handled" by the programmer by fixing the bug.
Fixing the bug prevents the event from ever occurring again.
You can use the 'assert' C preprocessor macro to help detect bugs.

Whenever possible, an exception should be "handled"
at the point where it is detected.
If it cannot be completely handled at the point where it is detected,
the function must pass enough information back to the calling function
to completely handle the exception and recover. This information
is passed back to the calling function in an "exception" object.
When should not use exception, instead, use regular function returns?
A function may "throw" or "return" an exception [object].
A function that "returns" an exception cannot be used
in any expression that does not test the exception.
For example,

double mySqrt(double x, int* pError) {
double y = sqrt(x);
*pError = errno;
return y;
}

int main(int argc, char* argv[]) {
int Error = 0;
const
double x = atof(argv[1]);
const
double y = 3.0 + (mySqrt(x, &Error) + 30.0)/13.0;
return 0;
}

It isn't possible, in this case, to detect an error in mySqrt
before y is initialized with an invalid result.
Besides dividing by 0, bad memory allocation,
what're the most popular exceptions?


Any function that accepts input from the user
that can't easily be checked before passing it to the function
is a good candidate for exception handling.
In the above example, it is better to test for x < 0.0
before attempting to initialize y.

This topic has been discussed extensively
in the comp.lang.c++ newsgroup so it might be a good idea
to consult Google Groups

http://groups.google.com/

Jul 22 '05 #21

P: n/a

"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...

You've got code there anyway to say there's an error. Rather than
preferring an abend (which can NOT necessarily be reproduced, regardless of Adam's premise), just add a macro for line and file to the error statement that already exists.


What code? Where? Why should there be code slowing down
customers application simply to detect bugs at ought to have
been caught during basic devvie testing?


Are you in the same thread I am? Adam wrote, "That he has a core dump
and an error message from the OS, which will allow you to debug it -- or
that he got a message stating, "This program cannot continue due to an
uninitialized variable. Please call customer support.""
Jul 22 '05 #22

P: n/a

"Adam Fineman" <af******@retupmoc.org> wrote in message
news:fA*****************@news.uswest.net...

Those are not mutually exclusive things, as you assume. It's not much
harder to say "This program cannot continue due to a null pointer on line 51 of module xyz."

It is significantly more effort to code and maintain that kind of
message vs. a simple assert().


Sure it is. But the basic problem here is that the assert is useless - it's
not compiled into production code.
Jul 22 '05 #23

P: n/a

"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...

That's beside the point. The assert never happens in production code, so you get NOTHING "in the field".
What are you doing "in the field"? Isn't it wet, windy, and
cold?


I don't think that's the relevant question - the relevant question is "what
are you doing in your ivory tower? Isn't it quiet, lonely, and lacking in
revenue?"
A message like:

"This program cannot continue due to a null pointer on
line 51 of module xyz."

is practically useless. The question you need to ask is how
did it get to line51 of module xyz with a null pointer? What
was happening at the time?


Hardly useless. Anyway, compare that to an assert.
Jul 22 '05 #24

P: n/a
jeffc wrote:
"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...
You've got code there anyway to say there's an error. Rather than
preferring an abend (which can NOT necessarily be reproduced, regardless
of
Adam's premise), just add a macro for line and file to the error
statement
that already exists.

What code? Where? Why should there be code slowing down
customers application simply to detect bugs at ought to have
been caught during basic devvie testing?

Are you in the same thread I am?


Dunno! Adam and myself seem to understand the point
perfectly well.
Adam wrote, "That he has a core dump
and an error message from the OS, which will allow you to debug it -- or
that he got a message stating, "This program cannot continue due to an
uninitialized variable. Please call customer support.""


Actually he said he'd rather have a customer reported a core
dump due to an uninitialized variable than an error message,
because given the core dump he can more quickly find the
cause of the error. Though to be fair an uninitialized
variable is more likely to produce erroneous results rather
than a core dump.

Nevertheless to be able to display an error message rather
than crashing, the program has to be checking for the
condition, and it has to be doing this in the customer
version of the program thus slowing it down. Here is an
example of a bug concerning null pointers.

Version A using guards:

bool error = false;
if (somePtr != 0) {
error = somePtr->someMethod();
// other statements
} else {
error = false;
}
Version B using asserts:

assert(somePtr != 0);
bool error somePtr->someMethod();
// other statements
version B is equivalent to A in debug builds, but faster in
release builds because the check is omitted. If somePtr is
ever 0 in a release build the program crashes, but it never
should be 0. Therefore it doesn't need to be checked in
release because the assert has caught any instances of it
being 0 during testing.

Jul 22 '05 #25

P: n/a
jeffc wrote:
"lilburne" <li******@godzilla.net> wrote in message
news:bp*************@ID-203936.news.uni-berlin.de...
That's beside the point. The assert never happens in production code,
so
you get NOTHING "in the field".


What are you doing "in the field"? Isn't it wet, windy, and
cold?

I don't think that's the relevant question - the relevant question is "what
are you doing in your ivory tower? Isn't it quiet, lonely, and lacking in
revenue?"


Revenue is fine thank you. The latest release has a "New
functionality" document that is over 200pp (note that is new
features not bug fixes) which isn't too bad for a 'mature'
product.

A message like:

"This program cannot continue due to a null pointer on
line 51 of module xyz."

is practically useless. The question you need to ask is how
did it get to line51 of module xyz with a null pointer? What
was happening at the time?

Hardly useless. Anyway, compare that to an assert.


The assert doesn't do anything in a release build, and that
is its advantage, you can afford to put more tests into your
code then you ever would if the tests were to be exercised
in release versions.

Would we ever put a check for validity into every method of
our Point class if it were to be exercised in customer
builds? Of course not, the test will be exercised millions
of times, but in debug versions we'll pay the price because
it eliminates a whole category of bugs due to an
uninitialized Point.

And so it goes on.

Array bounds checking, I don't want no stinking bounds
checking in release, but debug builds yes please. Write code
that overruns the elements in the dynamic array class and
BANG the asserts fire.

Now assertions only work as part of a quality system. You
need to be testing your code, and testing it regularly.

Jul 22 '05 #26

P: n/a
This comment from jeffc sparked a thought:
The assert doesn't do anything in a release build, and that
is its advantage, you can afford to put more tests into your
code then you ever would if the tests were to be exercised
in release versions.

Would we ever put a check for validity into every method of
our Point class if it were to be exercised in customer
builds? Of course not, the test will be exercised millions
of times
...


Error checking (and recovery) in itself isn't a bad thing in
a release build - it's the cost that people object to. A really
clever compiler could potentially take care of this problem -
for example:

T& Array::operator [] (int index)
{
assert(index >= 0 && index < arrayLength);
return arrayVal[index];
}

void incrementValues(Array &a)
{
for (int n = 0;n < a.length();++n)
{
++a[n];
}
}

An intelligent compiler could recognise that in this particular
loop, the assert condition is always false, and so the check is
not necessary at run time.

Has anyone ever seen an optimising compiler that actually does
this ?

This would require:

- data flow analysis across function boundaries
- some way of preventing execution of the "assert" code:
- multiple entry points to functions with preconditions ?
- multiple versions of functions, with and without checks ?
(similar to the code generation done by templates)
(but avoid having 2^N versions of a function with N asserts !)
- possibly embedding this information in object modules / libraries
so separate compilation is handled, too

This applies to "optimising out" any block of code the compiler
knows will never be entered, not just asserts like this one.

David F
Jul 22 '05 #27

P: n/a
David Fisher wrote:
This comment from jeffc sparked a thought:

The assert doesn't do anything in a release build, and that
is its advantage, you can afford to put more tests into your
code then you ever would if the tests were to be exercised
in release versions.

Would we ever put a check for validity into every method of
our Point class if it were to be exercised in customer
builds? Of course not, the test will be exercised millions
of times
...

Error checking (and recovery) in itself isn't a bad thing in
a release build - it's the cost that people object to. A really
clever compiler could potentially take care of this problem -
for example:

T& Array::operator [] (int index)
{
assert(index >= 0 && index < arrayLength);
return arrayVal[index];
}

void incrementValues(Array &a)
{
for (int n = 0;n < a.length();++n)
{
++a[n];
}
}

An intelligent compiler could recognise that in this particular
loop, the assert condition is always false, and so the check is
not necessary at run time.

Has anyone ever seen an optimising compiler that actually does
this ?

This would require:

- data flow analysis across function boundaries


The code I gave was actually a modification of a test I
wrote for claims by java advocates that runtime JIT
optimization produced better code than a static compiler,
because the optimization could be done dynamically based on
the data at runtime.

Sometimes the compiler can optimize the code away that it
see. In the example the calculation is constant and if you
compile it as:

g++ -O2 point.C -DNDEBUG

it runs in zero time because all the code is optimized away
using GCC 3.3.1. The optimization is done across function
boundaries.

However with the assert some threshold has been reached and
the compiler doesn't notice that Point::is_valid() is
returning a constant too.

That is the problem you can never be sure whether the code
will be optimized or not. The same is true for the inline
hint to the compiler, maybe it will inline the code maybe it
wont, you can't be sure. Nor can you be sure that what was
true in today's version of the compiler will be true in the
next version of the compiler.

GCC 3.2.x produces code for the exception version that is
only 15% slower than the assertion version, with -O3
optimization the exception version runs in the same time. I
guess that bugs forced GCC 3.3.1 to be more conservative in
its optimizations. Personally I think it is unacceptable to
be dependent on compiler version optimization strategy for
your runtime performance.

- some way of preventing execution of the "assert" code:
- multiple entry points to functions with preconditions ?
- multiple versions of functions, with and without checks ?
(similar to the code generation done by templates)
(but avoid having 2^N versions of a function with N asserts !)


You really need to separate out that which are programming
errors and that which can genuinely happen at runtime.
Assert for the former and throw an exception or defensively
code in the later case. Personally I'd only do the later
when dealing with external input, once the data is validated
and under program control you shouldn't have to worry about
bad inputs.

Jul 22 '05 #28

This discussion thread is closed

Replies have been disabled for this discussion.