【问题标题】:C++20 coroutines: implementing an awaitable futureC++20 协程:实现可等待的未来
【发布时间】:2019-07-31 15:37:43
【问题描述】:

自从 Coroutines TS 在 Kona 的 ISO 会议上被 C++20 接受后,我开始为自己尝试一下。 Clang 已经对协程提供了不错的支持,但仍然缺乏库支持的实现。特别是std::futurestd::generatorAwaitable类型还没有实现。

因此,我决定让std::future 等待。我主要关注talk by James McNellis at CppCon 2016,特别是这张幻灯片:

现在是 2019 年,我实际上对这张幻灯片上的(可能未经测试?)代码有些问题:

  • 在我看来重载operator co_await 不再是一回事了?相反,应该使用promise_type 的可选await_transform。不过,我不确定我是否正确。
  • 未来的then 延续按值捕获句柄,但resume 成员函数不是 const 限定的。我通过制作 lambda mutable 来解决这个问题。

另外,thenis_readystd::future 中不可用,但它们是我的 libc++ 版本中仍然缺少的 std::experimental::future 的一部分。为了避免处理 Awaiter 并实现未来的延续,我编写了一个派生的未来类,它是 Awaitable 和一个 Awaiter。据我了解,最终两者都适用于std::future。你可以看到我的example on Compiler Explorer。它确实编译。

但是,它也确实出现段错误。当get() 被调用时,这发生在await_resume 中。这实际上并不奇怪,因为 valid() 在此时返回 false(调用 get() UB)。我认为这是因为当then用于继续future时,原来的future对象被移入了async future,从而使旧的future失效(*this当时await_resume被调用,所以在move之后) .我对then 的实现大致受this answerthis code 我在GitHub 上找到的启发。那些可能并不理想,但cppreference explicitly statesvalid() == false作为调用then的后置条件,所以我认为离开原来的未来是正确的。

我在这里缺少什么?这个“错误”似乎已经出现在上面的幻灯片中。我该如何调和这个问题?有人知道 Awaitable future 的(工作)现有实现吗?谢谢。

【问题讨论】:

  • "自从 Coroutines TS 在 Kona 的 ISO 会议上被 C++20 接受" C++20。 C++20 协程功能与 Coroutines TS 相似,但不是 1:1 相同。
  • “有人知道 Awaitable future 的(工作)现有实现吗?” github.com/lewissbaker/cppcoro。请注意,作者还有一个带有解释的博客,例如lewissbaker.github.io/2017/11/17/…
  • herestd::future 可以等待的方式。

标签: c++ async-await future c++20 c++-coroutine


【解决方案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 抱怨在 promise 类型中同时包含 set_exception()unhandled_exception()。我删除了set_exception() 并将unhandled_exception() 更改为:

    void unhandled_exception() {
        _promise.set_exception(std::current_exception());
    }

【讨论】:

    猜你喜欢
    • 2018-12-04
    • 2020-07-26
    • 1970-01-01
    • 2018-10-26
    • 1970-01-01
    • 2020-02-26
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多