Exceptions, Go to Hell!

G

Goran Pusic

Hm, I am not so sure about that. It very much depends on how religiously and
locally you handle exceptions. On one extreme end, you let everything
propagate to the top-level and have one catch-all block. On the other end of
the spectrum, you can handle each exception as early as possible. In the
later case, you will still have a sizable number of catch blocks.

My question to you is, why do you want to handle exceptions locally?
Give some examples, or better yet, don't, just look at your own code
and count try/catch-es ;-).

If you use RAII /scope guard, try/catches are of two sorts:

1. report the error and continue working (or, for top-level catch in
main, exit)
2. re-throw the error with additional info.

1. is extremely rare, for example, top-level try/catch in main or any
thread function, or, in a GUI event loop, on the said loop, or on
similar loops elsewhere, where your mode of operation is "loop
until....", and you dont' want to terminate a loop due to an error in
one iteration.

2. is more common, but depends on how precise you can be at the actual
throw site, and how much you want to add while unwinding the stack.

Goran.
 
W

wij

My question to you is, why do you want to handle exceptions locally?
Give some examples, or better yet, don't, just look at your own code
and count try/catch-es ;-).

If you use RAII /scope guard, try/catches are of two sorts:

1. report the error and continue working (or, for top-level catch in
main, exit)
2. re-throw the error with additional info.

1. is extremely rare, for example, top-level try/catch in main or any
thread function, or, in a GUI event loop, on the said loop, or on
similar loops elsewhere, where your mode of operation is "loop
until....", and you dont' want to terminate a loop due to an error in
one iteration.

2. is more common, but depends on how precise you can be at the actual
throw site, and how much you want to add while unwinding the stack.

Goran.

std::vector<T> arr=initialize();
size_t i;
try {
for(i=0; i<arr.size()+1; ++i) {
if(arr.at(i)==T()) break;
}
}
catch(const std::eek:ut_of_range& e) {
assert(i>=arr.size()); // Can I really assure this assertion?
}
catch(const std::invalid_argument& e) {
// T::T(), T::eek:perator==() and std::vector::at did not say they
// will throw this type. What will be the fate of this exception?
}

General guideline for throw site: "stack unwinding to exit".
 
G

Goran Pusic

std::vector<T> arr=initialize();
size_t i;
try {
  for(i=0; i<arr.size()+1; ++i) {
    if(arr.at(i)==T()) break;
  }}

catch(const std::eek:ut_of_range& e) {
  assert(i>=arr.size());   // Can I really assure this assertion?}

I am not sure why I was quoted for this snippet, nor what is the
purpose of it all, but here goes...

This code most certainly has a bug. It makes no sense whatsoever to
ever try to call at(v.size()).

Exceptions are __not__ means of handling bugs. A "catch" like the one
you made is an attempt at handling an exception, therefore, at
handling a bug. Therefore your catch is misplaced.

Exceptions __can__ be used to __report__ bugs, but they are often
suboptimal for that, too; if possible in the given runtime
environment, a crash and a dump is better, because that allows you to
see the exact place where problem happened, which, in case of a bug,
is important. If, however, you do want to use them to report bugs, you
should let them unwind the stack up to the end and terminate. You
__may__ do ^^^ on the way up the stack, though.

Catching the exception above, that is caused by a bug, is a __bad__
idea. Bugs should be fixed, not "handled". Unless code inside catch
can somehow change the "i<arr.size()+1" to something correct, it makes
no sense whatsoever to catch anything, except if the purpose is to
mask the bug and do something wrong anyway.

As for invalid_argument, I guess you wanted to hint at the possibility
that operator== could throw that. Well, invalid_argument, because
invalid_argument is, just like out_of_range, a logic_error, that is, a
bug. Same logic applies.

If, by any chance, you did something like:

// ^^^
catch(const logic_error& e)
{
string more("We have a bug. We caught it in " + string(__FILE__) +
", line " + string(__LINE__) + "error text was: "
+ string(e.what());
throw logic_error(more);
}

, that is, if you were in 2) of my exception classification ;-), your
catching would have some sense. The way it is now, everything you
wrote is one huge bug to me.
General guideline for throw site:  "stack unwinding to exit".

I don't know what you mean by this, but I know that throw site can't
decide what will happen with the thrown exception. Next function can
catch it, so what gives?

Goran.
 
R

RB

Well that makes sense. I apologize for letting my emotions take offense
to my own lack of awarness. But sometimes it is frustrating to try to
put down exactly what you are asking and still not have it misunderstood
by the person you are depending on for an answer.
I work in a different vocation that has long hours of detailed stuff that
most folks would find boring. But I love to program as a productive hobby
so to speak in that I have been able to progress to the point that I can
actually write routines that help me with mathematical logistics in my vocation.
Items that I used to have to do long hand on my calculator while holding
the whole scenario in my head, now I have routines that hold and track
all my data while they are computing so I don't forget where I started
or where I am.
But writing them "correctly" ah that is where all of you guys on the
newsgroups have helped me tremendously and I owe much to all of you.
RB
 
G

Goran Pusic

   I work in a different vocation that has long hours of detailed stuff that
most folks would find boring. But I love to program as a productive hobby
so to speak in that I have been able to progress to the point that I can
actually write routines that help me with mathematical logistics in my vocation.
Items that I used to have to do long hand on my calculator while holding
the whole scenario in my head, now I have routines that hold and track
all my data while they are computing so I don't forget where I started
or where I am.

If you have a lot of maths, there's specialized software for that
(Mathematica, Matlab...) that easily beats C++ in ease of use?

Goran.
 
W

wij

I am not sure why I was quoted for this snippet, nor what is the
purpose of it all, but here goes...
Because I read your sorting 2, triggered my obsession that I wanted
to notify you that the caught object (especially if they are reused)
can mean differently, not as most people thought. Throw type for
dedicated purpose is fine, though. Slightly modified example to
clarify:

std::vector<T> arr(100);
size_t i=0;
try { arr.at(i); }
catch(const std::eek:ut_of_range&) {
assert(i>=arr.size()); // Time bomb! Can I really assure this
// assertion?
}
This code most certainly has a bug. It makes no sense whatsoever to
ever try to call at(v.size()).

Exceptions are __not__ means of handling bugs. A "catch" like the one
you made is an attempt at handling an exception, therefore, at
handling a bug. Therefore your catch is misplaced.

Exceptions __can__ be used to __report__ bugs, but they are often
suboptimal for that, too; if possible in the given runtime
environment, a crash and a dump is better, because that allows you to
see the exact place where problem happened, which, in case of a bug,
is important. If, however, you do want to use them to report bugs, you
should let them unwind the stack up to the end and terminate. You
__may__ do ^^^ on the way up the stack, though.

Catching the exception above, that is caused by a bug, is a __bad__
idea. Bugs should be fixed, not "handled". Unless code inside catch
can somehow change the "i<arr.size()+1" to something correct, it makes
no sense whatsoever to catch anything, except if the purpose is to
mask the bug and do something wrong anyway.

As for invalid_argument, I guess you wanted to hint at the possibility
that operator== could throw that. Well, invalid_argument, because
invalid_argument is, just like out_of_range, a logic_error, that is, a
bug. Same logic applies.

If, by any chance, you did something like:

// ^^^
catch(const logic_error& e)
{
  string more("We have a bug. We caught it in " + string(__FILE__) +
    ", line " + string(__LINE__) + "error text was: "
    + string(e.what());
  throw logic_error(more);

}
I remember I read from a book that C++ does not guarantee the proper
handling if the size of thrown object is larger than the standard
exception type. I am appropriate if anyone can point out a reference.
The above code is potential for bad_alloc.
, that is, if you were in 2) of my exception classification ;-), your
catching would have some sense. The way it is now, everything you
wrote is one huge bug to me.


I don't know what you mean by this, but I know that throw site can't
decide what will happen with the thrown exception. Next function can
catch it, so what gives?

Goran.
Maybe just different terms for the same thing, not necessarily
against your opinion.
 
W

wij

Because I read your sorting 2, triggered my obsession that I wanted
to notify you that the caught object (especially if they are reused)
can mean differently, not as most people thought. Throw type for
dedicated purpose is fine, though. Slightly modified example to
clarify:

std::vector<T> arr(100);
size_t i=0;
try { arr.at(i); }
catch(const std::eek:ut_of_range&) {
  assert(i>=arr.size());   // Time bomb! Can I really assure this
                                           // assertion?

}








I remember I read from a book that C++ does not guarantee the proper
handling if the size of thrown object is larger than the standard
exception type. I am appropriate if anyone can point out a reference.
The above code is potential for bad_alloc.




Maybe just different terms for the same thing, not necessarily
against your opinion.

Correction. The example should be:

std::vector<T> arr(100);
size_t i=101;
try { arr.at(i); }
catch(const std::eek:ut_of_range&) {
assert(i>=arr.size()); // Time bomb! Can I really assure this
// assertion,
}
 
W

wij

Really really apologize, the above is not correct.
It should be:

std::vector<T> arr=initialize();
size_t i;
try {
for(i=0; i<arr.size(); ++i) {
if(arr.at(i)==T()) break;
}
}
catch(const std::eek:ut_of_range& e) {
assert(i>=arr.size()); // Time bomb! Can I really assure this
// assertion?
}
 
J

joe

Goran said:
Exceptions are __not__ means of handling bugs. A "catch" like the one
you made is an attempt at handling an exception, therefore, at
handling a bug. Therefore your catch is misplaced.

Exceptions __can__ be used to __report__ bugs, but they are often
suboptimal for that, too; if possible in the given runtime
environment, a crash and a dump is better, because that allows you to
see the exact place where problem happened, which, in case of a bug,
is important. If, however, you do want to use them to report bugs, you
should let them unwind the stack up to the end and terminate. You
__may__ do ^^^ on the way up the stack, though.

Assertions ("bug catchers" if you will) can be mapped to exceptions for a
release build to facilitate reporting to the developer, for example.
 
Ö

Öö Tiib

If vm.overcommit_memory == 0, Linux uses "heuristic overcommit
handling", whatever that means (but it does mean
overcommitting).  1 means always overcommit, and 2 means don't
overcommit.  If vm_.overcommit_memory is anything but 2, your
system is unreliable.  (On the other hand, you may be able to do
more with it if it doesn't crash.  Some programs do allocate
large blocks of memory that they don't fully use.)

Heuristic sounds suspicious, perhaps that 2 is best then, thanks.
 
I

Ian Collins

Assertions ("bug catchers" if you will) can be mapped to exceptions for a
release build to facilitate reporting to the developer, for example.

Unless the exception somehow preserves the context of the assertion, it
will be worse than useless.
 
J

joe

Ian said:
Unless the exception somehow preserves the context of the assertion,
it will be worse than useless.

A programmer is free to make his exception classes as elaborate as he
wants to. Attach a stack trace, for example, if he wants to.
 
I

Ian Collins

A programmer is free to make his exception classes as elaborate as he
wants to. Attach a stack trace, for example, if he wants to.

Which is what I said. But no matter how elaborate the exception is, if
the stack is munted, or memory is exhausted or <insert bad things here>
when it is thrown, it is still worse than useless.
 
K

Kai-Uwe Bux

Ian said:
Which is what I said. But no matter how elaborate the exception is, if
the stack is munted, or memory is exhausted or <insert bad things here>
when it is thrown, it is still worse than useless.

True, but not really relevant is this context, or is it? Here, the focus is
on exceptions that replace asserts. If your application suffers from a
corrupted stack, you are in UB land anyway and neither asserts nor
exceptions will handle that gracefully. Thus, I don't see how that says
anything on the alternative of reporting the manifestation of a bug via
asserts or exceptions.


Best

Kai-Uwe Bux
 
J

joe

Ian said:
Which is what I said. But no matter how elaborate the exception is,
if the stack is munted, or memory is exhausted or <insert bad things
here> when it is thrown, it is still worse than useless.

Asserts aren't going to be about OOM and such. Note that asserts catch
things before they go awry mostly, like null ptr args, else you wouldn't
use them for debugging in the first place. So whatever information the
developer has been receiving during development, he can still get from
released software if he wants to. Just because the attempt to report the
error may, in the rare case, fail doesn't mean you can't make a best
effort try to do so. It's of course, application-specific and
programmer-specific. I don't use exceptions that way, but surely some do,
and I may analyze doing so in the future.
 
I

Ian Collins

True, but not really relevant is this context, or is it? Here, the focus is
on exceptions that replace asserts. If your application suffers from a
corrupted stack, you are in UB land anyway and neither asserts nor
exceptions will handle that gracefully. Thus, I don't see how that says
anything on the alternative of reporting the manifestation of a bug via
asserts or exceptions.

Fair enough. A more realistic situation would be a method detecting its
object is in an inconsistent state. Throwing an exception would very
likely result in an attempt to destroy the object during stack
unwinding, causing more grief.

I for one much prefer a core file!
 
Ö

Öö Tiib

Fair enough.  A more realistic situation would be a method detecting its
object is in an inconsistent state.  Throwing an exception would very
likely result in an attempt to destroy the object during stack
unwinding, causing more grief.

I for one much prefer a core file!

Depends on type of object and inconsistency. For example if external
device tells that value of some channel on it is 5 for what your
software has information that minimum value for it is 0 and maximum
value is 4. Is software broken? Is device broken? Has device been
upgraded without upgrading software? Maybe there was noise in
communication channel? It is highly likely that neither device maker
nor its operator expects the software to suicide right away in such a
situation.
 
I

Ian Collins

Depends on type of object and inconsistency. For example if external
device tells that value of some channel on it is 5 for what your
software has information that minimum value for it is 0 and maximum
value is 4. Is software broken? Is device broken? Has device been
upgraded without upgrading software? Maybe there was noise in
communication channel? It is highly likely that neither device maker
nor its operator expects the software to suicide right away in such a
situation.

True. But the discussion was about mapping assertions to exceptions
(which is still suicide) as a diagnostic tool. The situation you
describe probably wouldn't justify an assertion.
 
Ö

Öö Tiib

True.  But the discussion was about mapping assertions to exceptions
(which is still suicide) as a diagnostic tool.  The situation you
describe probably wouldn't justify an assertion.

Yes, but that is sort of too low level to decide what is good.
Assertion is usually about contract violation. For example my function
is described to not accept null pointer arguments, then it asserts
that argument is not null. If it was described to throw on null
pointers, then it throws when argument is null. What is "better"
contract is difficult to tell without better description of whole
context. Better description of whole context however is like i gave
(external device and its channels and the like). A part of program may
assert that such channel is between 0 and 4 or it may throw if it is
not between 0 and 4 or it may simply draw that 5 with red paint. All
about contract.
 
I

Ian Collins

Yes, but that is sort of too low level to decide what is good.
Assertion is usually about contract violation. For example my function
is described to not accept null pointer arguments, then it asserts
that argument is not null. If it was described to throw on null
pointers, then it throws when argument is null.

That isn't the topic of this sub-thread. it all started with

"Assertions ("bug catchers" if you will) can be mapped to exceptions for
a release build to facilitate reporting to the developer, for example."

Which unfortunately got snipped.

My argument is that this is seldom, if ever a good idea because the
context of the exception is lost and unwinding can do more damage.
 

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,773
Messages
2,569,594
Members
45,119
Latest member
IrmaNorcro
Top