In a recent discussion on
scope guards in C++, I saw a curious
standard library function used to detect the exception handling state — std::uncaught_exception().
Reading the cppreference page on uncaught_exception
hints that something funny is going on — why is there both a bool-returning implementation and an int one? And given
the latter was added in a later C version (C11 vs C++17, respectively), might there be some case the original
bool-returning version didn’t handle?
Yes.
The colloquial "double throwing" derives from [except.throw]/9:
If the exception handling mechanism handling an uncaught exception directly invokes a function that exits via an exception, the function
std::terminateis invoked.
So it may seem like you can’t double throw — if you throw while there is an uncaught exception, and exit the current
function, the program std::terminate's.
... but, you can throw and catch while unwinding (in a destructor) without leaving the current scope.
... and, you can nest this as many times as you want (if you are a masochist):
#include <cstdio>
#include <exception>
struct ThrowsN {
int n;
~ThrowsN() {
printf("~ThrowsN{%2d} start : std::uncaught_exceptions()=%d\n",
n, std::uncaught_exceptions());
if(n == 0) return;
try {
ThrowsN tn{n-1};
throw n;
} catch(...) {
}
printf("~ThrowsN{%2d} end : std::uncaught_exceptions()=%d\n",
n, std::uncaught_exceptions());
}
};
int main() {
ThrowsN tn{4};
return 0;
}
which outputs (godbolt):
~ThrowsN{ 4} start : std::uncaught_exceptions()=0
~ThrowsN{ 3} start : std::uncaught_exceptions()=1
~ThrowsN{ 2} start : std::uncaught_exceptions()=2
~ThrowsN{ 1} start : std::uncaught_exceptions()=3
~ThrowsN{ 0} start : std::uncaught_exceptions()=4
~ThrowsN{ 1} end : std::uncaught_exceptions()=3
~ThrowsN{ 2} end : std::uncaught_exceptions()=2
~ThrowsN{ 3} end : std::uncaught_exceptions()=1
~ThrowsN{ 4} end : std::uncaught_exceptions()=0