From: Doug Harrison [MVP] on
On Tue, 22 Jun 2010 13:12:33 -0400, Joseph M. Newcomer
<newcomer(a)flounder.com> wrote:

>>P.S. Don't forget: you are not allowed to write try/catch
>>statements ;-). If you do, you have 99% chance that you're doing it
>>wrong. Good C++ code of today has RIDICULOUSLY small number of try/
>>catch-es in ti.
>****
>I'm not sure this is a good piece of advice. I use try/catch a lot; it is essentially a
>"non-local GOTO", a structured way of aborting execution and returning to a known place,
>while still guaranteeing that all intermediate destructors for stack variables are called.
>It is particularly useful in writing tasks like recursive-descent parsers (particularly if
>you just want to stop without trying to do error recovery, which is always hard) and
>terminating threads while still guaranteeing that you return from the top-level thread
>function. It is clean and well-structured way of aborting a partially-completed
>operation. An it is the only way to report errors from operations like 'new'. Also, look
>at the number of exceptions that can be thrown by std:: or boost::.

Goran was not saying exceptions are bad, just that overly frequent use of
try/catch is bad, which it usually is. I've been saying for a long long
time that there's an inverse relationship between the number of try/catch
clauses you have and the effectiveness with which you're using exceptions.
There are a number of reasons for this. On the API designer side, using
exceptions where return codes are more appropriate forces callers to write
try/catch whenever they use the function, so that's a bad use of
exceptions. You never want to turn exception usage into a clumsier version
of return codes. On the user side, try/catch gets overused when people
don't employ the RAII idiom and need to perform clean-up that should be
handled by a destructor. Ideally, exceptions are caught far away from where
they're thrown, in a top-level handler, which reports the error to the
user, logs it, or whatever. It is relatively rare for well-designed code to
need to handle the exception closer to the throw-point.

--
Doug Harrison
Visual C++ MVP
From: David Ching on
"Doug Harrison [MVP]" <dsh(a)mvps.org> wrote in message
news:9c1226ldk8g207f4asif27nehrcfji6g0e(a)4ax.com...
> On Tue, 22 Jun 2010 13:12:33 -0400, Joseph M. Newcomer
> <newcomer(a)flounder.com> wrote:
>
>>I'm not sure this is a good piece of advice. I use try/catch a lot; it is
>>essentially a
>>"non-local GOTO", a structured way of aborting execution and returning to
>>a known place,
>>while still guaranteeing that all intermediate destructors for stack
>>variables are called.
>>It is particularly useful in writing tasks like recursive-descent parsers
>>(particularly if
>>you just want to stop without trying to do error recovery, which is always
>>hard) and
>>terminating threads while still guaranteeing that you return from the
>>top-level thread
>>function. It is clean and well-structured way of aborting a
>>partially-completed
>>operation. An it is the only way to report errors from operations like
>>'new'. Also, look
>>at the number of exceptions that can be thrown by std:: or boost::.
>
> Goran was not saying exceptions are bad, just that overly frequent use of
> try/catch is bad, which it usually is. I've been saying for a long long
> time that there's an inverse relationship between the number of try/catch
> clauses you have and the effectiveness with which you're using exceptions.
> There are a number of reasons for this. On the API designer side, using
> exceptions where return codes are more appropriate forces callers to write
> try/catch whenever they use the function, so that's a bad use of
> exceptions. You never want to turn exception usage into a clumsier version
> of return codes. On the user side, try/catch gets overused when people
> don't employ the RAII idiom and need to perform clean-up that should be
> handled by a destructor. Ideally, exceptions are caught far away from
> where
> they're thrown, in a top-level handler, which reports the error to the
> user, logs it, or whatever. It is relatively rare for well-designed code
> to
> need to handle the exception closer to the throw-point.
>

Not to mention, the overhead of throwing exceptions reduces performance if
many exceptions are thrown. Exceptions are not meant as a "non-local GOTO".
Exceptions are meant for rarely occurring ERROR conditions (you know,
'exceptional' conditions!), not normal control flow. I once saw a switch
statement rewritten as a bunch of thrown exceptions, not a pretty sight.

-- David


From: Joseph M. Newcomer on
See below...
On Tue, 22 Jun 2010 14:14:11 -0500, "Doug Harrison [MVP]" <dsh(a)mvps.org> wrote:

>On Tue, 22 Jun 2010 13:12:33 -0400, Joseph M. Newcomer
><newcomer(a)flounder.com> wrote:
>
>>>P.S. Don't forget: you are not allowed to write try/catch
>>>statements ;-). If you do, you have 99% chance that you're doing it
>>>wrong. Good C++ code of today has RIDICULOUSLY small number of try/
>>>catch-es in ti.
>>****
>>I'm not sure this is a good piece of advice. I use try/catch a lot; it is essentially a
>>"non-local GOTO", a structured way of aborting execution and returning to a known place,
>>while still guaranteeing that all intermediate destructors for stack variables are called.
>>It is particularly useful in writing tasks like recursive-descent parsers (particularly if
>>you just want to stop without trying to do error recovery, which is always hard) and
>>terminating threads while still guaranteeing that you return from the top-level thread
>>function. It is clean and well-structured way of aborting a partially-completed
>>operation. An it is the only way to report errors from operations like 'new'. Also, look
>>at the number of exceptions that can be thrown by std:: or boost::.
>
>Goran was not saying exceptions are bad, just that overly frequent use of
>try/catch is bad, which it usually is. I've been saying for a long long
>time that there's an inverse relationship between the number of try/catch
>clauses you have and the effectiveness with which you're using exceptions.
>There are a number of reasons for this. On the API designer side, using
>exceptions where return codes are more appropriate forces callers to write
>try/catch whenever they use the function, so that's a bad use of
>exceptions.
****
I once wrote code for an OS that threw an exception when an API failed. The result was
one try/catch block per API call, and the code was a nightmare. Building robust code in
that environment was amazingly challenging, and I consider it one of hte worst designs
I've ever had the misfortune to have to interact with.
****
>You never want to turn exception usage into a clumsier version
>of return codes.
****
Key here is how far up you need to get control. Return codes are often clumsy in this
regard, and so a lot of it has to do with the need to cleanly return to a very high point
in the call stack. The complement of have APIs throw exceptions is having the need to
"return FALSE" everywhere, but that turns out to be "return NULL" sometimes and "return 0"
other times, or "return -1" (e.g., when a count is expected). These have the additional
problem of losing the reason for the failure, so by the time it gets to the necessary
level the only information you have is "something failed down there" and you don't know
what or where, or necessarily even how to recover from it. Exceptions can provide a rich
set of information on the nature of the failure, and return it intact to abribrary levels
up the call hiearchy, and that is a LOT of power!
****
>On the user side, try/catch gets overused when people
>don't employ the RAII idiom and need to perform clean-up that should be
>handled by a destructor.
*****
Part of the problem is that not everything is encapsulable in RAII. For example, if I
should return a pointer to an object, the object must have a lifetime that exceeds scope.
But if there is a failure, I should free the storage up. RAII doesn't handle this
gracefully. Other examples include "either I will return a handle or no handle will be
allocated" but you have to allocate the handle to make progress. RAII is powerful,
partcularly with smart pointers, but it is always based on the premise that leaving scope
means deletion of whatever resources are consumed, and that is not always feasible.
****
>Ideally, exceptions are caught far away from where
>they're thrown, in a top-level handler, which reports the error to the
>user, logs it, or whatever. It is relatively rare for well-designed code to
>need to handle the exception closer to the throw-point.
****
I agree. If-statements work very well for this. I only use it to terminate threads
cleanly and abort deep recursions all the way back up to the top level.
joe
****
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: RB on
Well I'm glad that my thread is getting a lot of input, but
most of the talk on this section is loosing me at my level.
I'm getting confused as to whether I should "try and catch"
on not "try and catch". If you would, could you please
comment on my code with question comments below.
It is not that long and you can just say whatever brief
or elongation review you have time for.
---------------
Additionally I have this logic going so far.
// in the read loop of MyDoc class serialize
else
{
try
{
ar >> FID_Read;; // DWORD FID_Read;
if (FID_Read != FileID) // const DWORD FileID;
{ // FileID mismatch
throw new CWrongFileIDException();
}
ar >> VerData.Ver >> VerData.CpyRt >> VerData.Corp;
ar.SerializeClass(RUNTIME_CLASS(CMapStringToString));
ExpMap1.Serialize(ar);
}
catch(CWrongFileIDException* e)
{
// Do whatever I may need here
// so far nothing that I can tell, in fact it seems I could have just
// eliminated my try and catch handlers and just called the
// AfxThrowArchiveException in the if loop above ?
e->Delete( );
AfxThrowArchiveException(CArchiveException::badIndex, NULL ); //Invalid file format
// The above takes me to the exact same cleanup code that I get when
// I read in a corrupt file (with NO exception code at all ) and mfc handlers it.
// And leaves me with an untitled filename eliminating the change if inadvertant
// save overwrite.
}
}

From: Joseph M. Newcomer on
To maximize performance, the Microsoft implementation was designed to create exception
frames extremely quickly (the try blocks) and it was (I believe rightly) felt that any
costs required to actually handle the exceptions could be paid at the time the exception
was thrown. I think this is a good engineering tradeoff. So yes, throwing an exception
is a pretty heavy-duty operation. But when an error occurs, you are going to be reduced
to working in "people time" (that is, someone is going to have to read the error message
and respond to it, integer seconds if not integer tens of seconds) so expending a few
hundred microseconds (remember, on a 3GHz machine, we can execute as many as 6
instructions per nanosecond (and that's on the OLD Pentium machines, not the Core
architectures which can do more), so a microsecond is a *lot* of instructions. (For those
of you who don't believe the arithmetic, look up "superscalar architecture").

So having exceptions as a basic loop control structure is going to be a Really Bad Idea.
But frankly, understanding such "spaghetti" code is extremely difficult, and trust me, the
worst assembler spaghetti code is clear as crystal compared to some of the
exception-driven code I've had to plow through! Exceptions are just that: something went
wrong.
joe
On Tue, 22 Jun 2010 14:06:56 -0700, "David Ching" <dc(a)remove-this.dcsoft.com> wrote:

>
>"Doug Harrison [MVP]" <dsh(a)mvps.org> wrote in message
>news:9c1226ldk8g207f4asif27nehrcfji6g0e(a)4ax.com...
>> On Tue, 22 Jun 2010 13:12:33 -0400, Joseph M. Newcomer
>> <newcomer(a)flounder.com> wrote:
>>
>>>I'm not sure this is a good piece of advice. I use try/catch a lot; it is
>>>essentially a
>>>"non-local GOTO", a structured way of aborting execution and returning to
>>>a known place,
>>>while still guaranteeing that all intermediate destructors for stack
>>>variables are called.
>>>It is particularly useful in writing tasks like recursive-descent parsers
>>>(particularly if
>>>you just want to stop without trying to do error recovery, which is always
>>>hard) and
>>>terminating threads while still guaranteeing that you return from the
>>>top-level thread
>>>function. It is clean and well-structured way of aborting a
>>>partially-completed
>>>operation. An it is the only way to report errors from operations like
>>>'new'. Also, look
>>>at the number of exceptions that can be thrown by std:: or boost::.
>>
>> Goran was not saying exceptions are bad, just that overly frequent use of
>> try/catch is bad, which it usually is. I've been saying for a long long
>> time that there's an inverse relationship between the number of try/catch
>> clauses you have and the effectiveness with which you're using exceptions.
>> There are a number of reasons for this. On the API designer side, using
>> exceptions where return codes are more appropriate forces callers to write
>> try/catch whenever they use the function, so that's a bad use of
>> exceptions. You never want to turn exception usage into a clumsier version
>> of return codes. On the user side, try/catch gets overused when people
>> don't employ the RAII idiom and need to perform clean-up that should be
>> handled by a destructor. Ideally, exceptions are caught far away from
>> where
>> they're thrown, in a top-level handler, which reports the error to the
>> user, logs it, or whatever. It is relatively rare for well-designed code
>> to
>> need to handle the exception closer to the throw-point.
>>
>
>Not to mention, the overhead of throwing exceptions reduces performance if
>many exceptions are thrown. Exceptions are not meant as a "non-local GOTO".
>Exceptions are meant for rarely occurring ERROR conditions (you know,
>'exceptional' conditions!), not normal control flow. I once saw a switch
>statement rewritten as a bunch of thrown exceptions, not a pretty sight.
>
>-- David
>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm