`co_yield` return 能否在协程恢复时从调用者那里获得一个值?
Can `co_yield` return a value from the caller on resumption of the coroutine?
C++20 引入了协程,可用于创建生成器和其他类似的东西:
generator<int> counter(int max) {
for(int i = 0; i < max; i++) {
co_yield i;
}
}
有没有什么方法可以创建协程,以便调用者可以提供 co_yield
在协程恢复后返回的响应?我们称其为 channel
而不是生成器。
这是我希望能够执行的操作的示例:
channel<int, int> accumulator(int initial) {
while(true) {
// Can channel be written so co_yield provides a response?
int response = co_yield initial;
initial += response;
}
}
在这里,每当调用者恢复协程时,它都会提供一个值,然后在恢复协程后从 co_yield
返回该值,如下所示:
std::vector<int> accumulate(std::vector<int> values) {
channel<int, int> acc = accumulator(0);
std::vector<int> summed_values;
for(int v : values) {
// Get whatever value was yielded by the accumulator
int sum = acc.recieve();
// Do something with the value
summed_values.push_back(sum);
// Resume the accumulator, returning this value from co_yield:
acc.send(v);
}
return summed_values;
}
根据评论编辑
任何人都可以提供一些指导或示例来说明如何执行此操作吗?协程对我来说还是很新的。我有一个 channel
class 的基本实现,但我不确定为了实现这个目标应该从 yield_value
返回什么。
我在评论中标记了 (A)
和 (B)
的两个位置。
template <class Out, class In>
struct channel {
struct promise_type {
Out current_value;
auto yield_value(Out value) {
current_value = value;
// (A) What do I return here?
}
channel get_return_object() {
return {std::coroutine_handle<promise_type>::from_promise(*this)};
}
// We run up until the first value is ready
auto initial_suspend() noexcept { return std::suspend_never(); }
auto final_suspend() noexcept { return std::suspend_always(); }
void unhandled_exception() noexcept { std::terminate(); }
};
Out receive() {
return handle.promise().current_value;
}
void send(In response) {
// (B) What do I do here?
}
// Constructors, destructor and move assignment operator omitted for brevity
private:
std::coroutine_handle<promise_type> handle = nullptr;
};
key是await_resume
,调用waiter(yield_value
的结果)得到co_yield
的结果。
您还需要将响应存储在某处。正如 Raymond Chen 在 中建议的那样,您可以将值放入 promise_type
.
的新数据成员中
所以变化是:
添加一个数据成员到promise_type
。
In response;
为该数据成员 return 定义自定义等待程序。
struct awaiter : std::suspend_always {
friend promise_type;
constexpr In await_resume() const { return m_p->response; }
private:
constexpr awaiter(promise_type* p) : m_p(p) {}
promise_type* m_p;
};
在(A)
中,return自定义服务员。
return awaiter(this);
在(B)
中设置数据成员,然后恢复协程。
handle.promise().response = response;
handle.resume();
C++20 引入了协程,可用于创建生成器和其他类似的东西:
generator<int> counter(int max) {
for(int i = 0; i < max; i++) {
co_yield i;
}
}
有没有什么方法可以创建协程,以便调用者可以提供 co_yield
在协程恢复后返回的响应?我们称其为 channel
而不是生成器。
这是我希望能够执行的操作的示例:
channel<int, int> accumulator(int initial) {
while(true) {
// Can channel be written so co_yield provides a response?
int response = co_yield initial;
initial += response;
}
}
在这里,每当调用者恢复协程时,它都会提供一个值,然后在恢复协程后从 co_yield
返回该值,如下所示:
std::vector<int> accumulate(std::vector<int> values) {
channel<int, int> acc = accumulator(0);
std::vector<int> summed_values;
for(int v : values) {
// Get whatever value was yielded by the accumulator
int sum = acc.recieve();
// Do something with the value
summed_values.push_back(sum);
// Resume the accumulator, returning this value from co_yield:
acc.send(v);
}
return summed_values;
}
根据评论编辑
任何人都可以提供一些指导或示例来说明如何执行此操作吗?协程对我来说还是很新的。我有一个 channel
class 的基本实现,但我不确定为了实现这个目标应该从 yield_value
返回什么。
我在评论中标记了 (A)
和 (B)
的两个位置。
template <class Out, class In>
struct channel {
struct promise_type {
Out current_value;
auto yield_value(Out value) {
current_value = value;
// (A) What do I return here?
}
channel get_return_object() {
return {std::coroutine_handle<promise_type>::from_promise(*this)};
}
// We run up until the first value is ready
auto initial_suspend() noexcept { return std::suspend_never(); }
auto final_suspend() noexcept { return std::suspend_always(); }
void unhandled_exception() noexcept { std::terminate(); }
};
Out receive() {
return handle.promise().current_value;
}
void send(In response) {
// (B) What do I do here?
}
// Constructors, destructor and move assignment operator omitted for brevity
private:
std::coroutine_handle<promise_type> handle = nullptr;
};
key是await_resume
,调用waiter(yield_value
的结果)得到co_yield
的结果。
您还需要将响应存储在某处。正如 Raymond Chen 在 promise_type
.
所以变化是:
添加一个数据成员到
promise_type
。In response;
为该数据成员 return 定义自定义等待程序。
struct awaiter : std::suspend_always { friend promise_type; constexpr In await_resume() const { return m_p->response; } private: constexpr awaiter(promise_type* p) : m_p(p) {} promise_type* m_p; };
在
(A)
中,return自定义服务员。return awaiter(this);
在
(B)
中设置数据成员,然后恢复协程。handle.promise().response = response; handle.resume();