【发布时间】:2021-07-24 09:10:24
【问题描述】:
C++ 中的典型建议是使用std::uncaught_exceptions() 检测析构函数中的堆栈展开,参见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");
}
};
但是这个建议看起来不再适用于 C++20 协程,它可以暂停和恢复,包括在堆栈展开期间。考虑以下示例:
#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();
}
它在协程中使用了相同的类Foo,该类被正常销毁(不是由于异常期间堆栈展开),但仍然打印:
Exception being thrown
Foo()
Exception caught
~Foo() called during stack unwinding
演示:https://gcc.godbolt.org/z/Yx1b18zT9
如何重新设计类 Foo 以正确检测协程中的堆栈展开?
【问题讨论】:
-
如果
coroutine_handle从协程的机器中泄漏,这是一种代码味道。协程故意是一个函数的实现细节。协程未来应该始终成为函数协程与外界之间的中介。 -
你说的未来是什么意思?可能是 promise_type?
-
不,promise 是函数协程机制定义的类型。未来是用户与之交互的类型。 “promise”存储最终值,“future”向最终接收者提供对该值的访问。未来类型是函数的返回类型,协程与否。
-
谢谢,我明白了。至于代码,其实是根据这个手册:scs.stanford.edu/~dm/blog/c++-coroutines.html我只是简化了一点,并调整以突出显示堆栈展开的这个问题。
-
这并没有减少代码的味道。复制future类型是一件......可疑的事情,更不用说提取
coroutine_handle并直接使用它。该页面就像一个巨大的不良协程实践列表。我希望人们不要再在互联网上提供糟糕的建议。
标签: c++ c++20 c++-coroutine stack-unwinding