I'm trying to add a visitor to an existing library. The visitor must be able to visit some classes from separate projects.
Let's say my solution have the following projects: ProjectA
, ProjectB
and they contain 1 class each respectively: NodeA
, NodeB
. Since the visitor is only an implementation of a class member outside of the class, I think its dependency won't allow it to be placed at another project. So if I want to create a visitor which can visit both NodeA
and NodeB
objects, I can't use a simple visitor.
Is this assessment correct?
I was thinking about a couple of solutions to overcome this.
- Move one of the classes to the other projectand implement visitor there (this isnot a feasible solution).
- Add an extra indirection. Create a class for the visitor implementation for each project. And create a "head visitor" which would use these implementation objects.
Am I missing something? Is there a better way to do this?
Please find my visitor implementation below. Please note that I removed header guards for the sake of simplicity.
/// --- Node.h in project A ---
class Visitor;
class Node {
public:
Node() = default;
virtual ~Node() = default;
virtual void accept(Visitor & v) = 0;
};
/// --- NodeA.h in project A ---
#include "Visitor.h"
class NodeA : public Node {
public:
void accept(Visitor & v);
};
/// --- NodeB.h in project B ---
#include "Visitor.h"
class NodeB : public Node {
public:
void accept(Visitor & v);
};
/// --- Visitor.h ---
#include "Node.h"
class NodeA;
class NodeB;
class Visitor {
public:
Visitor() = default;
virtual ~Visitor() = default;
virtual void visit(const Node & node);
virtual void visit(const NodeA & node);
virtual void visit(const NodeB & node);
};
// --- Visitor.cpp ---
#include "Visitor.h"
#include "NodeA.h"
void Visitor::visit(const Node & node)
{
// implementation
}
void Visitor::visit(const NodeA & node)
{
// implementation
}
void Visitor::visit(const NodeB & node)
{
// implementation
}
CodePudding user response:
If there is nothing in common between NodeA
and NodeB
other than being able to accept a visitor and store it in a, say, std::vector<std::unique_ptr<Node>>
consider using std::variant
instead.
The code looks something like this:
#include <variant>
#include <iostream>
#include <vector>
class NodeA {};
class NodeB {};
using Node = std::variant<NodeA, NodeB>;
class Visitor
{
public:
void operator()(NodeA & node)
{
std::cout << "encountered A" << std::endl;
}
void operator()(NodeB & node)
{
std::cout << "encountered B" << std::endl;
}
};
int main()
{
std::vector<Node> nodes;
nodes.emplace_back(NodeA{});
nodes.emplace_back(NodeA{});
nodes.emplace_back(NodeB{});
nodes.emplace_back(NodeA{});
nodes.emplace_back(NodeB{});
nodes.emplace_back(NodeB{});
Visitor visitor;
for (auto & node : nodes)
{
std::visit(visitor, node);
}
}
This way the implementation of NodeA
, NodeB
and the visitor is mostly decoupled.
CodePudding user response:
If I put a visitor interface class to the same project as Node and only use that in the derived node headers, I can put the derived Visitor classes to different projects, which will depend on everything (base and derived nodes and base visitor). The visit method will be dynamically dispached to the appropriate visitor instance.