The typical advice in C++ is to detect stack unwinding in the destructor using std::uncaught_exceptions()
, see the example from https://en.cppreference.com/w/cpp/error/uncaught_exception :
struct Foo {
int count = std::uncaught_exceptions();
~Foo() {
std::cout << (count == std::uncaught_exceptions()
? "~Foo() called normally\n"
: "~Foo() called during stack unwinding\n");
}
};
But this advice looks no longer applicable to C++20 coroutines, which can be suspended and resumed including during stack unwinding. Consider the following example:
#include <coroutine>
#include <iostream>
struct ReturnObject {
struct promise_type {
ReturnObject get_return_object() { return { std::coroutine_handle<promise_type>::from_promise(*this) }; }
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() {}
void return_void() {}
};
std::coroutine_handle<promise_type> h_;
};
struct Foo {
int count = std::uncaught_exceptions();
Foo() { std::cout << "Foo()\n"; }
~Foo() {
std::cout << (count == std::uncaught_exceptions()
? "~Foo() called normally\n"
: "~Foo() called during stack unwinding\n");
}
};
struct S
{
std::coroutine_handle<ReturnObject::promise_type> h_;
~S() { h_(); }
};
int main()
{
auto coroutine = []() -> ReturnObject { Foo f; co_await std::suspend_always{}; };
auto h = coroutine().h_;
try
{
S s{ .h_ = h };
std::cout << "Exception being thrown\n";
throw 0; // calls s.~S() during stack unwinding
}
catch( int ) {}
std::cout << "Exception caught\n";
h();
h.destroy();
}
It uses the same class Foo
inside the coroutine, which is destructed normally (not due to stack unwinding during exception), but still prints:
Exception being thrown
Foo()
Exception caught
~Foo() called during stack unwinding
Demo: https://gcc.godbolt.org/z/Yx1b18zT9
How one can re-design class Foo
to properly detect stack unwinding in coroutines as well?