C++协程:实现任务<void>



所以,我正在尝试理解新的&amp;共同的复杂概念。为此clang,通过clang++ -std=c++17 -fcoroutines-ts -stdlib=libc++编译很好。

最有用的概念之一是task<> Coroutine类型,此处提到了Gor Nishanov和Cppcoro库中的几个有趣的实现。

好吧,在最简单的情况下尝试自己看起来不错。因此,目的是实施应如下的功能:

    {
        auto producer = []() -> task<int> {
            co_return 1;
        };
        auto t = producer();
        assert(!t.await_ready());
        assert(t.result() == 1);
        assert(t.await_ready());
    }

模板类task<>本身非常简单:

#pragma once
#include <experimental/coroutine>
#include <optional>
namespace stdx = std::experimental;
template <typename T=void>
struct task 
{
    template<typename U>
    struct task_promise;
    using promise_type = task_promise<T>;
    using handle_type = stdx::coroutine_handle<promise_type>;
    mutable handle_type m_handle;
    task(handle_type handle)
        : m_handle(handle) 
    {}
    task(task&& other) noexcept 
        : m_handle(other.m_handle)
    { other.m_handle = nullptr; };
    bool await_ready() 
    { return m_handle.done(); }
    bool await_suspend(stdx::coroutine_handle<> handle) 
    {
        if (!m_handle.done()) {
            m_handle.resume();
        }
        return false;
    }
    auto await_resume() 
    { return result(); }
    T result() const 
    {     
        if (!m_handle.done())
            m_handle.resume();  
        if (m_handle.promise().m_exception)
            std::rethrow_exception(m_handle.promise().m_exception);
        return *m_handle.promise().m_value;
    }
    ~task() 
    {
        if (m_handle)
            m_handle.destroy();
    }
    template<typename U>
    struct task_promise 
    {
        std::optional<T>    m_value {};
        std::exception_ptr  m_exception = nullptr;
        auto initial_suspend() 
        { return stdx::suspend_always{}; }
        auto final_suspend() 
        { return stdx::suspend_always{}; }
        auto return_value(T t) 
        {
            m_value = t;
            return stdx::suspend_always{};
        }
        task<T> get_return_object()
        { return {handle_type::from_promise(*this)}; }
        void unhandled_exception() 
        {  m_exception = std::current_exception(); }
        void rethrow_if_unhandled_exception()
        {
            if (m_exception)
                std::rethrow_exception(std::move(m_exception));
        }
    };
};

真的无法使较小的代码完整且可编译,对不起。无论如何,它以某种方式起作用,但是仍然存在task<void>的情况,它的用法可能如下:

    {
        int result = 0;
        auto int_producer = []() -> task<int> {
            co_return 1;
        };
        auto awaiter = [&]() -> task<> { // here problems begin
            auto i1 = co_await int_producer();
            auto i2 = co_await int_producer();
            result = i1 + i2;
        };
        auto t = awaiter();
        assert(!t.await_ready());
        t.await_resume();
        assert(result == 2);
    }

后者似乎根本没有问题,看来task_promise<U>需要对void进行专业化(可能是没有该无效情况的非模板结构(。所以,我尝试了:

    template<>
    struct task_promise<void>
    {
        std::exception_ptr  m_exception;
        void return_void() noexcept  {}
        task<void> get_return_object() noexcept
        { return {handle_type::from_promise(*this)}; }
        void unhandled_exception() 
        { m_exception = std::current_exception(); }
        auto initial_suspend() 
        { return stdx::suspend_always{}; }
        auto final_suspend() 
        { return stdx::suspend_always{}; }
    };

整洁而简单...它会导致segfault而没有任何可读的stacktrace =((当task<>更改为任何非流动模板时,工作正常,例如task<char>

我的模板专业化有什么问题?还是我错过了那些共同的概念?

将感谢任何想法。

显然,通常的嫌疑人是罪犯:专业!从标准本身[temp.expl.spec]/7

编写专业化时,请注意其位置;或使其编译将是一项试验,以便在其自动化。

要避免问题,让我们尽可能简单:task_promise可以是一个非模板,并且会尽快声明成员专业化:

template<class T=void>
struct task{
  //...
  struct task_promise{
    //...
    };
  };
//member specialization declared before instantiation of task<void>;
template<>
struct task<void>::task_promise{
  //...
  };

相关内容

最新更新