fastest way to get the type

Here's what I have:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Apple{...};
struct Banana : Apple{...};
struct Cyanide : Apple{...};
struct Donkey : Apple{...};
//and so on...

void Eat(Banana* a, Banana* b){...}
void Eat(Banana* a, Donkey* b){...}
void Eat(Cyanide* a, Banana* b){...}
//and so on...

int main(){
    Apple* aptr = new Cyanide;
    Apple* bptr = new Banana;
    //???
    return 0;
}

On line 15 I'd like to call the appropriate version of the Eat function. But I can't because I don't know what the underlying types of aptr and bptr are. The way I do it now is create an enumeration enum tp{ apple, banana, cyanide, donkey, ...}; and virtual int Type() that returns a specific value from the enum for each class and then if(aptr->Type() == cyanide && bptr->Type() == banana) Eat((Cyanide*)aptr, (Banana*)bptr);.
However his method requires tons of code, if you have many different Eat functions. Do you know any way to do this quicker?
Why do you have different Eat functions?
Use the OO idea of polymorphism. You're already kind of doing that in your main, but it will work much better with classes. Change your structs to classes, and implement a version of the Eat method for each of the derived classes. Check out: http://www.cplusplus.com/doc/tutorial/polymorphism/
Polymorphism can also be used with structs
@Bazzy
This is not the actual code I have. Apple, Banana, Cyanide, Donkey and Eat are there only because that's easier to understand then A B C D and E.

@sammy
I'm afraid you didn't understand me correctly. I'm already using a bunch of polymorphism and I know quite well how that works. As I've stated above, I already have this thing working, but in a rather painful way.
I think is a design flaw, it should be better if you had only one Eat function and use polymorphism in it
BTW you can try a dynamic cast, if it returns 0 means that it's not the right type
Well, most of my Eat functions do very different things. Of course I could do everything in one function, but I don't see how would that do any good. My plan was to use c++ mechanism of dealing with overloaded functions to do the part where I have to choose what will the Eat do.
Overloading won't work if you have pointers to the same base class
Sorry for my post before, it was a bit naive of me :). I am also curious about the design in this case, like Bazzy said. This thread talks about a similar situation, maybe it will help:

http://stackoverflow.com/questions/351845/finding-the-type-of-an-object-in-c
I know. That's exactly the problem I have.
I think I understand why that won't work. C++ deals with overloaded functions during compilation... So my plan won't work out.
I guess I'll have to do it the old way.
You can add an overload to the eat function taking pointers to the base class, in it use dynamic_cast and call the other overloads according to the result of the casts
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
Last edited on
Oh thanks, that's an interesting solution.
Thanks for raising the question, I'll likely use this some time..

With a reliable typeid, it's even easier...

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
template<class T> static const char* TypeName() { return typeid(T).name(); }
template<class T> const char* TypeName(const T* p) { return typeid(*p).name(); }

// should have a vtable for derived type recognition
struct Apple { virtual ~Apple() {} };

struct Banana : Apple { };
struct Donkey : Apple { };

void EatBananaDonkey(Apple*, Apple*) { cout << "Eat Banana, Donkey" << endl; }

struct EatDispatcher
{
	EatDispatcher()
	{
		dispatchMap[make_pair(TypeName<Banana>(),TypeName<Donkey>())] = EatBananaDonkey;
	}

	void Eat(Apple* a1, Apple* a2) 
	{
		dispatchMap[make_pair(TypeName(a1),TypeName(a2))](a1,a2);
	}

	map<pair<const char*,const char*>, void(*)(Apple*,Apple*) > dispatchMap;
};

Last edited on
This is the way with double dispatch and no typeinfo used ;-)
More code required but it is really fast and simple.

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
class Apple;
class Banana;
class Cyanide;

class Food
{
public:
	virtual void Eat(const Apple* f) const = 0;
	virtual void Eat(const Banana* f) const = 0;
	virtual void Eat(const Cyanide* f) const = 0;

	virtual void Eat(const Food* f) const = 0;
};

class Apple : public Food
{
public:
	virtual void Eat(const Apple* f) const; // Apple with Apple
	virtual void Eat(const Banana* f) const; // Apple with Banana
	virtual void Eat(const Cyanide* f) const; // Apple with poison!

	virtual void Eat(const Food* f) const
	{
		f->Eat(this);
	}
};

class Banana : public Food
{
public:
	virtual void Eat(const Banana* f) const; // Banana with Banana
	virtual void Eat(const Apple* f) const; // Banana with Apple
	virtual void Eat(const Cyanide* f) const; // Banana with poison!

	virtual void Eat(const Food* f) const
	{
		f->Eat(this);
	}
};

class Cyanide : public Food
{
public:
	virtual void Eat(const Apple* f) const; //Cyanide with Apple
	virtual void Eat(const Banana* f) const; //Cyanide with Banana
	virtual void Eat(const Cyanide* f) const; //Cyanide with Cyanide

	virtual void Eat(const Food* f) const
	{
		f->Eat(this);
	}
};

void Eat(const Food* a, const Food* b)
{
	a->Eat(b);
}
Last edited on
ha! next exercise...triple dispatch!
Topic archived. No new replies allowed.