C ++ 20协程:实现可期待的未来

Jon*_*ann 11 c++ future async-await c++20 c++-coroutine

由于Coroutines TS在Kona的ISO会议上已被C ++ 20接受,因此我开始自己研究一下它们。Clang已经对协程提供了不错的支持,但是仍然缺乏库支持的实现。特别是,Awaitable类型,如std::futurestd::generator等方面都尚未实现。

因此,我将自己置于std::future等待之中。我主要关注James McNellis在CppCon 2016上的演讲,特别是这张幻灯片:

James McNellis时间戳记37:41上的Screengrab在CppCon 2016上的演讲

这是2019年,实际上我在这张幻灯片上的(可能未经测试?)代码上遇到了一些麻烦:

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

另外,在我的libc ++版本中,thenis_ready尚不可用,std::futurestd::experimental::future仍缺少其中的一部分。为了避免与Awaiter打交道并实现将来的延续,我编写了派生的Future类,它是Awaitable和Awaiter。根据我的理解,最终两者都会成立std::future。您可以在Compiler Explorer上看到我的示例。它确实可以编译。

但是,它也确实存在段错误。这发生在await_resume何时get()调用。这实际上不足为奇,因为此时valid()回报false(调用get()UB)。我认为这是因为当then用于继续将来时,原始的将来对象已移入异步将来,从而使旧的将来无效(*this当时await_resume称为,所以在移动之后)。我的实现从这个答案和我在GitHub上找到的代码then大致上受到启发。这些可能并不理想,但是cppreference明确指出了call的后置条件,因此我认为脱离原来的未来是正确的。 valid() == falsethen

我在这里想念什么?上面的幻灯片中似乎已经存在该“错误”。我该如何调和这个问题?有人知道等待的未来的(有效的)现有实现吗?谢谢。

Yeh*_* B. 2

正如您自己提到的,问题是因为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();
        });
    }
Run Code Online (Sandbox Code Playgroud)

顺便说一句,VS2017 抱怨承诺类型中同时存在set_exception()和。unhandled_exception()我删除set_exception()并更改unhandled_exception()为:

    void unhandled_exception() {
        _promise.set_exception(std::current_exception());
    }
Run Code Online (Sandbox Code Playgroud)