我想知道是否有一个好的设计模式或习惯来实现以下目标:



问题似乎在于,当我们仅调用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;
}

10-07 19:07