读 Coroutines and C++20 这篇文章时发现一段有意思的代码,用 C++ 模拟 python 中的 range 函数。核心是使用函数静态变量,但有些细节很有意思,记录一下。
第一段代码:
// A bad simulation of coroutine, no state saving
#include<iostream>
int range(int a, int b)
{
static long long int i = a-1;
for (;i < b;)
{
return ++i;
}
return 0;
}
int main()
{
int i;
for (; i=range(1, 5);)
std::cout << i << '\n';
return 0;
}
这里有几个细节:
- static 变量用来在多次调用中保存历史信息。
- main 函数中 for 循环除了实现打印多个值外,还利用 for 循环的判断机制,避免了打印完 4 后再打印一个 0。
这个函数也有缺点,因为 static 定义变量那一行只会执行一次,所以这个 range 函数重复调用是不符合预期的。所以作者又写了升级版:
// A better simulation of coroutine, state saving!!
#include<iostream>
int range(int a, int b)
{
static long long int i;
static int state = 0;
switch (state)
{
case 0: /* start of function */
state = 1;
for (i = a; i < b; i++)
{
return i; /* Returns control */
case 1:; /* resume control straight after the return */
}
}
state = 0;
return 0;
}
int main()
{
int i;
for (; i=range(1, 5);)
std::cout << i << '\n';
return 0;
}
这段代码除了上一段代码的核心细节点外,还增加了:
- static 变量
i
只有在 state==0 时才会赋值,因此 range 可以多次调用(但不可并发调用)。 - range 内的 for 循环体包括了
case 1
这一行,因此在范围内时每次 return 都是由return i
返回的。(for 循环体是能和 case 混合的!amazing!)
总结
利用 static 变量保存历史信息,for + switch 语句巧妙组合实现状态重置,for 语句中条件判断避免输出无用信息。