And*_*zos 5 c++ linux gcc coroutine c++11
背景:我试图通过提出这个玩具问题来弄清楚如何实现continuation/coroutines/generator(无论以下是什么).环境是gcc 4.6和linux 3.0 x86_64上的C++ 11.非便携式很好,但不允许使用外部库(boost.coroutine,COROUTINE等).我认为longjmp(3)和/或makecontext(2)朋友可能会帮助但不确定.
描述:
以下玩具解析器应该解析as和bs等长的序列.即
((a+)(b+))+
Run Code Online (Sandbox Code Playgroud)
使得第二次包围生产的长度等于第三个.
当它找到生产(例如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;
}
Run Code Online (Sandbox Code Playgroud)
问题:
如您所见,consume调用堆栈的状态必须在调用时保存yield,然后produce返回.当produce再次调用时,consume必须通过返回来重新启动yield.挑战在于修改produce调用的方式consume,并实现yield它们按预期运行.
(显然重新实现消耗以便它能够保存并重建其状态,从而失去了练习的目的.)
我认为需要做的是类似于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;
}
Run Code Online (Sandbox Code Playgroud)
使用 makecontext/swapcontext 来实现这一点相当简单——您使用 makecontext 创建一个新的协程上下文,并使用 swapcontext 在它们之间进行交换。在您的情况下,您需要一个额外的协程来运行consume无限循环,并且您在主上下文中运行 main 并生成。
因此main应该调用 getcontext+makecontext 来创建一个将运行消费循环的新上下文:
getcontext(&consume_ctxt);
// set up stack in consume_context
makecontext(&consume_ctxt, consume, 0);
Run Code Online (Sandbox Code Playgroud)
然后produce将切换到它而不是consume直接调用:
void produce(const char* s_)
{
s = s_;
swapcontext(&main_ctxt, &consume_ctxt);
}
Run Code Online (Sandbox Code Playgroud)
最后yield只是调用swapcontext(&consume_ctxt, &main_ctxt);切换回主上下文(它将继续produce并立即返回)。
请注意,由于consume是无限循环,因此您无需过多担心它返回时会发生什么(因此该链接永远不会被使用)
| 归档时间: |
|
| 查看次数: |
2316 次 |
| 最近记录: |