Reputation: 679
First, sorry if this is a duplicate. I didn't see anything similar though.
I'm familiar with the visitor pattern and I'm trying to add a little bit more flexibility for the visitor on my graph. If I have node classes A,B,C, SubB (inherits B), I would like to be able to have a visitor that accepts B nodes and will automatically accept SubB nodes without knowing about them (defining the accept()).
The obvious benefit is that I can subclass all day and the visitor doesn't need to care about the defining a visit for those types.
I have code and results to demonstrate below. As you can see, here are the nodes it will visit.
new Node(), new ANode(), new BNode(), new CNode(), new BNode(), new SubBNode()
Basically the CountVisitor correctly reports it found 2 Bs, and I understand why. CountVisitor.visit(SubB&) isn't overridden and will redirect to Visitor.visit(SubB&) instead, thus skipping the count. However, I would like the functionality where it reports 3 ( 2 Bs + 1 SubB ) because SubB "is a" B. I've thought about this and I just can't figure out how to make the type system do it. How should I rearrange things to achieve that functionality? I'm also open to alternative patterns if that maintains the "is a" relationship but I do think the visitor would be perfect if this snag was resolved.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Visitor;
class Node
{
public:
vector<Node*> children;
virtual void accept(Visitor&);
};
class ANode : public Node
{
public:
virtual void accept(Visitor&);
};
class BNode : public Node
{
public:
virtual void accept(Visitor&);
};
class CNode : public Node
{
public:
virtual void accept(Visitor&);
};
//-- try inheritance
class SubBNode: public BNode
{
public:
virtual void accept(Visitor&);
};
//--
class Visitor
{
public:
virtual void visit(Node& n);
virtual void visit(ANode& n);
virtual void visit(BNode& n);
virtual void visit(CNode& n);
virtual void visit(SubBNode& n);
};
class CountVisitor : public Visitor
{
public:
virtual void visit(BNode& n);
int count = 0;
void print();
};
//---------------------------------------------
void Node::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void ANode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void BNode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void CNode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
void SubBNode::accept(Visitor& v){
cout << __PRETTY_FUNCTION__ << endl;
v.visit(*this);
}
// -----
void Visitor::visit(Node& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(ANode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(BNode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(CNode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
void Visitor::visit(SubBNode& n){
cout << __PRETTY_FUNCTION__ << "\t\tDEFAULT" << endl;
}
// -----
void CountVisitor::visit(BNode& n){
count++;
cout << __PRETTY_FUNCTION__ << "\t\tSPECIAL" << endl;
}
void CountVisitor::print(){
cout << "CountVisitor Found Bs: "<< count << endl;
}
// ====================================================
int main() {
cout << "======FLAT TEST======" << endl;
vector<Node*> nodes = {
new Node(),
new ANode(),
new BNode(),
new CNode(),
new BNode(),
new SubBNode()
};
cout << "--DEFAULT--" << endl;
Visitor v1;
for( Node* n : nodes ){
n->accept(v1);
}
cout << "--COUNT--" << endl;
CountVisitor cv1;
for( Node* n : nodes ){
n->accept(cv1);
}
cv1.print();
return 0;
}
RESULTS
======FLAT TEST======
--DEFAULT--
virtual void Node::accept(Visitor&)
virtual void Visitor::visit(Node&) DEFAULT
virtual void ANode::accept(Visitor&)
virtual void Visitor::visit(ANode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void Visitor::visit(BNode&) DEFAULT
virtual void CNode::accept(Visitor&)
virtual void Visitor::visit(CNode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void Visitor::visit(BNode&) DEFAULT
virtual void SubBNode::accept(Visitor&)
virtual void Visitor::visit(SubBNode&) DEFAULT
--COUNT--
virtual void Node::accept(Visitor&)
virtual void Visitor::visit(Node&) DEFAULT
virtual void ANode::accept(Visitor&)
virtual void Visitor::visit(ANode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void CountVisitor::visit(BNode&) SPECIAL
virtual void CNode::accept(Visitor&)
virtual void Visitor::visit(CNode&) DEFAULT
virtual void BNode::accept(Visitor&)
virtual void CountVisitor::visit(BNode&) SPECIAL
virtual void SubBNode::accept(Visitor&)
virtual void Visitor::visit(SubBNode&) DEFAULT
CountVisitor Found Bs: 2
Upvotes: 5
Views: 572
Reputation: 217810
You may add an additional layer, something like:
template <typename F>
class FunctorVisitor : public Visitor
{
public:
explicit FunctorVisitor (F& f) : f(f) {}
virtual void visit(Node& n) override { f(n);}
virtual void visit(ANode& n) override { f(n);}
virtual void visit(BNode& n) override { f(n);}
virtual void visit(CNode& n) override { f(n);}
virtual void visit(SubBNode& n) override { f(n);}
private:
F& f;
};
class CountVisitor
{
public:
void operator() (const Node& n) const {
cout << __PRETTY_FUNCTION__ << "\t\tDefault" << endl;
}
void operator() (const BNode& n) {
count++;
cout << __PRETTY_FUNCTION__ << "\t\tSPECIAL" << endl;
}
int count = 0;
void print() const {
cout << "CountVisitor Found Bs: "<< count << endl;
}
};
Upvotes: 2