I just got what I really wanted with the cyclic visitor. I changed the Strict / Nonstrict enumerated type from my previous post so that it takes a Functor that allows to implement whatever behavior you want for the Nonstrict visitor. In this way you can create a cyclic visitor as follows:
// personalize the behavior of the non-strict visitor
template <class R>
struct Functor1 {
template <class T>
R operator()(T&) {
// do whatever with T, when the function is not implemented
// in the visitor
}
};
typedef visitor::Visitor<ReturnType, ClassList, Immutable, Functor1> ClassVisitor3;
// use strict visitor (compiler errors when R Visit(T&) not defined
// mutable
typedef visitor::Visitor<ReturnType, ClassList, Mutable, Strict> ClassVisitor4;
// immutable
typedef visitor::Visitor<ReturnType, ClassList, Immutable, Strict> ClassVisitor5;
I think this is much better than my previous post, even though I don't think anyone has read that yet. The code is given below.
Best regards,
Alejandro M. Aragón
#ifndef VISITOR_HPP_
#define VISITOR_HPP_
#include <Loki/Typelist.h>
namespace visitor {
using Loki::NullType;
using Loki::Typelist;
// visitor class template, adapted from the Andrei Alexandrescu's "Modern C++ Design"
enum Visit_type { Mutable, Immutable };
template <class T, typename R = void, Visit_type = Mutable>
class StrictVisitor;
template <class T, typename R>
class StrictVisitor<T, R, Mutable>
{
public:
typedef R ReturnType;
typedef T ParamType;
virtual ~StrictVisitor() {}
virtual ReturnType Visit(ParamType&) = 0;
};
template <class T, typename R>
class StrictVisitor<T, R, Immutable>
{
public:
typedef R ReturnType;
typedef const T ParamType;
virtual ~StrictVisitor() {}
virtual ReturnType Visit(ParamType&) = 0;
};
/// class template StrictVisitor (specialization)
template <class Head, class Tail, typename R>
class StrictVisitor<Typelist<Head, Tail>, R, Mutable>
: public StrictVisitor<Head, R, Mutable>, public StrictVisitor<Tail, R, Mutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
// using StrictVisitor<Head, R>::Visit;
// using StrictVisitor<Tail, R>::Visit;
};
template <class Head, typename R>
class StrictVisitor<Typelist<Head, NullType>, R, Mutable> : public StrictVisitor<Head, R, Mutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
using StrictVisitor<Head, R, Mutable>::Visit;
};
template <class Head, class Tail, typename R>
class StrictVisitor<Typelist<Head, Tail>, R, Immutable>
: public StrictVisitor<Head, R, Immutable>, public StrictVisitor<Tail, R, Immutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
// using StrictVisitor<Head, R>::Visit;
// using StrictVisitor<Tail, R>::Visit;
};
template <class Head, typename R>
class StrictVisitor<Typelist<Head, NullType>, R, Immutable> : public StrictVisitor<Head, R, Immutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
using StrictVisitor<Head, R, Immutable>::Visit;
};
////////////////////////////////////////////////////////////////////////////////
// class template NonStrictVisitor
// Implements non-strict visitation (you can implement only part of the Visit
// functions)
////////////////////////////////////////////////////////////////////////////////
template <class T, typename R = void, Visit_type V = Mutable, class F = DefaultFunctor<R> > class BaseVisitorImpl;
template <class Head, class Tail, typename R, Visit_type V, class F>
class BaseVisitorImpl<Typelist<Head, Tail>, R, V, F>
: public StrictVisitor<Head, R, V>, public BaseVisitorImpl<Tail, R, V, F> {
public:
typedef typename StrictVisitor<Head, R, V>::ParamType ParamType;
virtual R Visit(ParamType& h)
{ return F()(h); }
};
template <class Head, typename R, Visit_type V, class F >
class BaseVisitorImpl<Typelist<Head, NullType>, R, V, F> : public StrictVisitor<Head, R, V>
{
public:
typedef typename StrictVisitor<Head, R, V>::ParamType ParamType;
virtual R Visit(ParamType& h)
{ return F()(h); }
};
/// Visitor
template <class R>
struct Strict {};
template <typename R, class TList, Visit_type V = Mutable, template <class> class FunctorPolicy = DefaultFunctor>
class Visitor : public BaseVisitorImpl<TList, R, V, FunctorPolicy<R> > {
public:
typedef R ReturnType;
I like the idea to have a strict mode.
And in case of non-strict mode you could introduce a policy to switch between different fallback impls,
your mentioned template-template parameter.
What about reimplementing the visitor example with your code, then the differences become clearer?
Peter
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I came up with this little example that uses a small class list. Let's say your classes allow both strict and non-strict visit, which means that they define two accept virtual functions, depending on the type of the visitor:
// concrete non-strict visitor, only defines the Visit function for Class1
class VVisitor : public ClassVisitor{
ReturnType Visit( Class1&) {
cout<<"inside class 1"<<endl;
}
};
// concrete strict visitor, the code will not compile if Visit is not defined for every class
// in the list because of pure virtual Visit function
class SVisitor : public StrictClassVisitor {
ReturnType Visit(Class1&) {
cout<<"strict visit for class 1"<<endl;
}
ReturnType Visit(Class2&) {
cout<<"strict visit for class 2"<<endl;
}
ReturnType Visit(Class3&) {
cout<<"strict visit for class 3"<<endl;
}
};
// class hierarchy
struct Base {
virtual void accept(ClassVisitor& vis) = 0;
virtual ~Base() {}
};
// the following classes define accept function that accept the strict and
// the non-strict visitors
aaragon@~/Desktop/visitor$./a.out
inside class 1
visiting class 6Class2
visiting class 6Class3
strict visit for class 1
strict visit for class 2
strict visit for class 3
Alejandro M. Aragón
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello everyone,
I just got what I really wanted with the cyclic visitor. I changed the Strict / Nonstrict enumerated type from my previous post so that it takes a Functor that allows to implement whatever behavior you want for the Nonstrict visitor. In this way you can create a cyclic visitor as follows:
// defaults to mutable (non-const), do-nothing nonstrict visitor
typedef visitor::Visitor<ReturnType, ClassList> ClassVisitor1;
// do-nothing non-strict visitor for immutable objects (const)
typedef visitor::Visitor<ReturnType, ClassList, Immutable> ClassVisitor2;
// personalize the behavior of the non-strict visitor
template <class R>
struct Functor1 {
template <class T>
R operator()(T&) {
// do whatever with T, when the function is not implemented
// in the visitor
}
};
typedef visitor::Visitor<ReturnType, ClassList, Immutable, Functor1> ClassVisitor3;
// use strict visitor (compiler errors when R Visit(T&) not defined
// mutable
typedef visitor::Visitor<ReturnType, ClassList, Mutable, Strict> ClassVisitor4;
// immutable
typedef visitor::Visitor<ReturnType, ClassList, Immutable, Strict> ClassVisitor5;
I think this is much better than my previous post, even though I don't think anyone has read that yet. The code is given below.
Best regards,
Alejandro M. Aragón
#ifndef VISITOR_HPP_
#define VISITOR_HPP_
#include <Loki/Typelist.h>
namespace visitor {
using Loki::NullType;
using Loki::Typelist;
// visitor class template, adapted from the Andrei Alexandrescu's "Modern C++ Design"
enum Visit_type { Mutable, Immutable };
template <class T, typename R = void, Visit_type = Mutable>
class StrictVisitor;
template <class T, typename R>
class StrictVisitor<T, R, Mutable>
{
public:
typedef R ReturnType;
typedef T ParamType;
virtual ~StrictVisitor() {}
virtual ReturnType Visit(ParamType&) = 0;
};
template <class T, typename R>
class StrictVisitor<T, R, Immutable>
{
public:
typedef R ReturnType;
typedef const T ParamType;
virtual ~StrictVisitor() {}
virtual ReturnType Visit(ParamType&) = 0;
};
/// class template StrictVisitor (specialization)
template <class Head, class Tail, typename R>
class StrictVisitor<Typelist<Head, Tail>, R, Mutable>
: public StrictVisitor<Head, R, Mutable>, public StrictVisitor<Tail, R, Mutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
// using StrictVisitor<Head, R>::Visit;
// using StrictVisitor<Tail, R>::Visit;
};
template <class Head, typename R>
class StrictVisitor<Typelist<Head, NullType>, R, Mutable> : public StrictVisitor<Head, R, Mutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
using StrictVisitor<Head, R, Mutable>::Visit;
};
template <class Head, class Tail, typename R>
class StrictVisitor<Typelist<Head, Tail>, R, Immutable>
: public StrictVisitor<Head, R, Immutable>, public StrictVisitor<Tail, R, Immutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
// using StrictVisitor<Head, R>::Visit;
// using StrictVisitor<Tail, R>::Visit;
};
template <class Head, typename R>
class StrictVisitor<Typelist<Head, NullType>, R, Immutable> : public StrictVisitor<Head, R, Immutable>
{
public:
typedef R ReturnType;
typedef Head ParamType;
using StrictVisitor<Head, R, Immutable>::Visit;
};
////////////////////////////////////////////////////////////////////////////////
// class template NonStrictVisitor
// Implements non-strict visitation (you can implement only part of the Visit
// functions)
////////////////////////////////////////////////////////////////////////////////
template <class R>
struct DefaultFunctor {
template <class T>
R operator()(T&) { return R(); }
};
template <class T, typename R = void, Visit_type V = Mutable, class F = DefaultFunctor<R> > class BaseVisitorImpl;
template <class Head, class Tail, typename R, Visit_type V, class F>
class BaseVisitorImpl<Typelist<Head, Tail>, R, V, F>
: public StrictVisitor<Head, R, V>, public BaseVisitorImpl<Tail, R, V, F> {
public:
typedef typename StrictVisitor<Head, R, V>::ParamType ParamType;
virtual R Visit(ParamType& h)
{ return F()(h); }
};
template <class Head, typename R, Visit_type V, class F >
class BaseVisitorImpl<Typelist<Head, NullType>, R, V, F> : public StrictVisitor<Head, R, V>
{
public:
typedef typename StrictVisitor<Head, R, V>::ParamType ParamType;
virtual R Visit(ParamType& h)
{ return F()(h); }
};
/// Visitor
template <class R>
struct Strict {};
template <typename R, class TList, Visit_type V = Mutable, template <class> class FunctorPolicy = DefaultFunctor>
class Visitor : public BaseVisitorImpl<TList, R, V, FunctorPolicy<R> > {
public:
typedef R ReturnType;
template <class Visited>
ReturnType GenericVisit(Visited& host) {
StrictVisitor<Visited, ReturnType, V>& subObj = *this;
return subObj.Visit(host);
}
};
template <typename R, class TList, Visit_type V>
class Visitor<R, TList, V, Strict> : public StrictVisitor<TList, R, V> {
public:
typedef R ReturnType;
template <class Visited>
ReturnType GenericVisit(Visited& host) {
StrictVisitor<Visited, ReturnType, V>& subObj = *this;
return subObj.Visit(host);
}
};
} // namespace visitor
#endif /* VISITOR_HPP_ */
Hi Alejandro,
I like the idea to have a strict mode.
And in case of non-strict mode you could introduce a policy to switch between different fallback impls,
your mentioned template-template parameter.
What about reimplementing the visitor example with your code, then the differences become clearer?
Peter
Hi Peter, thanks for replying.
I came up with this little example that uses a small class list. Let's say your classes allow both strict and non-strict visit, which means that they define two accept virtual functions, depending on the type of the visitor:
#include <stdexcept>
#include <iostream>
#include <typeinfo>
#include "visitor.hpp"
using std::cout;
using std::endl;
class Base;
class Class1;
class Class2;
class Class3;
template <class R>
struct NonStrictVisitor {
template <class T>
R operator()(T& t) {
// whatever default behavior goes here
cout<<"visiting class "<<typeid(t).name()<<endl;
}
};
// list of classes to visit
typedef LOKI_TYPELIST_3(Class1, Class2, Class3) ClassList;
// non-strict visitor
typedef visitor::Visitor<void, ClassList, visitor::Mutable, NonStrictVisitor> ClassVisitor;
// strict visitor
typedef visitor::Visitor<void, ClassList, visitor::Mutable, visitor::Strict> StrictClassVisitor;
// concrete non-strict visitor, only defines the Visit function for Class1
class VVisitor : public ClassVisitor{
ReturnType Visit( Class1&) {
cout<<"inside class 1"<<endl;
}
};
// concrete strict visitor, the code will not compile if Visit is not defined for every class
// in the list because of pure virtual Visit function
class SVisitor : public StrictClassVisitor {
ReturnType Visit(Class1&) {
cout<<"strict visit for class 1"<<endl;
}
ReturnType Visit(Class2&) {
cout<<"strict visit for class 2"<<endl;
}
ReturnType Visit(Class3&) {
cout<<"strict visit for class 3"<<endl;
}
};
// class hierarchy
struct Base {
virtual void accept(ClassVisitor& vis) = 0;
virtual ~Base() {}
};
// the following classes define accept function that accept the strict and
// the non-strict visitors
struct Class1 : public Base {
virtual void accept(ClassVisitor& vis) {
vis.GenericVisit(*this);
}
virtual void strict_accept(StrictClassVisitor& vis) {
vis.GenericVisit(*this);
}
};
struct Class2 : public Base {
virtual void accept(ClassVisitor& vis) {
vis.GenericVisit(*this);
}
virtual void strict_accept(StrictClassVisitor& vis) {
vis.GenericVisit(*this);
}
};
struct Class3 : public Base {
virtual void accept(ClassVisitor& vis) {
vis.GenericVisit(*this);
}
virtual void strict_accept(StrictClassVisitor& vis) {
vis.GenericVisit(*this);
}
};
int main(int argc, char* argv[]) {
Class1 c1;
Class2 c2;
Class3 c3;
VVisitor vis;
c1.accept(vis);
c2.accept(vis);
c3.accept(vis);
SVisitor vis2;
c1.strict_accept(vis2);
c2.strict_accept(vis2);
c3.strict_accept(vis2);
return 0;
}
This code prints:
aaragon@~/Desktop/visitor$./a.out
inside class 1
visiting class 6Class2
visiting class 6Class3
strict visit for class 1
strict visit for class 2
strict visit for class 3
Alejandro M. Aragón
Is this added to the feature requests?