简介

在 C++ 中,coroutine 是可暂停以及可恢复执行的函数,在定义一个函数时,如果函数体中出现了 co_yield, co_return 或者 co_await 关键字,则该函数会被编译器视为一个 coroutine, 并且按照 coroutine 的方式来翻译,然而,在定义 coroutine 时需要注意几个限制:

一是co_yield, co_return, 和 co_await 这些关键字并不是能出现在任何地方;

二是 coroutine(作为函数)的返回值类型有要求,而且返回值类型不能是 auto , decltype(auto) 这样的 type placeholder;

具体这些限制和要求我们限于篇幅不会详细讲述,但是感兴趣的读者可以参考 cppreference 上的 coroutine 资料页面

本文是初级的并且面向初级读者,本文的目标是教会读者如何快速写好一个 coroutine 的返回值类型的定义。

补充说明

这是一篇力求快速,但是极其不严谨的教程,后续如果有机会,我们会提供一篇比这个稍微严谨一些的文章做 coroutine 的原理解释,可能会从编译器如何翻译 coroutine 的角度来说,其中可能会涉及到源码/机器码分析。

定义并实现一个 coroutine

我们不希望从 coroutine 的执行过程、原理或者是编译器如何翻译 coroutine 这种角度开始讲,我觉得最快的学习方式是上手去做,那么我们可以先打开 IDE(推荐 CLion),新建一个项目,C++ 标准选 C++20,然后直接在 main.cpp 文件中开始编辑。

无限序列生成器 (generator)

我们注意到在 Python 中有类似这样的代码:

def my_range(n):
  while True:
    yield n
    n = n + 1
  
num_limits = 10
for x in my_range(1):
  print(x)
  num_limits = num_limits - 1
  if num_limits <= 0:
    break

# 输出内容是打印前 10 个正整数

在 Python 中像 my_range 这样定义的函数称为 generator, 特点就是它的函数体中有 yield 语句。

事实上有了 C++20 引入了 coroutine 语法的支持,在 C++ 中也能实现类似的效果,我们就首先来尝试在 C++ 中实现这个 my_range 函数。

用 coroutine 实现 generator

在 main.cpp 中首先定义一个结构体:

struct generator {};

这里的 generator 这个名字不是固定的,可以修改。