Prev: Deriving from a class derived from a CDialog
Next: How to show underlined menu shortcut letters by default for a contextmenu ?
From: Doug Harrison [MVP] on 22 Jun 2010 15:14 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 22 Jun 2010 17:06 "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 22 Jun 2010 17:27 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 22 Jun 2010 18:09 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 22 Jun 2010 18:11
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 |