Discussion:
Nested exceptions
(too old to reply)
Shachar Shemesh
2015-04-20 03:20:22 UTC
Permalink
Hi all,

This, I think, is the most sore point in C++ today. Exception nesting.

Current standard mandates that an exception thrown during stack unwind
for another exception causes immediate program termination. This has led
to many suggestions that destructors never throw. I find that suggestion
both problematic and too onerous.

It is problematic because in 99% of the cases, it is quite alright for
destructors to throw. In other words, there are two types of C++
programmers. Those who never heard of the problem, those who think it is
illegal for a destructor to throw, and those... three types of C++
programmers.

This restriction is also too onerous. If an operation that needs to be
done during destruction fails, the programmer is left with zero options
as to what to do about it. There is, in fact, no way for the programmer
to report this failure, or to act about it. The only advice I offer
people (and follow it myself) is: disregard the threat, throw from the
destructor. This effectively places me in the same group as the most
ignorant of the two three.

In fact, this restriction is so onerous, that I have seen code where the
destructor is declared as "nothrow", but also throws. This is obvious
mistake was done by someone aware of the problem, and yet, when push
comes to shove, he needed to report the error somehow.

What I suggest:
The simplest solution to this problem is to simply ignore the new
exception. There are zero cases where the programmer is worse off than
she is today. In all likelihood, the second exception is a byproduct of
the first one anyways.

An alternative solution, but a more complicated one, is to employ
excption chaining, such as the one used by D. This has several
challenges to implement. For one thing, the exception needs to be
chained to something. As long as C++ allows throwing just any ol' thing,
that simply cannot be done. I think a reasonable compromise on that
front is to add a chain pointer to std::exception, and chain the new
exception only if the types line up (i.e. - if both end of chain object
and newly thrown object derive from std::exception).

An even more generic solution (not sure that's a good thing, mind you)
is to define some interface, and allow chaining if the static types line
up. I.e. - if the existing end of chain object has a function called
"chain_exception" that is capable of accepting a pointer to the object
currently being thrown. I'm not confident I understand the exception
mechanism well enough, but I suspect this would incur a run time cost
that is, probably, not worth the trouble.

Personally, I'm perfectly happy with option 1: discard the new
exception. It is simple, easy to implement, covers most of the cases and
has zero down sides compared to the situation as it is today.

All of the above does not refer to errors thrown during construction and
destruction of the exception object itself. Personally, I'm perfectly
fine with leaving the situation there as it is today. It is perfectly
valid, in my eyes, to require greater care from programmers designing
exception objects than from programmers merely using them.

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-04-22 18:23:24 UTC
Permalink
Post by Shachar Shemesh
The simplest solution to this problem is to simply ignore the new
exception. There are zero cases where the programmer is worse off than
she is today. In all likelihood, the second exception is a byproduct of
the first one anyways.
That's probably a much worse scenario.

Imagine the following code:

try {
someCode();
someCode2();
} catch (...) {
...
}
someOtherCode();

if someCode() throws, where would you expect execution to continue?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-23 19:11:42 UTC
Permalink
Post by Thiago Macieira
That's probably a much worse scenario.
try {
someCode();
someCode2();
} catch (...) {
...
}
someOtherCode();
if someCode() throws, where would you expect execution to continue?
Same as today. There is nothing here that is relevant to my suggestion.

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Thiago Macieira
2015-04-24 16:11:12 UTC
Permalink
Post by Shachar Shemesh
Post by Thiago Macieira
That's probably a much worse scenario.
try {
someCode();
someCode2();
} catch (...) {
...
}
someOtherCode();
if someCode() throws, where would you expect execution to continue?
Same as today. There is nothing here that is relevant to my suggestion.
If it is the same as today, then the exception isn't ignored. That is what you
had suggested.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Johannes Schaub
2015-04-22 18:31:20 UTC
Permalink
Post by Shachar Shemesh
Hi all,
This, I think, is the most sore point in C++ today. Exception nesting.
Current standard mandates that an exception thrown during stack unwind for
another exception causes immediate program termination. This has led to many
suggestions that destructors never throw. I find that suggestion both
problematic and too onerous.
It is problematic because in 99% of the cases, it is quite alright for
destructors to throw. In other words, there are two types of C++
programmers. Those who never heard of the problem, those who think it is
illegal for a destructor to throw, and those... three types of C++
programmers.
This restriction is also too onerous. If an operation that needs to be done
during destruction fails, the programmer is left with zero options as to
what to do about it. There is, in fact, no way for the programmer to report
this failure, or to act about it. The only advice I offer people (and follow
it myself) is: disregard the threat, throw from the destructor. This
effectively places me in the same group as the most ignorant of the two
three.
In fact, this restriction is so onerous, that I have seen code where the
destructor is declared as "nothrow", but also throws. This is obvious
mistake was done by someone aware of the problem, and yet, when push comes
to shove, he needed to report the error somehow.
The simplest solution to this problem is to simply ignore the new exception.
There are zero cases where the programmer is worse off than she is today. In
all likelihood, the second exception is a byproduct of the first one
anyways.
An alternative solution, but a more complicated one, is to employ excption
chaining, such as the one used by D. This has several challenges to
implement. For one thing, the exception needs to be chained to something. As
long as C++ allows throwing just any ol' thing, that simply cannot be done.
I think a reasonable compromise on that front is to add a chain pointer to
std::exception, and chain the new exception only if the types line up (i.e.
- if both end of chain object and newly thrown object derive from
std::exception).
An even more generic solution (not sure that's a good thing, mind you) is to
define some interface, and allow chaining if the static types line up. I.e.
- if the existing end of chain object has a function called
"chain_exception" that is capable of accepting a pointer to the object
currently being thrown. I'm not confident I understand the exception
mechanism well enough, but I suspect this would incur a run time cost that
is, probably, not worth the trouble.
Personally, I'm perfectly happy with option 1: discard the new exception. It
is simple, easy to implement, covers most of the cases and has zero down
sides compared to the situation as it is today.
All of the above does not refer to errors thrown during construction and
destruction of the exception object itself. Personally, I'm perfectly fine
with leaving the situation there as it is today. It is perfectly valid, in
my eyes, to require greater care from programmers designing exception
objects than from programmers merely using them.
A question: Have you considered that this also changes the meaning of
programs that don't terminate at all?

#include <iostream>

struct Hello {
~Hello() {
try {
throw "Hello";
}
catch(const char *x) { std::cout << x; }
}
};

int main() {
try {
Hello hello;
throw "World";
}
catch(const char *y) { std::cout << y; }
}

This program prints HelloWorld, and you are going to make this print
Hello, if I understand you correctly.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-23 19:13:20 UTC
Permalink
Post by Johannes Schaub
A question: Have you considered that this also changes the meaning of
programs that don't terminate at all?
My proposal relates on the to the case where C++ today calls terminate.
Post by Johannes Schaub
#include <iostream>
struct Hello {
~Hello() {
try {
throw "Hello";
}
catch(const char *x) { std::cout << x; }
}
};
int main() {
try {
Hello hello;
throw "World";
}
catch(const char *y) { std::cout << y; }
}
This program prints HelloWorld, and you are going to make this print
Hello, if I understand you correctly.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Brian Bi
2015-04-22 18:33:58 UTC
Permalink
Can't you implement option 1 yourself? If you really think it is sensible
to have the second exception ignored if it occurs during stack unwinding,
then something like this (there are probably edge cases I am missing...)
should work:

Foo::~Foo() try {
// body of destructor
} catch (...) {
if (std::uncaught_exceptions() > 0) {
// log the exception maybe
return; // swallow the exception
} else {
throw; // actually, this can be omitted; the exception will be
automatically rethrown
}
}
Post by Shachar Shemesh
Hi all,
This, I think, is the most sore point in C++ today. Exception nesting.
Current standard mandates that an exception thrown during stack unwind for
another exception causes immediate program termination. This has led to
many suggestions that destructors never throw. I find that suggestion both
problematic and too onerous.
It is problematic because in 99% of the cases, it is quite alright for
destructors to throw. In other words, there are two types of C++
programmers. Those who never heard of the problem, those who think it is
illegal for a destructor to throw, and those... three types of C++
programmers.
This restriction is also too onerous. If an operation that needs to be
done during destruction fails, the programmer is left with zero options as
to what to do about it. There is, in fact, no way for the programmer to
report this failure, or to act about it. The only advice I offer people
(and follow it myself) is: disregard the threat, throw from the destructor.
This effectively places me in the same group as the most ignorant of the
two three.
In fact, this restriction is so onerous, that I have seen code where the
destructor is declared as "nothrow", but also throws. This is obvious
mistake was done by someone aware of the problem, and yet, when push comes
to shove, he needed to report the error somehow.
The simplest solution to this problem is to simply ignore the new
exception. There are zero cases where the programmer is worse off than she
is today. In all likelihood, the second exception is a byproduct of the
first one anyways.
An alternative solution, but a more complicated one, is to employ excption
chaining, such as the one used by D. This has several challenges to
implement. For one thing, the exception needs to be chained to something.
As long as C++ allows throwing just any ol' thing, that simply cannot be
done. I think a reasonable compromise on that front is to add a chain
pointer to std::exception, and chain the new exception only if the types
line up (i.e. - if both end of chain object and newly thrown object derive
from std::exception).
An even more generic solution (not sure that's a good thing, mind you) is
to define some interface, and allow chaining if the static types line up.
I.e. - if the existing end of chain object has a function called
"chain_exception" that is capable of accepting a pointer to the object
currently being thrown. I'm not confident I understand the exception
mechanism well enough, but I suspect this would incur a run time cost that
is, probably, not worth the trouble.
Personally, I'm perfectly happy with option 1: discard the new exception.
It is simple, easy to implement, covers most of the cases and has zero down
sides compared to the situation as it is today.
All of the above does not refer to errors thrown during construction and
destruction of the exception object itself. Personally, I'm perfectly fine
with leaving the situation there as it is today. It is perfectly valid, in
my eyes, to require greater care from programmers designing exception
objects than from programmers merely using them.
Shachar
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
*Brian Bi*
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-23 19:28:55 UTC
Permalink
It is definitely an improvement, but still rather onerous. Please bear
in mind that it only makes sense to implement this in a destructor if
that destructor might throw. On the other hand, if the destructor calls
any other function, that function might throw as well. You end up
needing this construct for pretty much each and every destructor,
including destructors for most classes you use from libraries.

This is error prone and dangerous.
Post by Brian Bi
Can't you implement option 1 yourself? If you really think it is
sensible to have the second exception ignored if it occurs during
stack unwinding, then something like this (there are probably edge
Foo::~Foo() try {
// body of destructor
} catch (...) {
if (std::uncaught_exceptions() > 0) {
// log the exception maybe
return; // swallow the exception
} else {
throw; // actually, this can be omitted; the exception
will be automatically rethrown
}
}
Here's a nasty edge case. See if you can figure it out without the
explanation:

Foo::~Foo() {
try {
while(something) {
Bar b; // No relation to Mattel Inc.
}
} catch(...) // The rest of the catch as as with your code
}

Here's the problem. On the second iteration of the while, b's destructor
wanted to throw. Because it has the same code as us, however, it did not
throw anything. As a result, this perfectly safe throw (we would have
caught and absorbed it) did not happen, and the while loop ran more
times than it should. Your chances to reconstruct this problem with a
debugger are, not to be to specific, zero.

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
T. C.
2015-04-24 06:42:47 UTC
Permalink
Post by Brian Bi
Can't you implement option 1 yourself? If you really think it is sensible
to have the second exception ignored if it occurs during stack unwinding,
then something like this (there are probably edge cases I am missing...)
Foo::~Foo() try {
// body of destructor
} catch (...) {
if (std::uncaught_exceptions() > 0) {
// log the exception maybe
return; // swallow the exception
} else {
throw; // actually, this can be omitted; the exception will be
automatically rethrown
}
}
Here's a nasty edge case. See if you can figure it out without the
Foo::~Foo() {
try {
while(something) {
Bar b; // No relation to Mattel Inc.
}
} catch(...) // The rest of the catch as as with your code
}
Here's the problem. On the second iteration of the while, b's destructor
wanted to throw. Because it has the same code as us, however, it did not
throw anything. As a result, this perfectly safe throw (we would have
caught and absorbed it) did not happen, and the while loop ran more times
than it should. Your chances to reconstruct this problem with a debugger
are, not to be to specific, zero.
Shachar
This is exactly why uncaught_exceptions() was added, and
uncaught_exception() deprecated.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-24 10:20:31 UTC
Permalink
Post by T. C.
This is exactly why uncaught_exceptions() was added, and
uncaught_exception() deprecated.
Except I fail to see how it makes things any better.

Can you elaborate using code?

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
T. C.
2015-04-24 15:24:07 UTC
Permalink
Post by T. C.
This is exactly why uncaught_exceptions() was added, and
uncaught_exception() deprecated.
Except I fail to see how it makes things any better.
Can you elaborate using code?
Shachar
To quote http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf:

A type that wants to know whether its destructor is being run to unwind
Post by T. C.
this object can query uncaught_exceptions in its constructor and store the
result, then query uncaught_exceptions again in its destructor; if the
result is different, then this destructor is being invoked as part of stack
unwinding due to a new exception that was thrown later than the object’s
construction.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-24 15:39:20 UTC
Permalink
Post by Shachar Shemesh
Post by T. C.
This is exactly why uncaught_exceptions() was added, and
uncaught_exception() deprecated.
Except I fail to see how it makes things any better.
Can you elaborate using code?
Shachar
To quote
A type that wants to know whether its destructor is being run to
unwind this object can query uncaught_exceptions in its
constructor and store the result, then query uncaught_exceptions
again in its destructor; if the result is different, then this
destructor is being invoked as part of stack unwinding due to a
new exception that was thrown later than the object’s construction.
I'll point out that I believe there are still some use cases where this
isn't good enough. That's not my main objection, however.

What I'm failing to see is how is this better than what I'm suggesting?
This means that a whole bunch of classes now need to store an extra int
for a use case that will hardly ever come to pass. This, to me, seems
simply wrong.

The whole point of exceptions is that you don't pay for them in the good
path. That's the principle that caused cfont to be abandoned. It's the
reason the compiler often produces two versions of the same code when
exceptions are employed. It is one of the main founding principles of
exception handling.

This principle is clearly violated here. I'm paying both in code and in
storage for a feature that I will need only in the bad path, and worse,
only in rare cases in the bad path.

That seems simply wrong to me.

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2015-04-23 02:00:28 UTC
Permalink
The simplest solution to this problem is to simply ignore the new exception. There are zero cases where the programmer is worse off than she is today.
There are cases where terminating the program is better than continuing it with invalid inputs or broken preconditions.
In all likelihood, the second exception is a byproduct of the first one anyways.
Regardless, the program isn’t going to handle both if the first one hasn’t been caught when the second is thrown.
An alternative solution, but a more complicated one, is to employ excption chaining, such as the one used by D. This has several challenges to implement. For one thing, the exception needs to be chained to something.
std::throw_with_nested and std::nested_exception (since C++11) do this with generic inheritance and polymorphism.
As long as C++ allows throwing just any ol' thing, that simply cannot be done. I think a reasonable compromise on that front is to add a chain pointer to std::exception, and chain the new exception only if the types line up (i.e. - if both end of chain object and newly thrown object derive from std::exception).
The existing solution is even better: No core language or ABI modification, and it works with all class types. No need for conformance to a generic interface, either.
An even more generic solution (not sure that's a good thing, mind you) is to define some interface, and allow chaining if the static types line up. I.e. - if the existing end of chain object has a function called "chain_exception" that is capable of accepting a pointer to the object currently being thrown.
This can be achieved using nested_exception::rethrow_nested with a catch clause matching the expected static type.

C++ could make nested exception generation the default, but it would tend to hide the nested exceptions. Until the user adds nested_exception handlers, it’s no better than ignoring and replacing the first exception with the second. That path wasn’t taken because it’s unsafe.

Correct code handles all exceptions as soon and unexceptionally as possible, and explicitly swallows all exceptions that will be safely ignored.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-23 19:09:48 UTC
Permalink
I'm sorry if this is a dup. I'm having some mail server problems.
Post by David Krauss
Post by Shachar Shemesh
The simplest solution to this problem is to simply ignore the new
exception. There are zero cases where the programmer is worse off
than she is today.
There are cases where terminating the program is better than
continuing it with invalid inputs or broken preconditions.
The question is the opposite, I think. Are there cases where terminating
the program makes little sense? I'd argue that with RAII properly
implemented, that would be the rule, rather than the exception.
Post by David Krauss
Post by Shachar Shemesh
In all likelihood, the second exception is a byproduct of the first one anyways.
Regardless, the program isn’t going to handle both if the first one
hasn’t been caught when the second is thrown.
Hasn't been caught *yet*.
Post by David Krauss
Post by Shachar Shemesh
An alternative solution, but a more complicated one, is to employ
excption chaining, such as the one used by D. This has several
challenges to implement. For one thing, the exception needs to be
chained to something.
std::throw_with_nested and std::nested_exception (since C++11) do this
with generic inheritance and polymorphism.
Both are unusable in the use case discussed above. They are meant to be
used from the catch clause of the first exception. I'm talking about an
exception that happens before the catch clause even started. Consider
the following program:

class A {
public:
~A() {
throw std::runtime_error("A failed");
}
};

int main()
{
try {
A a;

throw std::runtime_error("generated");
} catch(...) {
std::cout<<"Caught it\n";
}

return 0;
}

We never reach the catch clause, with either exceptions nor anything else.
Post by David Krauss
The existing solution is even better: No core language or ABI
modification, and it works with all class types. No need for
conformance to a generic interface, either.
I might agree, if I could, somehow, convert an in-flight exception.
Otherwise, std::nested_exception is just a distraction.
Post by David Krauss
C++ could make nested exception generation the default, but it would
tend to hide the nested exceptions. Until the user adds
nested_exception handlers, it’s no better than ignoring and replacing
the first exception with the second. That path wasn’t taken because
it’s unsafe.
All irrelevant to the scenario I'm describing.
Post by David Krauss
Correct code handles all exceptions as *soon* and unexceptionally as
possible, and explicitly swallows all exceptions that will be safely
ignored.
I have issues with the word "soon", especially when used the way you
just did, to imply concurrent exceptions mean bad programming. The WHOLE
point of exceptions is that they solve the unsolvable problem: how do
you get the error from the point where we know what the error is, to the
point where we know what we should do about it. Depending on how much
effort you need to throw away in case of exception, that may not be
"soon" at all.

Take an example, an HTTP while processing the body of a request, might
decide that the request is malicious. A perfectly reasonable programming
design (IMHO) is to then decide to just close the connection and clear
all of its resources. There is no better way to do that than to throw a
malicious_connection exception, and let RAII do all the rest. Such an
exception would be caught quite a few stack frames up the ladder,
probably at the very function that initiated the fiber.

Now, consider this. Through all of this unwinding, no destructor may
throw. Even if the socket close failed due to no buffers, or writing to
logs failed because of an unavailable host. No destructor may report any
error, because if they do, the entire program terminates. Please let me
know what, in your opinion, is incorrect about this design (except the
obvious it is incorrect to throw from a destructor, as that is precisely
what I am trying to change).

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2015-04-24 09:06:30 UTC
Permalink
Post by David Krauss
Regardless, the program isn’t going to handle both if the first one hasn’t been caught when the second is thrown.
Hasn't been caught yet.
When will it be caught? I don’t see where you’ve proposed to have two exceptions unwinding simultaneously.

You mentioned the chained exception facility in D. I’m not familiar with that language, and I couldn’t find anything online about throwing destructors or automatic chaining/nesting. Perhaps you can provide a reference.
Take an example, an HTTP while processing the body of a request, might decide that the request is malicious. A perfectly reasonable programming design (IMHO) is to then decide to just close the connection and clear all of its resources. There is no better way to do that than to throw a malicious_connection exception, and let RAII do all the rest. Such an exception would be caught quite a few stack frames up the ladder, probably at the very function that initiated the fiber.
Now, consider this. Through all of this unwinding, no destructor may throw. Even if the socket close failed due to no buffers, or writing to logs failed because of an unavailable host. No destructor may report any error, because if they do, the entire program terminates. Please let me know what, in your opinion, is incorrect about this design (except the obvious it is incorrect to throw from a destructor, as that is precisely what I am trying to change).
There are indeed rough edges around exceptions, but:

1. Malicious connections should be torn down ASAP, no need to do things gracefully. Most socket libraries provide a facility for this. Anyway, resources for graceful shutdown should be pre-allocated, and if they’re not, the connection must be abandoned as a fallback.

2. If logging can throw, and you do it directly from destructors, then you’re making a conscious decision that failure of the logging system may terminate the program.
2a. Otherwise, you may write logs to a local buffer and let a different thread or outer loop flush that to the logging host.
2b. Or, just swallow logging exceptions within destructors. This is the more popular choice. (The default behavior of iostreams goes one step further: badbit will disable logging completely until you acknowledge the problem.)

3. How do you suggest to actually handle simultaneous exceptions? You mentioned to “convert an in-flight exception” before it’s caught, but do you really want to replace the malicious_connection error with a bad_alloc or ios::failure, and then re-throw it from the context of the logging or memory exception handler? The order of handlers on the stack is dictated by high-level program architecture, and it’s not even clear which arrangement is correct.

Just being imaginative, one possibility is extend unwinding to work on a set of exceptions. When a handler is found matching any exception, remove it from the set. When the handler exits, normally or abnormally, resume unwinding with the remaining set plus any new exceptions being unwound. However, this seems like a theoretical solution in search of a problem.

A more sensible way to handle low-level exceptions is to have a protocol for offloading the exception-unsafe behavior from the destructor into an asynchronous process or coroutine. If a destructor is unfortunate enough to realize that a webpage can’t be served, don’t force it to try to synchronously serve an error page. Enqueue some minimal information so the rest of the server can deal with the situation “soon,” but perhaps not immediately.

As for the immediate recourse, C++17 will include a feature to allow destructors to decide whether or not to throw. When not throwing, you can record exceptions into std::exception_ptr objects, perhaps in thread-local storage. Inspect and re-throw those pointers as appropriate. Such technique should be more expressive and intuitive than built-in chaining could ever be.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-24 11:04:21 UTC
Permalink
Post by David Krauss
You mentioned the chained exception facility in D. I’m not familiar
with that language, and I couldn’t find anything online about throwing
destructors or automatic chaining/nesting. Perhaps you can provide a
reference.
Some good news and some bad. This is described quite nicely in section
9.5, titled "Collateral Exceptions" of "The D Programming Language".
Unfortunately, the book is not available online (at least, not for free).

In a nutshell:

* You can only throw classes that derive from Throwable.
* Thrownable has a field, "next", that can be queried by catch handler
* In any situation where, in C++, the program would terminate, in D
the new exception is added to the "next" field of the exception
currently last in the chain
* The catch can query the next header to scan the list and decide
whether they want to do something about the collateral exceptions or
not.
Post by David Krauss
1. Malicious connections should be torn down ASAP, no need to do
things gracefully.
Huh? This statement is so wrong on so many levels:

* ASAP is as soon as you can detect it is malicious. You cannot detect
malicious body until you've parsed the request, the headers, looked
up the resource etc. In other words, "soon" is a very relative term
in this case.
* No need to do things gracefully? Seriously? What about the thousands
of other connections this server is handling? Shouldn't they get a
chance to use the memory you've allocated for this connection?

May I remind you that exceptions are the only case where C++ potentially
does dynamic heap allocations without you explicitly asking for it?
Post by David Krauss
Anyway, resources for graceful shutdown should be pre-allocated, and
if they’re not, the connection must be abandoned as a fallback.
Abandoning the connection is precisely what I'm trying to do here.
Post by David Krauss
2. If logging can throw, and you do it directly from destructors, then
you’re making a conscious decision that failure of the logging system
may terminate the program.
You're begging the question
<https://en.wikipedia.org/wiki/Begging_the_question>. The only reason
you're equating the two is because that's how C++ does things. But that
is precisely what I'm urging to change. You could log, have log failures
throw and do it from a destructor without risking program termination if
my proposal is accepted. In fact, you've just validated the necessity.
Thank you :-)

2a and 2b are just workarounds to the problem C++ is dumping at my door.
They might be a good idea anyway (debatable), but my point is that C++
should not force me to use them.
Post by David Krauss
3. How do you suggest to actually handle simultaneous exceptions? You
mentioned to “convert an in-flight exception” before it’s caught, but
do you really want to replace the malicious_connection error with a
bad_alloc or ios::failure, and then re-throw it from the context of
the logging or memory exception handler? The order of handlers on the
stack is dictated by high-level program architecture, and it’s not
even clear which arrangement is correct.
The primary exception, i.e. - the one that triggered first, should be
the one the catch clauses should compare against. The rest should
piggyback on it, e.g., the D way, by means of a singly linked list.
Post by David Krauss
Just being imaginative, one possibility is extend unwinding to work on
a set of exceptions. When a handler is found matching any exception,
remove it from the set. When the handler exits, normally or
abnormally, resume unwinding with the remaining set plus any new
exceptions being unwound. However, this seems like a theoretical
solution in search of a problem.
A more sensible way to handle low-level exceptions is to have a
protocol for offloading the exception-unsafe behavior from the
destructor into an asynchronous process or coroutine. If a destructor
is unfortunate enough to realize that a webpage can’t be served, don’t
force it to try to synchronously serve an error page. Enqueue some
minimal information so the rest of the server can deal with the
situation “soon,” but perhaps not immediately.
Again, a workaround. Not a very practical one, too. Most high
performance web servers utilize micro-threading, which is not something
C++ is even aware of.

In particular, the code I have in mind when judging your suggested
alternatives is ghost, Akamai's proxy server. It drives 25% of the
Internet's web traffic, and I'm simply not seeing it doing so the way
you describe it should.
Post by David Krauss
As for the immediate recourse, C++17 will include a feature to allow
destructors to decide whether or not to throw. When not throwing, you
can record exceptions into std::exception_ptr objects, perhaps in
thread-local storage. Inspect and re-throw those pointers as
appropriate. Such technique should be more expressive and intuitive
than built-in chaining could ever be.
Maybe, but see my reply to Brian Bi, and later to T.C., for why what
C++17 isn't good enough (IMHO).

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-24 12:51:48 UTC
Permalink
Post by Shachar Shemesh
In particular, the code I have in mind when judging your suggested
alternatives is ghost, Akamai's proxy server. It drives 25% of the
Internet's web traffic, and I'm simply not seeing it doing so the way
you describe it should.
I'd like to clarify that the opinions presented by me, both in this
thread and elsewhere, are mine and mine alone. They represent neither
the opinions of my current employer nor those of Akamai, for whom I no
longer work.

Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2015-04-24 21:21:53 UTC
Permalink
In any situation where, in C++, the program would terminate, in D the new exception is added to the "next" field of the exception currently last in the chain
So the would-be offending exception doesn’t get thrown. This is effectively the same as having a thread-local list of exceptions that couldn’t be processed, which is within the scope of my exception_ptr suggestion.
The catch can query the next header to scan the list and decide whether they want to do something about the collateral exceptions or not.
And if it doesn’t, the exceptions never get handled. Not a good policy to bake into the core language.
Post by David Krauss
1. Malicious connections should be torn down ASAP, no need to do things gracefully.
ASAP is as soon as you can detect it is malicious. You cannot detect malicious body until you've parsed the request, the headers, looked up the resource etc. In other words, "soon" is a very relative term in this case.
No need to do things gracefully? Seriously? What about the thousands of other connections this server is handling? Shouldn't they get a chance to use the memory you've allocated for this connection?
I was only speaking in terms of sockets, once the problem is detected. You shouldn’t need to buffer any additional data to gracefully end a malicious user’s TCP connection, which is what you seemed to be referring to.
May I remind you that exceptions are the only case where C++ potentially does dynamic heap allocations without you explicitly asking for it?
Exception objects are usually allocated within a reserved arena. Heap allocation doesn’t sound like a conforming implementation of exceptions, at least not for bad_alloc.
Post by David Krauss
2. If logging can throw, and you do it directly from destructors, then you’re making a conscious decision that failure of the logging system may terminate the program.
You're begging the question <https://en.wikipedia.org/wiki/Begging_the_question>. The only reason you're equating the two is because that's how C++ does things.
When a destructor throws, not only does unwinding get a headache, but the object hasn’t been destroyed. A destructor that fails to free resources when the log facility is unavailable isn’t a very good one.

Exception-free destructors are the foundation of RTTI. C++ has good reason for doing things the way it does.
But that is precisely what I'm urging to change. You could log, have log failures throw and do it from a destructor without risking program termination if my proposal is accepted. In fact, you've just validated the necessity. Thank you :-)
You still haven’t described how any of these exceptions will actually get handled.
2a and 2b are just workarounds to the problem C++ is dumping at my door. They might be a good idea anyway (debatable), but my point is that C++ should not force me to use them.
Please provide a self-consistent outline of some alternative, including the restrictions on the malicious_connection handler. Is it allowed to attempt allocation or logging if it runs while there are already outstanding bad_alloc or ios::failure exceptions? Under what conditions does it re-throw the nested exception?
Post by David Krauss
A more sensible way to handle low-level exceptions is to have a protocol for offloading the exception-unsafe behavior from the destructor into an asynchronous process or coroutine. If a destructor is unfortunate enough to realize that a webpage can’t be served, don’t force it to try to synchronously serve an error page. Enqueue some minimal information so the rest of the server can deal with the situation “soon,” but perhaps not immediately.
Again, a workaround. Not a very practical one, too. Most high performance web servers utilize micro-threading, which is not something C++ is even aware of.
For best performance, don’t use exceptions at all. There’s currently an intensive study effort on high-performance coroutines, which will likely bear fruit after C++17.

This “workaround” doesn’t rely on, but rather avoids C++ exceptions, and it should be compatible with any threading system: preemptive, cooperative, or ad-hoc. It’s really only a recommendation to separate concerns and put major work items under the control of some kind of dispatcher.
In particular, the code I have in mind when judging your suggested alternatives is ghost, Akamai's proxy server. It drives 25% of the Internet's web traffic, and I'm simply not seeing it doing so the way you describe it should.
Perhaps you could provide a more precise reference, to source code perhaps? (Even to a different project.)
Post by David Krauss
As for the immediate recourse, C++17 will include a feature to allow destructors to decide whether or not to throw. When not throwing, you can record exceptions into std::exception_ptr objects, perhaps in thread-local storage. Inspect and re-throw those pointers as appropriate. Such technique should be more expressive and intuitive than built-in chaining could ever be.
Maybe, but see my reply to Brian Bi, and later to T.C., for why what C++17 isn't good enough (IMHO).
I don’t think that the proper usage and intent of uncaught_exceptions has (edit: had) been conveyed. In particular, “if (std::uncaught_exceptions() > 0)” makes me wince because it is exactly the same as if (std::uncaught_exception()) which is being deprecated.

For what it’s worth, I’m not sure that uncaught_exceptions is a reliable solution even when used properly, so I simply mentioned that the problem will be solved in time for C++17.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Shachar Shemesh
2015-04-27 17:51:27 UTC
Permalink
Hello David,
Post by David Krauss
Post by Shachar Shemesh
* In any situation where, in C++, the program would terminate, in D
the new exception is added to the "next" field of the exception
currently last in the chain
So the would-be offending exception doesn’t get thrown.
No. It would be more correct to say that would-be offending exception
doesn't propagate beyond the frame of the destructor that was called by
the unwind code.
Post by David Krauss
And if it doesn’t, the exceptions never get handled. Not a good policy
to bake into the core language.
I beg to differ. If you know these exceptions might be important, do
ahead and scan them. If you know RAII has got you covered, ignore them.
True to the spirit of C++, the programmer has enough gun power to shoot
for the sky or shoot himself in the foot.
Post by David Krauss
I was only speaking in terms of sockets, once the problem is detected.
You shouldn’t need to buffer any additional data to gracefully end a
malicious user’s TCP connection, which is what you seemed to be
referring to.
No. It was not. I was referring to resources allocated by the header's
parser, chunked encoding parser, resource locator etc. These need to be
freed once we realize the connection is malicious, and they reside
higher up in the stack frame than where we are.
Post by David Krauss
Post by Shachar Shemesh
May I remind you that exceptions are the only case where C++
potentially does dynamic heap allocations without you explicitly
asking for it?
Exception objects are usually allocated within a reserved arena. Heap
allocation doesn’t sound like a conforming implementation of
exceptions, at least not for bad_alloc.
Yes, they *usually* are. That's why I said "potentially". IIRC, they are
allocated from a pre-allocated space *if there is enough space there*.
bad_alloc can, pretty certainly, fit. If I throw an exception carrying a
2KB backtrace and extra info, maybe not. In that case, the compiler is
entitled to allocate the exception on the heap (if you want a really
nasty job interview question, feel free to ask where are exceptions
stored while in transit).

I will admit that what happens when you try to throw because there was
not enough memory, and now can't throw because of the same problem, did
not occur to me. Personally, I'm okay with calling terminate in such a
case. A case where we can't even begin the unwind is different, in my
eyes, then a case where an exception is thrown during the unwind.
Post by David Krauss
When a destructor throws, not only does unwinding get a headache, but
the object hasn’t been destroyed. A destructor that fails to free
resources when the log facility is unavailable isn’t a very good one.
I don't think this is as big a problem as you make it out to be. At the
end of the day, a destructor implementer should have enough
understanding of what the destructor is doing to implement something
sane. With that said, the problem is a real one, not only for C++. From
Post by David Krauss
Not checking the return value of close() is a common but nevertheless
serious programming error. It is quite possible that errors on a
previous write(2) operation are first reported at the final close().
Not checking the return value when closing the file may lead to
silent loss of data. This can especially be observed with NFS and
with disk quota. Note that the return value should only be used for
diagnostics. In particular close() should not be retried after an
EINTR since this may cause a reused descriptor from another thread
to be closed.
In other words. Naughty you if you do not check the return value of
close, but we're not going to tell you of anything reasonable you can do
about it.
Post by David Krauss
Exception-free destructors are the foundation of RTTI. C++ has good
reason for doing things the way it does.
You lost me. Can you point me to somewhere said reasoning is documented
so I can either give better arguments or concede my mistake?
Post by David Krauss
Post by Shachar Shemesh
But that is precisely what I'm urging to change. You could log, have
log failures throw and do it from a destructor without risking
program termination if my proposal is accepted. In fact, you've just
validated the necessity. Thank you :-)
You still haven’t described how any of these exceptions will actually get handled.
If you are referring to what the programmer should see, then I believe I
gave a pretty good description. If there is anything there you do not
understand, please ask specific questions.

If you are referring to how the compiler should implement this, then the
idea is that, conceptually, each call to a destructor from the unwind
code would be surrounded by a try with catch( std::exception &ex ) {
current_tail->next = &ex;
current_tail = &ex;
}

this means that nested exceptions deriving from std::exception get
chained, while other exceptions call terminate, as per today.
Post by David Krauss
Post by Shachar Shemesh
2a and 2b are just workarounds to the problem C++ is dumping at my
door. They might be a good idea anyway (debatable), but my point is
that C++ should not force me to use them.
Please provide a self-consistent outline of some alternative,
including the restrictions on the malicious_connection handler. Is it
allowed to attempt allocation or logging if it runs while there are
already outstanding bad_alloc or ios::failure exceptions?
yes.
Post by David Krauss
Under what conditions does it re-throw the nested exception?
I'm not sure what that means.
Post by David Krauss
Post by Shachar Shemesh
Again, a workaround. Not a very practical one, too. Most high
performance web servers utilize micro-threading, which is not
something C++ is even aware of.
For best performance, don’t use exceptions at all. There’s currently
an intensive study effort on high-performance coroutines, which will
likely bear fruit after C++17.
This “workaround” doesn’t rely on, but rather avoids C++ exceptions,
and it should be compatible with any threading system: preemptive,
cooperative, or ad-hoc. It’s really only a recommendation to separate
concerns and put major work items under the control of some kind of
dispatcher.
I think this is sidestepping the discussion. If we are going to cancel
exception support, then I agree this discussion is moot. Since I believe
that won't happen, then I feel discussing this within the context of
exception handling is the right thing to do.
Post by David Krauss
Perhaps you could provide a more precise reference, to source code
perhaps? (Even to a different project.)
Sorry. Nothing off the top of my head. I'll try to have a look around later.
Post by David Krauss
For what it’s worth, I’m not sure that uncaught_exceptions is a
reliable solution even when used properly, so I simply mentioned that
the problem will be solved in time for C++17.
And I'm trying to contribute my share as to how such proper solution
should look.

Thanks for listening,
Shachar
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2015-04-28 09:55:30 UTC
Permalink
Post by David Krauss
So the would-be offending exception doesn’t get thrown.
No. It would be more correct to say that would-be offending exception doesn't propagate beyond the frame of the destructor that was called by the unwind code.
Fair enough. It theoretically gets thrown, but stashed in an exception_ptr without being caught when the two unwind operations collide. But there’s almost no observable difference.

The implementation would be free to elide the throw when the proposal is in effect, and to immediately stash the object. An implementation is free to identify the target handler before starting to unwind.

C++17 will introduce a concept of checking whether the current unwinding context was directly initiated by a previously recorded context (currently drafted as std::uncaught_exceptions). Destructors in the inner unwind would need to observe the presence of the inner exception, regardless of whether it’s caught. This would create a way for the program to observe its disappearance, complicating the above optimization.
I beg to differ. If you know these exceptions might be important, do ahead and scan them. If you know RAII has got you covered, ignore them.
The program can’t scan for every “important” exception in every other handler, especially in third-party libraries. Also as I mentioned, it’s hard to handle an inner exception if the outer exception already unwound the stack beyond its natural handler.
No. It was not. I was referring to resources allocated by the header's parser, chunked encoding parser, resource locator etc. These need to be freed once we realize the connection is malicious, and they reside higher up in the stack frame than where we are.
That should be taken care of by RAII. Freeing those resources shouldn’t require first allocating something else, and resources spent on malicious connections should be minimized, hence the suggestion that the destructor close the connection “ASAP,” or abandon it, as opposed to following the protocol.
IIRC, they are allocated from a pre-allocated space if there is enough space there. bad_alloc can, pretty certainly, fit. If I throw an exception carrying a 2KB backtrace and extra info, maybe not.
The reserved space is only used for the exception object itself. Such a backtrace should be stored on the heap, only if new(std::nothrow) provides the storage.
In that case, the compiler is entitled to allocate the exception on the heap (if you want a really nasty job interview question, feel free to ask where are exceptions stored while in transit).
That’s easy — in the reserved area. “In transit” includes the time from the initial throw until its final handler exits (without a rethrow).

The tricky question is where the result of std::current_exception resides, which may be the heap. If you call this function with a full heap, it can save a bad_alloc instead of whatever you expect.
In other words. Naughty you if you do not check the return value of close, but we're not going to tell you of anything reasonable you can do about it.
http://stackoverflow.com/q/22603025/153285 <http://stackoverflow.com/q/22603025/153285>
Post by David Krauss
Exception-free destructors are the foundation of RTTI. C++ has good reason for doing things the way it does.
You lost me. Can you point me to somewhere said reasoning is documented so I can either give better arguments or concede my mistake?
Sorry, I said RTTI when I meant RAII.

If a destructor throws, some part of it wasn’t executed, therefore some resources weren’t freed or some protocol wasn’t followed. RAII is an all-or-nothing proposition.

struct foo {
controller *master;
char *buffer = new char[100];
foo::~foo() noexcept(false) {
std::clog << "ending foo\n"; // std::clog.exceptions() are enabled
master->release( this ); // potential dangling pointer in another object
delete[] buffer; // potential memory leak
}
};

You can work around using scope guards (emulating “finally”), or by doing risky and optional steps last, but destructor failure is usually considered irrecoverable. That’s why, since C++11, you have to explicitly say noexcept(false) to even get the possibility — otherwise it’s immediate termination.
Post by David Krauss
You still haven’t described how any of these exceptions will actually get handled.
If you are referring to what the programmer should see, then I believe I gave a pretty good description. If there is anything there you do not understand, please ask specific questions.
I’m asking how in general to guarantee that the chained exceptions will be unchained and handled.
Post by David Krauss
Please provide a self-consistent outline of some alternative, including the restrictions on the malicious_connection handler. Is it allowed to attempt allocation or logging if it runs while there are already outstanding bad_alloc or ios::failureexceptions?
yes.
If an exception handler exits by a new exception, the original exception is lost. So if you catch an exception with a chained bad_alloc but get an ios::failure before getting a chance to peek at the chain, the bad_alloc will be forgotten. You have the option of re-throwing the bad_alloc even if unwinding, but that will cause it to be chained to the ios::failure, which doesn’t make much sense because there’s no particular relation between those two exceptions.

And the exceptions could just as easily be chained in the other order. How can the handlers be arranged to support that?

Wouldn’t you be better off setting global flags indicating failure of various systems, and polling them whenever a chain would be processed?
Post by David Krauss
Again, a workaround. Not a very practical one, too. Most high performance web servers utilize micro-threading, which is not something C++ is even aware of.
For best performance, don’t use exceptions at all. There’s currently an intensive study effort on high-performance coroutines, which will likely bear fruit after C++17.
This “workaround” doesn’t rely on, but rather avoids C++ exceptions, and it should be compatible with any threading system: preemptive, cooperative, or ad-hoc. It’s really only a recommendation to separate concerns and put major work items under the control of some kind of dispatcher.
I think this is sidestepping the discussion. If we are going to cancel exception support, then I agree this discussion is moot. Since I believe that won't happen, then I feel discussing this within the context of exception handling is the right thing to do.
You brought high-performance servers into the discussion. Which one uses C++ exceptions for things like malicious connections?

It would be nice to integrate traditional exceptions with modern server architectures. As long as throw is slow, though, there’s a motivation to allow it to be avoided.

In any case, a solution to the problem at hand without throwing anything is just as good, and doesn’t detract from throw.
Post by David Krauss
For what it’s worth, I’m not sure that uncaught_exceptions is a reliable solution even when used properly, so I simply mentioned that the problem will be solved in time for C++17.
And I'm trying to contribute my share as to how such proper solution should look.
I don’t see the connection between throwing a new exception during unwinding, and getting information about when the current unwinding started.

Any contributions on that topic would be appreciated, though.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Martin Ba
2015-05-02 18:54:01 UTC
Permalink
Post by Shachar Shemesh
Hi all,
This, I think, is the most sore point in C++ today. Exception nesting.
Current standard mandates that an exception thrown during stack unwind for
another exception causes immediate program termination. This has led to
many suggestions that destructors never throw. I find that suggestion both
problematic and too onerous.
It is problematic because in 99% of the cases, it is quite alright for
destructors to throw. ... ...
May I humbly point to this piece: http://stackoverflow.com/a/4098662/321013

I'd say that 90% of destructors are totally fine because they just contain
cleanup / release code that never throws anyway.

Those destructors with commit/flush (or rollback) semantics that contain
code that can *legitimately* throw can:

* today: use std::uncaught_exception() to determine whether they are
allowed to throw and get some amount of false negatives
* in C++17: use std::uncaught_exceptionS() to accurately determine whether
they are allowed to throw, and if not, just (possibly log and) swallow it,
just like one of your proposed points.

For d'tors where throwing is legitimate (files, DB connections, ...) I do
not think it is too much to ask to do the explicit check dance in their
c'tor and d'tor.

I would love to hear a well thought out proposal that tries something like
D apparently does today by using / extending the machinery we already have
for http://en.cppreference.com/w/cpp/error/nested_exception

cheers.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Loading...