Справочная информация: Я пытаюсь выяснить, как реализовать продолжения / сопрограммы / генераторы (как бы ни называлось следующее), поставив эту игрушечную проблему. Средой является C ++ 11 на gcc 4.6 и linux 3.0 x86_64. Непереносимо, но использование внешней библиотеки (boost.coroutine, COROUTINE и т. Д.) Не допускается. Я думаю longjmp(3)
и / или makecontext(2)
и друзья могут помочь, но не уверены.
Описание:
Следующий игрушечный синтаксический анализатор должен анализировать последовательности as и bs одинаковой длины. т.е.
((a+)(b+))+
такой, что длина второго скобочного производства равна третьему.
Когда он находит производство (например, aaabbb), он выводит число a
s, которое он находит (например, 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;
}
Проблема:
Как видите, состояние consume
стека вызовов должно быть сохранено, когда вызывается yield
, а затем возвращается produce
. Когда produce
вызывается снова, consume
необходимо перезапустить, вернувшись с yield
. Задача состоит в том, чтобы изменить способ produce
вызовов consume
и внедрить yield
, чтобы они функционировали должным образом.
(Очевидно, переопределение потребления так, чтобы оно сохраняло и восстанавливало свое состояние, нарушало цель упражнения.)
Я думаю, что нужно сделать, это что-то вроде примера внизу справочной страницы makecontext: http://www.kernel.org/doc/man-pages/online/pages/man3/makecontext.3.html,, но не совсем понятно, как перевести это на эту проблему. (и мне нужно спать)
Решение:
(Спасибо Крису Додду за дизайн)
#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;
}