自从Coroutines TS在科纳(Kona)的ISO会议上被C ++ 20接受后,我就开始为他们自己玩一些。 Clang已经对协程提供了不错的支持,但是仍然缺乏库支持的实现。特别是,尚未实现 Awaitable 类型,例如std::future
,std::generator
等。
因此,我自己承担起std::future
的等待。我在很大程度上遵循了talk by James McNellis at CppCon 2016,尤其是这张幻灯片:
现在是2019年,我实际上对这张幻灯片上的(可能未经测试?)代码遇到了麻烦:
operator co_await
过载已不是问题了吗?相反,应该使用await_transform
中的可选promise_type
。不过,我不确定我是否正确。then
继续按值捕获句柄,但是resume
成员函数不是const限定的。我通过制作lambda mutable
来解决此问题。此外,then
中没有is_ready
和std::future
,但它们是std::experimental::future
的一部分,而我的libc ++版本中仍然缺少它们。为了避免与Awaiter打交道并实现将来的延续,我编写了派生的Future类,它是Awaitable和Awaiter。据我了解,std::future
最终都将适用。您可以看到我的example on Compiler Explorer。它执行编译。
但是,它也确实存在段错误。在调用await_resume
时,这会在get()
中发生。实际上,这并不奇怪,因为valid()
会在此时返回false
(调用get()
UB)。我认为这是因为当使用then
来延续未来时,原始的Future对象被移入了异步的Future中,从而使旧的Future失效(在*this
时被称为await_resume
,因此在移动之后)。我在GitHub上找到的this answer和this code启发了then
的实现。这些可能并不理想,但是将cppreference explicitly states valid() == false
作为调用then
的后置条件,所以我认为摆脱原来的未来是正确的。
我在这里想念什么?上面的幻灯片中似乎已经存在该“错误”。我该如何调和这个问题?有人知道等待的未来的(有效的)现有实现吗?谢谢。
答案 0 :(得分:1)
您曾经提到自己,问题是因为future
在调用.then()
之后已经移出。诀窍是在准备就绪时将其移回。如果传递给.then()
的延续采用future
,而不是它所拥有的值,则可以完成此操作。
这是我从您的代码中获取并更改的功能。我也将它们从传递参数到std::async
改为捕获它们,因为对我来说这看起来更直观,但这不是重要的更改。
template <typename Work>
auto then(Work&& w) -> co_future<decltype(w())> {
return { std::async([fut = std::move(*this), w = std::forward<Work>(w)]() mutable {
fut.wait();
return w();
})};
}
template <typename Work>
auto then(Work&& w) -> co_future<decltype(w(std::move(*this)))> {
return { std::async([fut = std::move(*this), w = std::forward<Work>(w)]() mutable {
return w(std::move(fut));
})};
}
void await_suspend(std::experimental::coroutine_handle<> ch) {
then([ch, this](auto fut) mutable {
*this = std::move(fut);
ch.resume();
});
}
顺便说一句,VS2017抱怨在承诺类型中同时包含set_exception()
和unhandled_exception()
。我删除了set_exception()
并将unhandled_exception()
更改为此:
void unhandled_exception() {
_promise.set_exception(std::current_exception());
}