Valid
	XHTML 1.1! Valid CSS!
Created 2004-12-18   Modified 2009-04-11
Chelton Evans

proj One Way Visitor Pattern home

Intro
Description
Source
Example
Interpreter
Hookup
Real Time Type Information (RTTI)

See Simulating Templated Virtual Functions for another technique related to the visitor pattern.

Intro

This is a variation on the Visitor pattern. This is a pattern which I discovered myself in the early 90's just because I do those sort of things. It addresses the primary shortcomings of the visitor pattern.

In the visitor pattern there are two trees - the data on one tree and operators on the other. The data tree is concrete and does not change, whereas the operator tree grows over time.

In my pattern there is not two separate data structure trees, all are on the one tree. Both data and functions can be added over time.

Source

Files

Makefile
main.cpp
visitbase.h
visitdataA.h
visitdataB.h
visitdataC.cpp
visitdataC.h
visitprint.cpp
visitprint.h

projcompile.txt
unittestsreport.txt

Doxygen

main.cpp
Makefile
visitbase
visitdataA
visitdataB
visitdataC
visitprint

Description

I originally called this pattern "One Way Visitor Pattern" because of the two objects, only one needs to know about the other to define their relationship (excluding trivial ignore operation case). Please email me if you know the official name of this pattern.

When I wrote this I was clearly thinking of the operators on one tree and the data structures on another even though both are on the same tree.

This however is only a perspective. It is quite possible to define ordered operation, operations on anything so their are no function or data, just well call them objects.

All objects accept all other objects - whether they respond is another question.

The matching of objects is a binary operation. That is not one but two matchings may be attempted if the first was unsuccessful.

The reason for this is that later another object can define the behavior. eg a library writer writes something and the client can write additional library components that work with the old ones and new ones too.

Applications for the pattern are that a system of functions and data could evolve over time. Both data and functions can be extended whereas the visitor pattern extends only functions.

Example

Objects A and B are part of a library and are data. The client adds a print function. Later another data C is added. C also needs to print but without touching the print objects code.

time

This separation allows a library of data and functions to be written that can be extended by the client.

Interpreter

The visitor pattern matches functions to data by passing the data's type to the function/operator. This pattern does not pass the type, rather a pointer which has its type found using RTTI is used. So all operators need only one function interface - that of accepting a base pointer.

Since the type is determined dynamically the accept method in the visitor pattern is obsolete - one less logic level to handle.

class visitbase
{
public:

  /** The default call says that w operator/data was not matched. */
  virtual bool const visit( visitbase & w ) 
    { return false; }

  /** Binary operator between the two objects. 
      Return true when a match was found (the expected case).*/
  bool const operator()(visitbase& x)
  {
    if( this->visit(x) )
      return true;
    return x.visit(*this);
  }
  bool const operator()(visitbase* x)
  {
    assert(x!=0);

    if( this->visit(*x) )
      return true;
    return x->visit(*this);
  }

  /** Cleanup */
  virtual ~visitbase() {}

};

The two orders F X and X F are tried. This is to ensure that at a later point in time if one object does not know of the others existence, the second object defines its relationship with the first.

For example
print C
will work when print knows nothing of C because C implements the behavior. If this was not done the client would have to be know the order themselves and implement
C print

Hookup

Catching or matching the operator to the data I used RTTI.

bool const visitprint::visit( visitbase & x)
{
  if (typeid(x)==typeid(visitdataA))
    return visit( (visitdataA&)x );

  if (typeid(x)==typeid(visitdataB))
    return visit( (visitdataB&)x );

  return false;
}

bool const visitprint::visit( visitdataA & a)
{...

This is case based. It forwards the work to the appropriate function when the type is identified.

Although case base systems traditionally have been criticized for unfriendly code this is perhaps more a hangup of the past. Java uses it a lot and I found it to be very good.

Real Time Type Information (RTTI)

I implemented this pattern about 1993 when RTTI in C++ was not around(on my compiler), so put an unsigned int in each class and implemented my own RTTI.

So this time around I thought I might use C++'s RTTI and see what differences would result in the code implementations.

The most striking difference is in the simplicity. While RTTI in C++ is basic it does the job - I do not know how efficiently. But from a coding perspective its much easier and cleaner.