Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visitor Pattern in C++ with multiple visitable parameters

Consider the following hierarchy:

class Base
{
   virtual void Method() = 0;
   virtual void Accept(Visitor *iVisitor) = 0;
};
class Derived1: public Base
{
   virtual void Method(){//impl}
   virtual void Accept(Visitor *iVisitor)
   {
        iVisitor->Visit(this);
   }
};
class Derived2: public Base
{
   virtual void Method(){//impl}
   virtual void Accept(Visitor *iVisitor)
   {
       iVisitor->Visit(this);
   }
};

and the visitor class:

class VisitorInterface
{
    virtual void Visit(Derived1 * param);
    virtual void Visit(Derived2 * param);
}
class Visitor: public VisitorInterface
{
    void Visit(Derived1 * param){}
    void Visit(Derived2 * param){}
}

Usually I use the visitor pattern to achieve double dispatching when the overload method depends on the parameter type, but I have only the pointer to the base class.

For example:

void foo(Visitor *visitorPtr, Base * basePtr)
{
    basePtr->Accept(visitorPtr);    
} 

I think this is the only way to achieve double dispatching since the dynamic binding of virtual functions should happen only on the object upon which the method is called and not on its parameters (derived types).

Now I encountered a new situation, where I need a sort of Visit method overloading on multiple parameters. Something like this:

class VisitorInterfaceMultiple
{
    virtual void Visit(Derived1 * param1, Derived2 * param2);
    virtual void Visit(Derived2 * param1, Derived3 *param2);
}

I cannot use the classical visitor pattern solution because accept method is called on only one of the parameters.

My question is: does exist any similar visitor pattern solution, or something similar, that I could use in this situation? (I need to overload Visit with exact 2 parameters, no more than 2).

like image 295
Heisenbug Avatar asked Jul 06 '12 15:07

Heisenbug


1 Answers

I created for you "triple" dispatch pattern: http://ideone.com/FoXNW It is quite easy. Main part below:

class Derived1;
class Derived2;
class Visitor;
class Base
{
public:
   virtual void Accept(Visitor &iVisitor, Base& param1) = 0;
   virtual void Accept(Visitor &iVisitor, Derived1& param2) = 0;
   virtual void Accept(Visitor &iVisitor, Derived2& param2) = 0;
};

class Visitor
{
public:
    virtual void Visit(Derived1 & param1, Derived1 &param2) { cout << "11\n"; }
    virtual void Visit(Derived1 & param1, Derived2 &param2) { cout << "12\n"; }
    virtual void Visit(Derived2 & param1, Derived1 &param2) { cout << "21\n"; }
    virtual void Visit(Derived2 & param1, Derived2 &param2) { cout << "22\n"; }
};

class Derived1: public Base
{
public:
   virtual void Accept(Visitor &iVisitor, Base& param1) 
   { param1.Accept(iVisitor, *this); }
   virtual void Accept(Visitor &iVisitor, Derived1& param2)
   { iVisitor.Visit(*this, param2); }
   virtual void Accept(Visitor &iVisitor, Derived2& param2)
   { iVisitor.Visit(*this, param2); }
};
class Derived2: public Base
{
public:
   virtual void Accept(Visitor &iVisitor, Base& param1) 
   { param1.Accept(iVisitor, *this); }
   virtual void Accept(Visitor &iVisitor, Derived1& param2)
   { iVisitor.Visit(*this, param2); }
   virtual void Accept(Visitor &iVisitor, Derived2& param2)
   { iVisitor.Visit(*this, param2); }
};

void Visit(Visitor& visitor, Base& param1, Base& param2)
{
   param2.Accept(visitor, param1);
}

Note that implementation of Derived1 and Derived2 is literally identical. You can enclose this in macro if you have more derived.

like image 74
PiotrNycz Avatar answered Oct 01 '22 09:10

PiotrNycz