views:

1018

answers:

5

I have a question on C++ double dispatch. In the code below, I want the results from the second set to match the results from the first set.

I don't know the actual type (unless I try dynamic_cast) but I do know that the object inherited from the BaseClass type. What is the most efficient (performance-wise) way to accomplish this?

After googling around for a while I found out about double dispatch and the loki multimethods. The problem I have with the Shape examples is that in my application, Processor and BaseClass are entirely independent and don't have a common method that they can call in each other. Secondly, there is only one Processor (i.e. nothing inherits from it).

Thanks for any help.

#include <iostream>
#include <string>
using namespace std;

class BaseClass{
public:
       BaseClass(){}
       virtual void myFunction(){cout << "base myFunction called" << endl;}
};

class Derived1: public BaseClass{
public:
       Derived1():BaseClass(){}
       void myFunction(){cout << "Derived1 myFunction called" << endl;}
};


class Derived2: public BaseClass{
public:
       Derived2():BaseClass(){}
       void myFunction(){cout << "Derived2 myFunction called" << endl;}
};

class Derived3: public BaseClass{
public:
       Derived3():BaseClass(){}
       void myFunction(){cout << "Derived3 myFunction called" << endl;}

};

class Processor{
public:
       Processor(){}
       virtual void processObj(BaseClass* bc){cout << "got a base object" << endl; bc->myFunction();}
       virtual void processObj(Derived1* d1){cout << "got a derived1 object" << endl; d1->myFunction();}
       virtual void processObj(Derived2* d2){cout << "got a derived2 object" << endl; d2->myFunction(); }
};


int main() {
   BaseClass *bcp=new BaseClass();
   Derived1 *dc1p=new Derived1();   
   Derived2 *dc2p=new Derived2();
   Derived3 *dc3p=new Derived3();

   Processor p;//can also use Processor* p = new Processor()

   //first set results
   p.processObj(bcp);
   p.processObj(dc1p);
   p.processObj(dc2p);
   p.processObj(dc3p);

   BaseClass *bcp1=bcp;
   BaseClass *dc1p1=dc1p;   
   BaseClass *dc2p1=dc2p;
   BaseClass *dc3p1=dc3p;

   //second set results
   p.processObj(bcp1);
   p.processObj(dc1p1);
   p.processObj(dc2p1);
   p.processObj(dc3p1);

   return 0;
}
+1  A: 

You'll have to put a virtual method on BaseClass to call processObj from the derived classes.

class BaseClass{
public:
       BaseClass(){}
       virtual void ProcessThis(Processor &p) { p.processObj(this); }
       virtual void myFunction(){cout << "base myFunction called" << endl;}
};

class Derived1: public BaseClass{
public:
       Derived1():BaseClass(){}
       void ProcessThis(Processor &p) { p.processObj(this); }
       void myFunction(){cout << "Derived1 myFunction called" << endl;}
};

class Derived2: public BaseClass{
public:
       Derived2():BaseClass(){}
       void ProcessThis(Processor &p) { p.processObj(this); }
       void myFunction(){cout << "Derived2 myFunction called" << endl;}
};

class Derived3: public BaseClass{
public:
       Derived3():BaseClass(){}
       void ProcessThis(Processor &p) { p.processObj(this); }
       void myFunction(){cout << "Derived3 myFunction called" << endl;}

};

class Processor{
public:
       Processor(){}
       virtual void processObj(BaseClass* bc){cout << "got a base object" << endl; bc->myFunction();}
       virtual void processObj(Derived1* d1){cout << "got a derived1 object" << endl; d1->myFunction();}
       virtual void processObj(Derived2* d2){cout << "got a derived2 object" << endl; d2->myFunction(); }
};

int main() {
   BaseClass *bcp=new BaseClass();
   Derived1 *dc1p=new Derived1();   
   Derived2 *dc2p=new Derived2();
   Derived3 *dc3p=new Derived3();

   Processor p;//can also use Processor* p = new Processor()

   //first set results
   bcp->ProcessThis(p);
   dc1p->ProcessThis(p);
   dc1p->ProcessThis(p);
   dc3p->ProcessThis(p);

   BaseClass *bcp1=bcp;
   BaseClass *dc1p1=dc1p;   
   BaseClass *dc2p1=dc2p;
   BaseClass *dc3p1=dc3p;

   //second set results
   bcp1->ProcessThis(p);
   dc1p1->ProcessThis(p);
   dc2p1->ProcessThis(p);
   dc3p1->ProcessThis(p);

   Processor p2;
   bcp1->ProcessThis(p2);
   dc1p1->ProcessThis(p2);
   dc2p1->ProcessThis(p2);
   dc3p1->ProcessThis(p2);

   return 0;
}

You basically want the Visitor pattern, but with only one type of visitor. You might save yourself some future effort and make Processor into an abstract class and implement the one concrete ProcessorImpl class, making it trivial to add another processor class in the future, or you could wait until that situation comes up and leave things as is for now.

Eclipse
+4  A: 

The Visitor Pattern is designed just for handling this sort of situation.

Glomek
A: 

Thanks, but the base class doesn't have a processObj method and it doesn't know what Processor will get it.

I can have any number of Processor instances. The main() can call any one of them AFTER the BaseClass object was created. So I have no idea which Processor will work on it, so I can't pass it to the BaseClass at construction time

Am I missing something - if yes, can you give the code?

cppalphadev
You have to add the processObj method yourself, that's how double dispatch works. It will work with any number of processor instances.
Eclipse
+6  A: 

You missed the "double" part of the double dispatch.

The point of this pattern is to make sure that the right method of the processor is called - the method that accepts the right type. Since the processor is initially not aware of the type of the object that's passed to it, you need the object to tell the processor what its type is.

In essence, each object needs a virtual "processMe(Processor &p)" method, and the processor calls it. The implementation of processMe calls p.processObject(this). But this time around, "this" has the known type! So instead of infinite recursion, you end up with the right proceessObj called

Arkadiy
A: 

Thank you so much. This solved my problem and I understand what double dispatch means! Here is the complete code for posterity (somebody please teach me how to get the formatting right):

#include <iostream>
using namespace std;

class BaseClass;
class Derived1;
class Derived2;
class Derived3;

class Processor {
public:
       Processor(){}
       virtual void processObj(BaseClass* bc);
       virtual void processObj(Derived1* d1);
       virtual void processObj(Derived2* d2);
};


class BaseClass{
public:
       BaseClass(){}
       virtual void ProcessThis(Processor &p) { p.processObj(this); }
       virtual void myFunction(){cout << "base myFunction called" << endl;}
};

class Derived1: public BaseClass{
public:
       Derived1():BaseClass(){}
       void ProcessThis(Processor &p) { p.processObj(this); }
       void myFunction(){cout << "Derived1 myFunction called" << endl;}
};

class Derived2: public BaseClass{
public:
       Derived2():BaseClass(){}
       void ProcessThis(Processor &p) { p.processObj(this); }
       void myFunction(){cout << "Derived2 myFunction called" << endl;}
};

class Derived3: public BaseClass{
public:
       Derived3():BaseClass(){}
       void ProcessThis(Processor &p) { p.processObj(this); }
       void myFunction(){cout << "Derived3 myFunction called" << endl;}

};

void Processor::processObj(BaseClass* bc){cout << "got a base object" << endl; bc->myFunction();}
void Processor::processObj(Derived1* d1){cout << "got a derived1 object" << endl; d1->myFunction();}
void Processor::processObj(Derived2* d2){cout << "got a derived2 object" << endl; d2->myFunction(); }


int main() {
   BaseClass *bcp=new BaseClass();
   Derived1 *dc1p=new Derived1();   
   Derived2 *dc2p=new Derived2();
   Derived3 *dc3p=new Derived3();

   Processor p;//can also use Processor* p = new Processor()

   //first set results

   bcp->ProcessThis(p);
   dc1p->ProcessThis(p);
   dc2p->ProcessThis(p);
   dc3p->ProcessThis(p);

   BaseClass *bcp1=bcp;
   BaseClass *dc1p1=dc1p;   
   BaseClass *dc2p1=dc2p;
   BaseClass *dc3p1=dc3p;

   //second set results

   bcp1->ProcessThis(p);
   dc1p1->ProcessThis(p);
   dc2p1->ProcessThis(p);
   dc3p1->ProcessThis(p);

   Processor p2;
   bcp1->ProcessThis(p2);
   dc1p1->ProcessThis(p2);
   dc2p1->ProcessThis(p2);
   dc3p1->ProcessThis(p2);

   return 0;
}
cppalphadev