协程(Coroutines)在 C++20 标准中正式加入标准库,为异步编程提供了强大而简洁的工具。虽然在 C++17 里没有直接支持协程的语法,但通过第三方库(如 Boost.Coroutine 或 cppcoro)以及手写状态机,仍能在 C++17 环境下模拟协程的行为。下面我们通过一个简易的生成器实现,演示如何在 C++17 中用类和迭代器模式实现协程效果。
#include <iostream>
#include <functional>
#include <vector>
template <typename T>
class Generator {
public:
using GeneratorFunc = std::function<void(std::function<void(const T&)>)>;
explicit Generator(GeneratorFunc func) : genFunc(std::move(func)) {}
// 内部迭代器,用于遍历生成器产出的值
class Iterator {
public:
Iterator(Generator* parent, bool end = false) : parent(parent), finished(end) {
if (!finished) {
// 立即请求第一个值
parent->nextValue(*this);
}
}
const T& operator*() const { return value; }
const T* operator->() const { return &value; }
Iterator& operator++() {
parent->nextValue(*this);
return *this;
}
bool operator==(const Iterator& other) const { return finished == other.finished; }
bool operator!=(const Iterator& other) const { return !(*this == other); }
private:
friend class Generator;
Generator* parent;
T value;
bool finished = false;
// 生成器从内部调用此方法获取下一个值
void setValue(const T& val) { value = val; finished = false; }
};
Iterator begin() { return Iterator(this); }
Iterator end() { return Iterator(this, true); }
private:
GeneratorFunc genFunc;
std::vector <T> buffer; // 缓存最近生成的值,支持多次遍历
size_t bufferIndex = 0;
void nextValue(Iterator& it) {
if (bufferIndex < buffer.size()) {
it.setValue(buffer[bufferIndex++]);
} else {
// 通过回调模式生成下一个值
bool produced = false;
genFunc([&](const T& val) {
buffer.push_back(val);
it.setValue(val);
produced = true;
});
if (!produced) {
it.finished = true;
} else {
bufferIndex = 1;
}
}
}
};
// 使用示例:生成斐波那契数列的前10项
int main() {
Generator <int> fib([](std::function<void(const int&)> yield) {
int a = 0, b = 1;
for (int i = 0; i < 10; ++i) {
yield(a);
int tmp = a + b;
a = b;
b = tmp;
}
});
std::cout << "斐波那契数列前10项:\n";
for (int n : fib) {
std::cout << n << ' ';
}
std::cout << '\n';
return 0;
}
代码要点说明
-
Generator 类
- 接收一个函数
GeneratorFunc,该函数内部使用yield回调把生成的值“投递”给外部。 begin()与end()方法返回自定义迭代器,满足 C++ 的范围 for 循环。
- 接收一个函数
-
Iterator 内部状态
finished用来标记是否已生成完所有值。setValue供内部nextValue调用来更新迭代器值。
-
缓冲区
buffer- 用来缓存已生成的值,支持多次遍历。
bufferIndex跟踪当前读取位置。
-
协程模拟
- 通过回调
yield,在生成函数中暂停并返回下一个值,类似协程的yield。 - 生成函数在需要停止时不调用
yield,导致produced为false,从而终止迭代。
- 通过回调
进一步扩展
- 异步读取:可以在
GeneratorFunc内部使用std::future或std::async,在协程停顿时进行 IO 操作。 - 多种数据类型:`Generator ` 可被任何可拷贝或移动类型使用,满足不同业务需求。
- 错误处理:在
yield里传递异常或错误码,以便外层捕获。
尽管 C++17 本身不支持 co_await、co_yield 等语法,但通过上述模式仍能在较老的编译器中实现协程般的行为。随着 C++20 的普及,建议在新项目中直接使用原生协程语法,以获得更直观、性能更优的实现。