Implementing the Visitor Pattern Using C++ Templates

Implementing the visitor pattern using C++ Templates

This can be done in C++11 using variadic templates. Continuing from Pete's answer:

// Visitor template declaration
template<typename... Types>
class Visitor;

// specialization for single type
template<typename T>
class Visitor<T> {
public:
virtual void visit(T & visitable) = 0;
};

// specialization for multiple types
template<typename T, typename... Types>
class Visitor<T, Types...> : public Visitor<Types...> {
public:
// promote the function(s) from the base class
using Visitor<Types...>::visit;

virtual void visit(T & visitable) = 0;
};

template<typename... Types>
class Visitable {
public:
virtual void accept(Visitor<Types...>& visitor) = 0;
};

template<typename Derived, typename... Types>
class VisitableImpl : public Visitable<Types...> {
public:
virtual void accept(Visitor<Types...>& visitor) {
visitor.visit(static_cast<Derived&>(*this));
}
};

Subclasses of Visitable:

class Mesh : public Object, public VisitableImpl<Mesh, Mesh, Text> {};
class Text : public Object, public VisitableImpl<Text, Mesh, Text> {};

A Visitor subclass:

class Renderer : public Visitor<Mesh, Text> {};

It's not clear what the value_type of your Scene container is but you need to obtain a reference or pointer to Visitable<Mesh, Text> on which to call accept:

for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
Visitable<Mesh, Text>& object = static_cast<Visitable<Mesh, Text>&>(*it);
if(pre_visit(object)) {
object.accept(*this);
post_visit(object);
}
}

Implement the visitor pattern with templates

Like user786653 says, the Curiously Recurring Template Pattern can solve this

template<typename Visitor, typename Derived>
struct ElementBase : public Base
{
void visit()
{
_e.visit(static_cast<Derived*>(this));
}

private:
Visitor _e;
};

// Atoms.
template<typename Visitor>
struct ElementA : public ElementBase<Visitor, ElementA<Visitor> >
{
ElementA() : a(5) {}

int a;
};

// Visitors.
struct VisitorA
{
void visit(ElementA<VisitorA> *a)
{
cout << a->a << endl;
}
};

How can I implement the visitor patter with return type in C++

If a visitor needs to return a value, it is normal to store the returned value in the visitor itself. Thus:

NodeVisitor<double> dv;
node->accept(dv);
double result = dv.result();

If you don't like the boilerplate, you can wrap it in a non-virtual member:

class model::VisitableNode {
public:
template<class T>
/* non-virtual */ T accept(NodeVisitor<T>& v) {
do_accept(v);
return v.result;
}
virtual void do_accept(NodeVisitorBase& v) = 0;
}

Generic visitor base class template in C++ - overload issue

The compiler doesn't know which base-class' visit function to call. See this question of mine. As such, as you correctly said, you need to make the functions available in the visitor class with using declarations. Sadly, you can't just use using visitor_base<T>::visit...;, as that is not a valid pattern. You have to recursively inherit from one base after another and everytime bring the base-class visits into the scope of the derived class:

template <typename T>
struct visitor_base {
virtual void visit(T&) {};
};

template <typename Head, typename... Tail>
struct recursive_visitor_base
: visitor_base<Head>
, recursive_visitor_base<Tail...>
{
using visitor_base<Head>::visit;
using recursive_visitor_base<Tail...>::visit;
};

template<class T>
struct recursive_visitor_base<T>
: visitor_base<T>
{
using visitor_base<T>::visit;
};

template <typename... T>
struct visitor
: recursive_visitor_base<T...>
{
using recursive_visitor_base<T...>::visit;
};

Live example on Ideone (I had to tweak the partial spec a bit, since GCC 4.5.1 is a bit buggy in that part. Clang compiles the code shown in this answer just fine). Output:

that was a base.
that was 42
that was 2.79
that was 2.79
that was a base.
=================
that was a base.
that was 1804289383
that was 8.46931e+08
that was 1.68169e+09
that was a base.

C++ Visitor Pattern with variable return types

Several options:

  • Don't return anything, handle the value inside the visitor. The visitor is completely free to store or handle any return value and type.

  • Return a variadic type (std::any or std::variant). Note that using them requires you to find out the actual type, which is kind-of similar to your original problem perhaps.

  • Inherit multiple IVisitor<> interfaces. This might be the easiest way even, although it's not necessary elegant.

visitor pattern for template derived classes

The problem is your Visitor class is tightly coupled with classes that derive from Element. As you expand your design this is going to get in the way more than it already is. You can reduce/eliminate the right coupling by providing a "destination" class that defines all the requirements of a visitable object. Since the name of a derived classes is a common attribute you can place the storage and access to it into the destination class as well.

// 1. Define out visitor and destination interfaces
struct Destination
{
Destination(const std::string& name) : name_(name) {}
virtual std::string ident() const { return name_; }

const std::string name_;
};

struct Visitor
{
virtual void visit(Destination *e) = 0;
};

This keeps the requirements of the visitor separate from the Element class which seems to be your intention. Then your This and That classes inherit from Destination and provide the necessary implementations.

// 2. Define our element and it's derived classes
class Element
{
public:
virtual void accept(class Visitor &v) = 0;
};

template <unsigned int N>
class This: public Element, public Destination
{
public:
This() : Destination("This") {}
virtual void accept(Visitor &v)
{
v.visit(this);
}
};

class That: public Element, public Destination
{
public:
That() : Destination("That") {}
virtual void accept(Visitor &v)
{
v.visit(this);
}
};

Now your up and down visitors are simplified into something like the following

// 3. Create a "visitor" derived class for each "operation" to do on "elements"
class UpVisitor: public Visitor
{
void visit(Destination *e) {
cout << "do Up on " + e->ident() << '\n';
}
};

class DownVisitor: public Visitor
{
void visit(Destination *e) {
cout << "do Down on " + e->ident() << '\n';
}
};

Although I did not change it in the solution above I recommend changing visit to take a reference instead of a pointer. Since C++ has no notion of a null reference this indicates that Destination is required where as a pointer could be considered optional.



Related Topics



Leave a reply



Submit