Немного пропатчил версию c-smile:
http://www.rsdn.ru/forum/message/2965247.1.aspxАвтор: c-smile Дата: 27.05.08
Дабы пофиксить следующие вещи, которые всплыли при первой же попытке реально применить Generators:
1. Почему "do {...} while (gen);", а не "while (gen) {...};"? А если последовательность пустая?!
2. Попытка вызвать в теле цикла gen() несколько раз приводит в очень неинтуитивному результату. Хотелось бы что бы новый элемент получался только после прохождения итерации, т.е. после вызова gen.operator bool().
3. Необходимость передавать в макрос stop() реальный элемент. Очень не удобно. Очень. Выход из функции должен означать конец последовательности, никаких элементов уже не должно быть.
И ещё добавил, что можно писать return посередине функции генерации.
Использование теперь выглядит так:
struct node
{
node* left_;
node* right_;
value_t value_;
};
generator(iterate_tree_t, node*)
{
node* root;
node* curr;
std::vector<node*> node_queue;
public:
iterate_tree_t(node* root)
: root (root)
, curr ()
{}
emit(node*)
if (0 == root)
return; // <--- можно писать просто return - значит конец последовательности
curr = root;
node_queue.clear();
node_queue.reserve(128);
for (;;)
{
yield(curr);
if (curr->left_ && curr->right_)
{
node_queue.push_back(curr->right_);
curr = curr->left_;
}
else if (curr->left_)
{
curr = curr->left_;
}
else if (curr->right_)
{
curr = curr->right_;
}
else
{
if (node_queue.empty())
break;
curr = node_queue.back();
node_queue.pop_back();
}
}
stop_emit(); // <--- не надо передавать реальный элемент
};
int main()
{
iterate_tree_t iter (0);
// <--- проверка теперь вначале цикла - т.е. поддерживает пустые последовательности
while (iter.next())
// <--- в теле можно вызывать iter.value() несколько раз - перехода не след. элемент не будет
std::cout << "node: " << iter.value()->value_ << " " << iter.value()->value_ << std::endl;
std::cout << "[end]" << std::endl;
} |
Реализация:
template<typename derived_t, typename elem_t>
class generator_base
{
protected:
int generator_line;
elem_t generator_value;
public:
generator_base()
: generator_line()
{}
bool next()
{
int generator_cur_line = generator_line;
generator_line = 0;
static_cast<derived_t*>(this)->generate_value(generator_cur_line);
return 0 != generator_line;
}
elem_t const& value()
{
return generator_value;
}
};
#define generator(NAME, T) class NAME : public generator_base<NAME, T>
#define emit(T)\
void generate_value(int generator_cur_line)\
{ switch(generator_cur_line) { case 0:;\
/**/
#define stop_emit() }}
#define yield(V)\
do {\
generator_line=__LINE__;\
generator_value = (V);\
return;\
case __LINE__:;\
} while (0)\
/**/ |
Теперь стало использовать немного попроще. Вот, например, обход дерева в глубину.
Кстати, если нужны локальные переменные внутри функции генерации, то их можно брать в expression block {}.
generator(iterate_tree_rec_t, node*)
{
node* root;
std::vector<iterate_tree_rec_t> iter_queue;
public:
iterate_tree_rec_t(node* root)
: root (root)
{}
emit(node*)
if (0 == root)
return;
yield(root);
{
// <--- локальная переменная
size_t i = 0;
for (; i != root->children_.size(); ++i)
iter_queue.push_back(root->children_[i]);
}
while (iter_queue.size())
{
while (iter_queue.back().next())
yield(iter_queue.back().value());
iter_queue.pop_back();
}
stop_emit();
}; |
 | |