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

C++20 coroutines: implementing an awaitable future

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

因此,我决定让 std::future 变得可等待。我基本上遵循了 talk by James McNellis at CppCon 2016,特别是这张幻灯片:

现在是 2019 年,我实际上在使用这张幻灯片上的代码(大概未经测试?)时遇到了一些问题:

此外,thenis_readystd::future 中不可用,但属于 std::experimental::future which is still missing from my libc++ version. To avoid dealing with the Awaiter and to implement future continuations, I wrote a derived future class which is Awaitable and an Awaiter. It is my understanding that eventually both would also be true of std::future. You can see my example on Compiler Explorer 的一部分。它确实编译。

但是,它也段错误。当 get() 被调用时,这发生在 await_resume 中。这实际上并不奇怪,因为那时 valid() returns false(调用 get() UB)。我认为这是因为当then用于继续未来时,原来的未来object被移动到异步未来,从而使旧的未来(*this在[=24时=] 被称为,所以在移动之后)。我对 then 的实现大致上受到 this answer and this code I found on GitHub. Those may not be ideal, but cppreference explicitly states valid() == false 的启发,作为调用 then 的后置条件,因此我认为移出原始 future 是正确的。

我在这里错过了什么? "bug" 似乎已经出现在上面的幻灯片中。我怎样才能调和这个问题?有谁知道 Awaitable 未来的(工作)现有实施?谢谢。

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