【发布时间】:2023-03-09 20:52:01
【问题描述】:
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 类的基本实现,但我不确定应该从 yield_value 返回什么来实现这一点。
我在 cmets 中标记了 (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;
};
【问题讨论】:
-
是的。
co_yield v的返回值是co_await p.yield_value(v)的返回值,其中p是协程承诺。因此,调用者可以调用 promise 上的自定义方法(您将其称为send)以将值反馈到协程中,然后yield_value()方法返回一个输出该值的等待器。 -
非常感谢您的回复。你能给我一些关于实施的指导吗?我已经更新了问题以包括我到目前为止的内容!我只标记了两个位置 A 和 B 我不知道如何填写
-
send可以使用handle.promise()访问promise_type并将值放入新的promise 成员变量中。yield_value然后返回一个产生该值的等待者。