Visitor Implementation: Constant versus Mutable Visitor

497 views Asked by At

Given that the difference between a constant visitor and a mutable visitor is that the methods in a constant visitor are not allowed to modify the visited object.

class Integer_Field;
class Boolean_Field;
class Text_Field;

class Visitor_Constant
{
  public:
    virtual void visit(const Integer_Field& f) = 0;
    virtual void visit(const Boolean_Field& f) = 0;
    virtual void visit(const Text_Field& f) = 0;
};

class Visitor_Mutable
{
  public:
    virtual void visit(Integer_Field& f) = 0;
    virtual void visit(Boolean_Field& f) = 0;
    virtual void visit(Text_Field& f) = 0;
};

I would like to minimize support for these visitors. For example, if I come up with a class Blob_Field, I need to modify both classes. I would prefer to have something where I only have to modify one class or stencil.

The maintenance issue fans out when there are many classes defined from these parent visitors. This is the main reason I want to simplify the maintenance.

My questions:
(Note: This must be resolved without using C++11 features as my development environment does not support C++11 and I'm not allowed to upgrade at this time.)

  1. Is there a way to use the template mechanism to merge the two (such as supply 'const' as a parameter to the template)?
  2. How can I set up these visitors so that I can pass a Visitor_Constant to methods using a Visitor_Mutable?

Note: Combining these classes via a parent class, doubles the visitor methods that must be implemented and maintained.

Edit 1: Class relationships

class Component; // Base class for fields and records.
class Field : public Component; // Base class for all fields
class Record : public Component // Base class for all records
{
  std::vector< boost::shared_ptr<Component> > component_container;
};
class Integer_Field : public Field;
class Boolean_Field : public Field;
class Text_Field : public Field;

Edit 2: Rationality of fields
One rationality for the fields being treated specifically is the case of generating an SQL statement for creating a table.
Another is for loading fields from a database table.

1

There are 1 answers

0
mpark On BEST ANSWER

Is there a way to use the template mechanism to merge the two (such as supply 'const' as a parameter to the template)?

You can supply it through a template template parameter, and also keep the pure visitor implementation generic. I originally did this in C++11, but since you say you don't have it, I'll present one with type lists instead.

Here is the type list implementation.

/* Our empty node for our type list. */
class Empty {};

/* Cons for our type list. */
template <typename First_, typename Rest_>
class Cons {
  public:

  /* First type */
  typedef First_ First;

  /* Rest.  */
  typedef Rest_ Rest;

};  // Cons<First_, Rest_>

Here is the generic visitor implementation.

/* Forward declaration. */
template <template <typename> class Decorator, typename Members>
class VisitorRecur;

/* Base case. */
template <template <typename> class Decorator, typename Member>
class VisitorRecur<Decorator, Cons<Member, Empty> > {
  public:

  /* Pure virtual for each of the members. */
  virtual void operator()(
      typename Decorator<Member>::Type that) const = 0;

};  // VisitorRecur<Decorator, Member>

/* Recursive case. */
template <template <typename> class Decorator, typename Members>
class VisitorRecur
    : public VisitorRecur<Decorator, typename Members::Rest> {
  public:

  /* Bring the operator()s into scope. */
  using VisitorRecur<Decorator, typename Members::Rest>::operator();

  /* Pure virtual for each of the members. */
  virtual void operator()(
      typename Decorator<typename Members::First>::Type that) const = 0;

};  // VisitorRecur<Decorator, typename Members::Rest>

/* Final visitor. */
template <template <typename> class Decorator, typename Members>
class Visitor : public VisitorRecur<Decorator, Members> {
  public:

  /* Bring the operator()s into scope. */
  using VisitorRecur<Decorator, Members>::operator();

};  // Visitor<Decorator, Members>

Here are the two decorators that we'll define.

/* ConstRef. */
template <typename T>
class ConstRef {
  public:

  typedef const T & Type;

};  // ConstRef<T>

/* Ref. */
template <typename T>
class Ref {
  public:

  typedef T & Type;

};  // Ref<T>

Here is the use case for it.

/* Forward declarations. */
class Circle;
class Square;
class Triangle;

/* Add the members into a type list. */
typedef Cons<Circle, Cons<Square, Cons<Triangle, Empty> > > Members;

/* Our const visitor which accepts the members by const-ref. */
typedef Visitor<ConstRef, Members> ConstVisitor;

/* Our mutating visitor which accepts the members by ref. */
typedef Visitor<Ref, Members> MutatingVisitor;