因此,我在这里试图理解协程的新的和复杂的概念。为此,请使用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{}; }
};
整洁又简单...它会导致段错误,而没有任何可读的stacktrace =(
将task<>
更改为任何非空模板,例如task<char>
时,效果很好。
我的模板专业化怎么了?还是我在那些协程中缺少一些棘手的概念?
感谢任何想法。
答案 0 :(得分:2)
显然,通常的犯罪嫌疑人是罪犯:专业!根据标准本身[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{
//...
};