gpt4 book ai didi

C++ 协程 : implementing task

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:42:21 25 4
gpt4 key购买 nike

因此,我正在尝试理解协程的新概念和复杂概念。使用 Clang,通过 clang++ -std=c++17 -fcoroutines-ts -stdlib=libc++ 编译一切顺利。

最有用的概念之一是 task<>协程类型,提到here甚至还有一些有趣的实现,by Gor Nishanov in cppcoro library .

好的,在最简单的情况下尝试自己看起来不错。因此,我们的目标是实现如下所示的功能:

    {
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 的特化(可以是没有 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{}; }
};

简洁明了...它会导致段错误而没有任何可读的堆栈跟踪 =(在 task<> 时工作正常更改为任何非空模板,例如 task<char> .

我的模板特化有什么问题?还是我遗漏了这些协程的一些棘手概念?

如有任何想法,我们将不胜感激。

最佳答案

显然,通常的嫌疑人是罪犯:特化!来自标准本身 [temp.expl.spec]/7

When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

为了避免问题,让我们尽可能简单: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{
//...
};

关于C++ 协程 : implementing task<void>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56106453/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com