背景:我正在尝试通过提出这个玩具问题来弄清楚如何实现continuations / coroutines / generators(无论以下内容叫什么)。环境是C ++ 11在gcc 4.6和Linux 3.0 x86_64上。非可移植性很好,但不允许使用外部库(boost.coroutine,COROUTINE等)。我认为
longjmp(3)
和/或makecontext(2)
可能有所帮助,但不确定。
描述:
以下玩具解析器应该解析长度相等的a和b序列。也就是说
((a+)(b+))+
使得第二个括号内的内容长度等于第三个括号内的内容长度。
当它找到一个产生式(例如 aaabbb),它会输出它所找到的 a
的数量(例如 3)。
代码:
#include <stdlib.h>
#include <iostream>
using namespace std;
const char* s;
void yield()
{
// TODO: no data, return from produce
abort();
}
void advance()
{
s++;
if (*s == 0)
yield();
}
void consume()
{
while (true)
{
int i = 0;
while (*s == 'a')
{
i++;
advance();
}
cout << i << " ";
while (i-- > 0)
{
if (*s != 'b')
abort();
advance();
}
}
}
void produce(const char* s_)
{
s = s_;
// TODO: data available, continue into consume()
consume();
}
int main()
{
produce("aaab");
produce("bba");
produce("baa");
produce("aabbb");
produce("b");
// should print: 3 1 4
return 0;
}
问题:
您可以看到,当调用yield
并且produce
返回时,必须保存consume
的调用堆栈状态。 当再次调用produce
时,通过从yield
返回来重新启动consume
。挑战在于修改produce
调用consume
的方式,并实现yield
使其正常工作。
(显然,重新实现consume
以保存和重建其状态会破坏练习的目的。)
我认为需要做的是类似于makecontext手册页面底部的示例http://www.kernel.org/doc/man-pages/online/pages/man3/makecontext.3.html,但不清楚如何将其转换为此问题。 (而我需要睡觉了)
解决方案:
(感谢Chris Dodd的设计)
#include <stdlib.h>
#include <iostream>
#include <ucontext.h>
using namespace std;
const char* s;
ucontext_t main_context, consume_context;
void yield()
{
swapcontext(&consume_context, &main_context);
}
void advance()
{
s++;
if (*s == 0)
yield();
}
void consume()
{
while (true)
{
int i = 0;
while (*s == 'a')
{
i++;
advance();
}
cout << i << " ";
while (i-- > 0)
{
advance();
}
}
}
void produce(const char* s_)
{
s = s_;
swapcontext(&main_context, &consume_context);
}
int main()
{
char consume_stack[4096];
getcontext(&consume_context);
consume_context.uc_stack.ss_sp = consume_stack;
consume_context.uc_stack.ss_size = sizeof(consume_stack);
makecontext(&consume_context, consume, 0);
produce("aaab");
produce("bba");
produce("baa");
produce("aabbb");
produce("b");
// should print: 3 1 4
return 0;
}
longjmp
?我不知道有任何名为longjump
的函数。 - Ben Voigt