Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I had this problem in a real-world setting with an R-tree implementation that provided a visitor interface, whereas I needed an iterator interface. The suggestion by Jerry above works only if you can accept storing all the results in a collection. That may result in high memory consumption if your result set is huge and you don't really need to store them.</p> <p>One solution that will work for sure is to launch the visitor in a separate thread and start waiting on a conditional variable for the results. When a visit call is made, you store the current result into a shared temp location, and use another conditional variable to wait for the next request. You signal the caller (main) thread's conditional variable before you wait on your own. The caller, which is implementing the iterator interface can then return the value stored at the temp location. During the next iteration, it could signal the visitor thread's conditional variable, and wait on its own for the next item. Unfortunately, this is somewhat costly if you do it on a per-item basis. You can buffer some items to improve the performance. </p> <p>What we really need is an extra stack and to alternate between two contexts. This abstraction is provided by coroutines. In C++, boost::coroutine provides a clean implementation. Below I include a full example of how visitor pattern can be adapted into an iterator pattern.</p> <pre><code>#include &lt;iostream&gt; #include &lt;boost/bind.hpp&gt; #include &lt;boost/coroutine/coroutine.hpp&gt; template&lt;typename Data&gt; class Visitor { public: virtual ~Visitor() { } virtual bool visit(Data const &amp; data) = 0; }; template &lt;typename Data&gt; class Visitable { public: virtual ~Visitable() {} virtual void performVisits(Visitor&lt;Data&gt; &amp; visitor) = 0; }; // Assume we cannot change the code that appears above template&lt;typename Data&gt; class VisitableIterator : public Visitor&lt;Data&gt; { private: typedef boost::coroutines::coroutine&lt;void()&gt; coro_t; public: VisitableIterator(Visitable&lt;Data&gt; &amp; visitable) : valid_(true), visitable_(visitable) { coro_ = coro_t(boost::bind(&amp;VisitableIterator::visitCoro, this, _1)); } bool isValid() const { return valid_; } Data const &amp; getData() const { return *data_; } void moveToNext() { if(valid_) coro_(); } private: void visitCoro(coro_t::caller_type &amp; ca) { ca_ = &amp; ca; visitable_.performVisits(*static_cast&lt;Visitor&lt;Data&gt; *&gt;(this)); valid_ = false; } bool visit(Data const &amp; data) { data_ = &amp;data; (*ca_)(); return false; } private: bool valid_; Data const * data_; coro_t coro_; coro_t::caller_type * ca_; Visitable&lt;Data&gt; &amp; visitable_; }; // Example use below class Counter : public Visitable&lt;int&gt; { public: Counter(int start, int end) : start_(start), end_(end) {} void performVisits(Visitor&lt;int&gt; &amp; visitor) { bool terminated = false; for (int current=start_; !terminated &amp;&amp; current&lt;=end_; ++current) terminated = visitor.visit(current); } private: int start_; int end_; }; class CounterVisitor : public Visitor&lt;int&gt; { public: bool visit(int const &amp; data) { std::cerr &lt;&lt; data &lt;&lt; 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&lt;int&gt; iter(static_cast&lt;Visitable&lt;int&gt;&amp;&gt;(counter)); for (; iter.isValid(); iter.moveToNext()) { int data = iter.getData(); std::cerr &lt;&lt; data &lt;&lt; std::endl; } } return EXIT_SUCCESS; } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload