Hi Hamsterman, I know where you are coming from! Double dispatch can be a real pain. If you have classes A,B,C add try to add D, using virtual methods means adding multiple methods to A,B and C as well as implementing D!
I think you are on the right track, I'd throw the enums away and use a custom runtime type identifier instead. Here's my solution, to add a new class you simply need to:
(1) Add two copy paste lines to the new class.
(2) Add one line into the dispatcher for each Eat function (90% copy paste)
(3) Write your eat function(s).
(4) You only need to add eat functions where it make sense (ie, if you don't want to eat(Donkey, Cyanide), don't implement it.
(btw, with this solution, you don't need to inherit everything from Apple, you could create an interface instead for the DynamicType method).
You could also change from using static int as the type identifer to using a char* with the actual class name, so you could print out the class name for debugging purposes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
#include <iostream>
#include <map>
using namespace std;
struct Apple
{
static void* TypeId(void) { static int i = 0; return &i; }
virtual void* DynamicType(void) const { return TypeId(); }
};
struct Banana : Apple {
static void* TypeId(void) { static int i = 0; return &i; }
virtual void* DynamicType(void) const { return TypeId(); }
};
struct Donkey : Apple {
static void* TypeId(void) { static int i = 0; return &i; }
virtual void* DynamicType(void) const { return TypeId(); }
};
void EatAppleApple(Apple*, Apple*)
{
cout << "Eat Apple, Apple" << endl;
}
void EatBananaDonkey(Apple*, Apple*)
{
cout << "Eat Banana, Donkey" << endl;
}
struct EatDispatcher
{
EatDispatcher()
{
dispatchMap[make_pair(Apple::TypeId(),Apple::TypeId())] = EatAppleApple;
dispatchMap[make_pair(Banana::TypeId(),Donkey::TypeId())] = EatBananaDonkey;
}
void Eat(Apple* a1, Apple* a2)
{
void(*f)(Apple*, Apple*) =
dispatchMap[make_pair(a1->DynamicType(),a2->DynamicType())];
// put some logic here to throw if not found
f(a1,a2); // call the eat function
}
map<pair<void*,void*>, void(*)(Apple*,Apple*) > dispatchMap;
};
int main()
{
Apple* aptr = new Apple;
Apple* bptr = new Banana;
Apple* cptr = new Donkey;
cout << aptr->DynamicType() << endl;
cout << bptr->DynamicType() << endl;
cout << cptr->DynamicType() << endl;
EatDispatcher dispatcher;
dispatcher.Eat(aptr, aptr);
dispatcher.Eat(bptr, cptr);
}
| |
Output is:
0x404040
0x404044
0x404048
Eat Apple, Apple
Eat Banana, Donkey
|