我想知道是否有一个好的设计模式或习惯来实现以下目标:
问题似乎在于,当我们仅调用visit
时,我们就失去了控制,无法暂时“返回”循环主体以对一个节点执行操作。看起来它应该在现实世界的程序中定期发生。知道如何解决吗?
最佳答案
在具有提供访问者接口(interface)的R树实现的现实环境中,我遇到了这个问题,而我需要一个迭代器接口(interface)。仅当您可以接受将所有结果存储在集合中时,上述Jerry的建议才有效。如果您的结果集很大并且您实际上不需要存储它们,则可能会导致大量的内存消耗。
可以肯定使用的一种解决方案是在一个单独的线程中启动访问者,并开始在条件变量中等待结果。进行拜访时,您将当前结果存储到共享的临时位置中,并使用另一个条件变量来等待下一个请求。在自己等待之前,您要向调用方(主)线程的条件变量发出信号。然后,实现迭代器接口(interface)的调用方可以返回存储在临时位置的值。在下一次迭代期间,它可以向访问者线程发出条件变量的信号,并独自等待下一个项目。不幸的是,如果您逐项执行此操作,则成本较高。您可以缓冲一些项目以提高性能。
我们真正需要的是一个额外的堆栈,并可以在两个上下文之间切换。协程提供了这种抽象。在C++中,boost::coroutine提供了一个干净的实现。下面,我提供了一个完整的示例,说明如何将访问者模式改编为迭代器模式。
#include <iostream>
#include <boost/bind.hpp>
#include <boost/coroutine/coroutine.hpp>
template<typename Data>
class Visitor
{
public:
virtual ~Visitor() { }
virtual bool visit(Data const & data) = 0;
};
template <typename Data>
class Visitable
{
public:
virtual ~Visitable() {}
virtual void performVisits(Visitor<Data> & visitor) = 0;
};
// Assume we cannot change the code that appears above
template<typename Data>
class VisitableIterator : public Visitor<Data>
{
private:
typedef boost::coroutines::coroutine<void()> coro_t;
public:
VisitableIterator(Visitable<Data> & visitable)
: valid_(true), visitable_(visitable)
{
coro_ = coro_t(boost::bind(&VisitableIterator::visitCoro, this, _1));
}
bool isValid() const
{
return valid_;
}
Data const & getData() const
{
return *data_;
}
void moveToNext()
{
if(valid_)
coro_();
}
private:
void visitCoro(coro_t::caller_type & ca)
{
ca_ = & ca;
visitable_.performVisits(*static_cast<Visitor<Data> *>(this));
valid_ = false;
}
bool visit(Data const & data)
{
data_ = &data;
(*ca_)();
return false;
}
private:
bool valid_;
Data const * data_;
coro_t coro_;
coro_t::caller_type * ca_;
Visitable<Data> & visitable_;
};
// Example use below
class Counter : public Visitable<int>
{
public:
Counter(int start, int end)
: start_(start), end_(end) {}
void performVisits(Visitor<int> & visitor)
{
bool terminated = false;
for (int current=start_; !terminated && current<=end_; ++current)
terminated = visitor.visit(current);
}
private:
int start_;
int end_;
};
class CounterVisitor : public Visitor<int>
{
public:
bool visit(int const & data)
{
std::cerr << data << std::endl;
return false; // not terminated
}
};
int main(void)
{
{ // using a visitor
Counter counter(1, 100);
CounterVisitor visitor;
counter.performVisits(visitor);
}
{ // using an iterator
Counter counter(1, 100);
VisitableIterator<int> iter(static_cast<Visitable<int>&>(counter));
for (; iter.isValid(); iter.moveToNext()) {
int data = iter.getData();
std::cerr << data << std::endl;
}
}
return EXIT_SUCCESS;
}