Prev: Available C++ Libraries FAQ
Next: What's the summary of N2855 - Rvalue References and Exception Safety?
From: DeMarcus on 1 May 2010 21:52 Hi, In C++ Coding Standards by Sutter & Alexandrescu, Item 32, they say in short; "An exception class has no-fail constructors, especially a no-fail copy constructor". I have some thoughts regarding this. 1. An exception class doesn't /need/ to have no-fail constructor, right? It's just that the following throw SomeException(); might actually throw std::bad_alloc instead if the constructor tried to allocate something and the memory is out. Correct me if I'm wrong, but allowing the constructor to throw will not introduce inconsistency or undefined behavior. It will just uncontrollable change the attention to std::bad_alloc that took precedence, so the biggest problem is that the original exception won't be logged. Or are there any other implications by throwing from the exception constructor? 2. I understand that an exception class must have a no-fail copy constructor if I catch by value, but why do I need to have a copy constructor at all if I catch by reference? 3. Will C++0x be able to utilize the move semantics to avoid all copy construction of exceptions? Thanks, Daniel -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Daniel Krügler on 2 May 2010 01:26 On 2 Mai, 14:52, DeMarcus <use_my_alias_h...(a)hotmail.com> wrote: > Hi, > > In C++ Coding Standards by Sutter & Alexandrescu, Item 32, they say in short; "An exception class has no-fail constructors, especially a no-fail copy constructor". > > I have some thoughts regarding this. > > 1. An exception class doesn't /need/ to have no-fail constructor, right? It's just that the following > > throw SomeException(); > > might actually throw std::bad_alloc instead if the constructor tried to allocate something and the memory is out. > > Correct me if I'm wrong, but allowing the constructor to throw will not introduce inconsistency or undefined behavior. It will just uncontrollable change the attention to std::bad_alloc that took precedence, so the biggest problem is that the original exception won't be logged. Or are there any other implications by throwing from the exception constructor? > This question is not an easy one. The crux of the matter is the current wording of [except.terminate]/1 b. 1: "In the following situations exception handling must be abandoned for less subtle error handling techniques: � when the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught (15.1), calls a function that exits via an uncaught exception, (footnote 143)" with footnote 143: "For example, if the object being thrown is of a class with a copy constructor, std::terminate() will be called if that copy constructor exits with an exception during a throw." So there exists some reason to assume that an exception thrown during the evaluation of the throw expression shall invoke terminate. But this conclusion depends indirectly on the question when the function std::uncaught_exception() evaluates to true and there exists a lengthy issue about this problem: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#475 This discussion shows that the wording needs to be clearer and that current implementations vary. As of the currently proposed resolution it looks like as if the wanted outcome should be that your example would not invoke terminate, but instead would throw the exception of the copy constructor. According to this wording, there would still be chance to get std::terminate involved, but this could only happen, if the programmer defines an exception handler "by-value": void test() { try { throw SomeException(); // #1 } catch (SomeException) { // #2 } } int main() { try { test(); } catch(...) { } } Assume that the copy constructor or default constructor involved in #1 does *not* throw an exception, but the copy constructor involved in #2 does, this will still lead to an invokation of std::terminate according to the current P/R of #475. IMO this is acceptable, because most style guide's I'm aware of already require that exceptions should be caught by reference, so this should be not a typical problem. > 2. I understand that an exception class must have a no-fail copy constructor if I catch by value, but why do I need to have a copy constructor at all if I catch by reference? > Because the runtime needs to create the so-called exception object, see [except.throw]/3+4, which is a copy of the initial-exception. This special exception object cannot be an automatic object, because it's life-time will need to exceed the current scope. It is explicitly allowed for an implementation to elide the copy step in your example, but that is not a requirement, see [except.throw]/5: "When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible, even if the copy/move operation is elided (12.8)." > 3. Will C++0x be able to utilize the move semantics to > avoid all copy construction of exceptions? Yes, an implementation is allowed to call the move constructor, if such exists. This is one of the special cases listed in [class.copy]/34 bullet 2: "� in a throw-expression, when the operand is the name of a non-volatile automatic object whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object" But the currently considered P/R of 475 would also ensure this for the general case. HTH & Greetings from Bremen, Daniel Kr�gler -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: DeMarcus on 3 May 2010 16:41 Daniel Kr�gler wrote: > On 2 Mai, 14:52, DeMarcus <use_my_alias_h...(a)hotmail.com> wrote: >> Hi, >> >> In C++ Coding Standards by Sutter & Alexandrescu, Item 32, they say in short; "An exception class has no-fail constructors, > especially a no-fail copy constructor". >> I have some thoughts regarding this. >> >> 1. An exception class doesn't /need/ to have no-fail constructor, right? It's just that the following >> >> throw SomeException(); >> >> might actually throw std::bad_alloc instead if the constructor tried to allocate something and the memory is out. >> >> Correct me if I'm wrong, but allowing the constructor to throw will not introduce inconsistency or undefined behavior. It will > just uncontrollable change the attention to std::bad_alloc that took precedence, so the biggest problem is that the original > exception won't be logged. Or are there any other implications by throwing from the exception constructor? > > This question is not an easy one. The crux of the matter is > the current wording of [except.terminate]/1 b. 1: > > "In the following situations exception handling must be > abandoned for less subtle error handling techniques: > � when the exception handling mechanism, after completing > evaluation of the expression to be thrown but before the > exception is caught (15.1), calls a function that exits > via an uncaught exception, (footnote 143)" > > with footnote 143: > > "For example, if the object being thrown is of a class > with a copy constructor, std::terminate() will be called > if that copy constructor exits with an exception during > a throw." > > So there exists some reason to assume that an exception > thrown during the evaluation of the throw expression > shall invoke terminate. > > But this conclusion depends indirectly on the question > when the function std::uncaught_exception() evaluates to > true and there exists a lengthy issue about this problem: > > http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#475 > > This discussion shows that the wording needs to be clearer > and that current implementations vary. As of the currently > proposed resolution it looks like as if the wanted outcome > should be that your example would not invoke terminate, > but instead would throw the exception of the copy constructor. > > According to this wording, there would still be chance to > get std::terminate involved, but this could only happen, > if the programmer defines an exception handler "by-value": > > void test() { > try { > throw SomeException(); // #1 > } catch (SomeException) { // #2 > } > } > > int main() { > try { > test(); > } catch(...) { > } > } > > Assume that the copy constructor or default constructor > involved in #1 does *not* throw an exception, but the > copy constructor involved in #2 does, this will still > lead to an invokation of std::terminate according to > the current P/R of #475. IMO this is acceptable, because > most style guide's I'm aware of already require that > exceptions should be caught by reference, so this > should be not a typical problem. > Thanks for your thorough explanation! So, as I understand it, with C++0x, as long as I catch by reference I can throw as much exceptions I want from an exception object without risking a std::terminate? (with the latest exception thrown taking precedence until caught) -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: DeMarcus on 4 May 2010 10:34 DeMarcus wrote: > Daniel Kr�gler wrote: >> On 2 Mai, 14:52, DeMarcus <use_my_alias_h...(a)hotmail.com> wrote: >>> Hi, >>> >>> In C++ Coding Standards by Sutter & Alexandrescu, Item 32, they say >>> in short; "An exception class has no-fail constructors, >> especially a no-fail copy constructor". >>> I have some thoughts regarding this. >>> >>> 1. An exception class doesn't /need/ to have no-fail constructor, >>> right? It's just that the following >>> >>> throw SomeException(); >>> >>> might actually throw std::bad_alloc instead if the constructor tried >>> to allocate something and the memory is out. >>> >>> Correct me if I'm wrong, but allowing the constructor to throw will >>> not introduce inconsistency or undefined behavior. It will >> just uncontrollable change the attention to std::bad_alloc that took >> precedence, so the biggest problem is that the original >> exception won't be logged. Or are there any other implications by >> throwing from the exception constructor? >> >> This question is not an easy one. The crux of the matter is >> the current wording of [except.terminate]/1 b. 1: >> >> "In the following situations exception handling must be >> abandoned for less subtle error handling techniques: >> � when the exception handling mechanism, after completing >> evaluation of the expression to be thrown but before the >> exception is caught (15.1), calls a function that exits >> via an uncaught exception, (footnote 143)" >> >> with footnote 143: >> >> "For example, if the object being thrown is of a class >> with a copy constructor, std::terminate() will be called >> if that copy constructor exits with an exception during >> a throw." >> >> So there exists some reason to assume that an exception >> thrown during the evaluation of the throw expression >> shall invoke terminate. >> >> But this conclusion depends indirectly on the question >> when the function std::uncaught_exception() evaluates to >> true and there exists a lengthy issue about this problem: >> >> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#475 >> >> This discussion shows that the wording needs to be clearer >> and that current implementations vary. As of the currently >> proposed resolution it looks like as if the wanted outcome >> should be that your example would not invoke terminate, >> but instead would throw the exception of the copy constructor. >> >> According to this wording, there would still be chance to >> get std::terminate involved, but this could only happen, >> if the programmer defines an exception handler "by-value": >> >> void test() { >> try { >> throw SomeException(); // #1 >> } catch (SomeException) { // #2 >> } >> } >> >> int main() { >> try { >> test(); >> } catch(...) { >> } >> } >> >> Assume that the copy constructor or default constructor >> involved in #1 does *not* throw an exception, but the >> copy constructor involved in #2 does, this will still >> lead to an invokation of std::terminate according to >> the current P/R of #475. IMO this is acceptable, because >> most style guide's I'm aware of already require that >> exceptions should be caught by reference, so this >> should be not a typical problem. >> > > Thanks for your thorough explanation! > > So, as I understand it, with C++0x, as long as I catch by reference I > can throw as much exceptions I want from an exception object without > risking a std::terminate? (with the latest exception thrown taking > precedence until caught) > Additionally, my point is that such behavior is quite important. The exception constructor has to have the possibility to throw. We have three scenarios down the scopes when an exception has been thrown. * Propagate the exception, i.e. no catch. * Handle or swallow the exception, i.e. catch. * Translate the exception, i.e. catch and (re)throw. If we start bottom up, the translation of an exception may be non-trivial where some implementations add more information to the new exception to be thrown, and some implementations may log the error (where the logger may throw). I claim that it's impossible to demand an exception translation be nothrow. try { doSomething(); } catch( const SomeException& ex ) { // -- Translation of exception -- // Maybe some logging... // Maybe some adding of information... throw AnotherException( moreInfo ); } If we agree a translation may throw, we may as well accept an exception constructor to throw as the constructor can be seen as part of a translation. If we agree a constructor may throw, it is natural that also the copy constructor should be able to throw. Since the copy constructor may be invoked unseen during an exception propagation we end up in following induction reasoning. If exception translation should be able to throw, i.e. any part within the catch clause, std::terminate must not be called if a new exception is thrown and caught by reference. Can we force (or even strongly recommend) exception translation be nothrow? Please give your thoughts on this. As a side discussion I would like to comment on following item in FAQ Lite. In [17.3] they discuss how to handle a destructor that fails. Note! I'm not going to argue against that a destructor must never throw, but I want to comment on their discussion in connection to exception translation. http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.3 They say "During stack unwinding, all the local objects in all those stack frames are destructed. If one of those destructors throws an exception (say it throws a Bar object), the C++ runtime system is in a no-win situation: should it ignore the Bar and end up in the } catch (Foo e) { where it was originally headed? Should it ignore the Foo and look for a } catch (Bar e) { handler? There is no good answer � either choice loses information." Yes, either choice loses information, but terminating the application loses *all* information. During an exception translation I find it important that at least some exception comes out of it instead of a termination. I prefer to have a std::bad_alloc logged and a graceful shutdown than a crash without information, even though the original exception may have been something as important as 'configuration file corrupt'. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Martin B. on 5 May 2010 03:19 DeMarcus wrote: > ..... > > As a side discussion I would like to comment on following item in FAQ > Lite. In [17.3] they discuss how to handle a destructor that fails. > Note! I'm not going to argue against that a destructor must never throw, > but I want to comment on their discussion in connection to exception > translation. > > http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.3 > > They say > "During stack unwinding, all the local objects in all those stack frames > are destructed. If one of those destructors throws an exception (say it > throws a Bar object), the C++ runtime system is in a no-win situation: > should it ignore the Bar and end up in the } catch (Foo e) { where it > was originally headed? Should it ignore the Foo and look for a } catch > (Bar e) { handler? There is no good answer � either choice loses > information." > > Yes, either choice loses information, but terminating the application > loses *all* information. During an exception translation I find it > important that at least some exception comes out of it instead of a > termination. I prefer to have a std::bad_alloc logged and a graceful > shutdown than a crash without information, even though the original > exception may have been something as important as 'configuration file > corrupt'. > Personally, I have found that the default bail-out mechanism of C++, namely to call terminate() -> abort() is totally insufficient - at least on Windows. However, we have set_terminate() and/or can react to SIGABRT and can basically do whatever we like. When we receive a SIGABRT in our app, we generate a dump file and then shut down. At least that way we can analyse where the abort() came from. (As opposed to catching, say, bad_alloc, as that will only tell me where I caught the exception, not where it originated.) cheers, Martin -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
|
Next
|
Last
Pages: 1 2 Prev: Available C++ Libraries FAQ Next: What's the summary of N2855 - Rvalue References and Exception Safety? |