C++20协程:实现可等待的future

14
自从在Kona的ISO会议上,协同程序TS已经被接受并纳入了C++20标准,我开始对它们进行一些尝试。Clang已经具有相当不错的协程支持,但库支持的实现仍然缺少。特别是像std::future、std::generator等类型尚未实现。

因此,我决定让std::future可等待。我主要参考了James McNellis在CppCon 2016的演讲,特别是这张幻灯片:talk by James McNellis at CppCon 2016

Screengrab at timestamp 37:41 of James McNellis talk at CppCon 2016

既然现在已经是2019年,我遇到了这个幻灯片上的(假设未经测试的?)代码有些麻烦:

  • 我觉得重载operator co_await已经不再可行?而应该使用promise_type的可选await_transform。不过我也不能确定自己理解的是否正确。
  • futurethen继续通过值捕获句柄,但是resume成员函数没有被声明为const。我通过使lambda表达式mutable来解决这个问题。

此外,thenis_ready都不适用于std::future,而是std::experimental::future的一部分,但我的libc++版本中仍然缺失。为了避免处理Awaiter并实现future continuations,我编写了一个派生的future类,它既是Awaitable又是Awaiter。我认为最终std::future也将是如此。你可以在Compiler Explorer上查看我的示例。这个代码可以编译。

然而,在调用get()时,await_resume会发生段错误。这实际上并不奇怪,因为此时valid()返回false(使调用get()成为未定义行为)。我认为这是因为当then用于继续future时,原始future对象被移动到异步future中,从而使旧future无效(在调用await_resume时的*this,所以在移动后)。我的then实现受此答案此代码的启发,我在GitHub上找到了这些内容。它们可能不是理想的,但cppreference明确说明调用then的后置条件是valid() == false,因此我认为从原始future中移出是正确的。

我错过了什么?这个“bug”似乎已经存在于上面的幻灯片中。如何解决这个问题?有没有人知道一个(可行的)现有的Awaitable future实现?谢谢。


3
自从协程 TS 在 Kona 的 ISO 会议上被列入 C++20,需要注意的是协程 TS 并不完全等同于被采纳进 C++20 中的协程特性。C++20 中的协程特性与协程 TS 相似,但并非一一对应。 - Nicol Bolas
2
有没有人知道一个(可用的)现有的Awaitable future实现? https://github.com/lewissbaker/cppcoro。请注意,作者还有一个博客,其中包含解释,例如https://lewissbaker.github.io/2017/11/17/understanding-operator-co-await。 - sudo rm -rf slash
这里是如何使std::future可等待的。 - anton_rh
1个回答

2
正如你自己提到的,问题是因为在调用 .then() 后,future 已经被移动了。技巧是在准备好后将其移回。这可以通过向 .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() { std::terminate(); }
    void unhandled_exception() {
        _promise.set_exception(std::current_exception());
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接