- A Problem Visitor
- A Better Visitor
- Solving D1, D2, and D3
- Download Sample Program
- Notes and References
Solving D1, D2, and D3
Even if D1 and D2 are the chief shortcomings of VP is interesting to notice that keeping ALG and SO together is still possible, even if it's extremely counterintuitive:
namespace Visitor_4 { struct Visitable { void visit() { accept(); } private: virtual void accept() = 0; }; struct BackwardsTraversal { BackwardsTraversal(Visitable* previous ) : previous_(previous) {} void traverse() { if (!previous_) return; previous_->visit(); } private: Visitable* previous_; }; template <class I, class TRAVERSAL> struct VisitorsGenerator { private: template <class T, class INFO, void (*FUN)(T&, INFO&), class TRAVERSE> struct VisitableAdapter : Visitable { VisitableAdapter(T& t, INFO& i, Visitable* previous = 0) : this_(t), info_(i), traversal_(previous) {} private: virtual void accept() { FUN(this_, info_); traversal_.traverse(); } private: T& this_; INFO& info_; TRAVERSE traversal_; }; public: template <class T, void (*FUN)(T&, I&)> Visitable* createVisitor(T& t, Visitable* previous = 0) { return new VisitableAdapter<T, I, FUN, TRAVERSAL>(t, info_, previous); } I& getInfo() { return info_; } private: I info_; }; struct Hammer {}; struct Drill {}; struct StateHolder { StateHolder() : i_(0) {} int i_; }; void hammer(Hammer & h, StateHolder& sh) { sh.i_ += 1; std::cout <<"hammer" << std::endl; } void drill(Drill & d, StateHolder& sh) { sh.i_ += 2; std::cout <<"drill" << std::endl; } struct Saw {}; void saw(Saw & s, StateHolder& sh) { sh.i_ += 4; std::cout <<"saw" << std::endl; } void alternate_hammer(Hammer & h, StateHolder& sh) { sh.i_ += 16; std::cout <<"alternate_hammer" << std::endl; } void doSomethingWithAllTools() { Hammer h; Drill d; VisitorsGenerator<StateHolder, BackwardsTraversal> gen; typedef std::auto_ptr<Visitable> VisitablePtr; VisitablePtr vah(gen.createVisitor<Hammer, hammer>(h)); VisitablePtr vad(gen.createVisitor<Drill, drill>(d, vah.get())); vad->visit(); std::cout <<"state is " << gen.getInfo().i_ << std::endl; VisitablePtr alternate_vah(gen.createVisitor<Hammer, alternate_hammer>(h, vah.get())); Saw s; VisitablePtr vas(gen.createVisitor<Saw, saw>(s, alternate_vah.get())); vad = VisitablePtr(gen.createVisitor<Drill, drill>(d, vas.get())); gen.getInfo().i_ = 0; vad->visit(); std::cout <<"state is " << gen.getInfo().i_ << std::endl; } }
The above is a more convoluted example based on external polymorphism. Not only does it take care of D1 and D2; it also puts visitor and visited together. In a nutshell, the Visitor class is factored in free functions (corresponding to member functions) and state (context), and this information is after that merged piece by piece with Visited classes, creating ad-hoc "classes." On top of this, Visitors can be created only by using a Generator taking traversal and context as policies. This mechanism enforces unique policies governing all Visitors.
And now the details:
- A state class is defined; this is actually the context shared by ALG when traversing SO.
- Free functions are created for each of the Visited classes, taking the above State as a parameter.
- Instantiate a VisitorsGenerator. It keeps State and Traversal, as well as an internal VisitableAdapter implementing the Visitable abstract class. This time Visitable has no relationship with the visited classes; it just enforces a common homogeneous interface to be used in traversal.
- VisitableAdapters keeping Visited and algorithm together are generated (seen outside generator as Visitable pointers). They share the same state and traversal; any attempt to use a different policy being flagged at compile-time.
We can add a new class, add a new algorithm for an existing class, and combine them in any way. Due to the fine granularity of the solution, users have a lot of flexibility in extending VP.
This concludes our visit in the Visitor's domain. All the idioms presented in this article can be packaged and reused any time the constraints of the original pattern make Visitor a less attractive or even impossible proposition.