-
May 10th, 2010, 12:19 PM
#1
Function overloading
Hi to everyone,
I have a little problem with my classes but before describing the problem I wish to ask forgiveness if similar questions were made before. This forum is huge and I'm unsure what to search so my apologize if made another useless post.
The problem is this one:
I have an abstract class named Collision wich have a pure virtual method "collide" which get a Collision* in input. I also have two implementations: CollisionBox and CollisionSphere. Both of these class implement the "collide" method once for each kind of collision (Box to Box, Sphere to Sphere, Box to Sphere).
Now the problem is: if I implement the "collide" method like these way (once in CollisionBox and then again in CollisionSphere):
Code:
bool CollideBox::collide(Collision* collident) {...}
bool CollideBox::collide(CollisionBox* collident) {...}
bool CollideBox::collide(CollisionSphere* collident) {...}
I usually have a Collision* pointer to pass to these method so always the first method will be called unless I dynamic cast the pointer to the right class... for example if I do:
Code:
CollisionBox* box = new CollisionBox();
Collision* generic = new CollisionBox();
box->collide(generic);
I will get the first method to be called but I wish the second one (collide(CollisionBox*) ) to be called.
Now actually I had to put a brutal switch and many dynamic casts in the general method (collide(Collision*)) which cast the pointer and call again the (hopefully) right "collide" method.
Is there any other non-brutal way or pattern to create these cross calls between the implementations of the classes? I'm going to add more classes and I cannot make so many method whenever I add, move or edit a class.
Thank you for the answers. I hope I was clear enough to explain my problem, if not then tell me: I'll try to make a better explanation.
-
May 10th, 2010, 02:22 PM
#2
Re: Function overloading
What happens if you do it the other way around? Make collide a virtual member of Collision and pass it a CollideBox as a parameter.
-
May 10th, 2010, 03:47 PM
#3
Re: Function overloading
I've never seen anyone try to make overloaded functions with subclasses where the superclass was also used in an overloaded function. I think the easiest way to make this work is to get rid of either
collide(Collision* collident)
OR
the other two methods.
I don't see any other feasible solution.
-
May 10th, 2010, 04:15 PM
#4
Re: Function overloading
This sounds tailor-made for a double-dispatch solution. Perhaps not precisely the visitor pattern, but similar.
First, you'll want to re-order your overloads so that the base class overload comes last. I don't know if that will make a difference, but I think it might. You want the others to be preferred when possible.
Second, define the base class version like so:
Code:
bool CollideBox::collide(Collision* other)
{
other->collide(this);
}
Now, it make take two collide() calls to fully resolve what's going on, but you can be sure that each call will resolve exactly one base class pointer into its concrete type via the virtual function table.
Unfortunately, even though each class derived from Collision must have the same implementation (above) in the generic case, I think you can't just write it once in the base class in this case. We're relying here on "this" being passed as the derived type, after all.
-
May 12th, 2010, 09:56 AM
#5
Re: Function overloading
First, thank you all for your answers.
Originally Posted by GCDEF
What happens if you do it the other way around? Make collide a virtual member of Collision and pass it a CollideBox as a parameter.
What do you mean? I'm not sure to understand clearly. "collide" is virtual (pure, thought).
Originally Posted by Bluefox815
I've never seen anyone try to make overloaded functions with subclasses where the superclass was also used in an overloaded function. I think the easiest way to make this work is to get rid of either
collide(Collision* collident)
OR
the other two methods.
I don't see any other feasible solution.
Which others two methods? The ones I suggested? To dispatch everything in every subclass?
Originally Posted by Lindley
This sounds tailor-made for a double-dispatch solution. Perhaps not precisely the visitor pattern, but similar.
First, you'll want to re-order your overloads so that the base class overload comes last. I don't know if that will make a difference, but I think it might. You want the others to be preferred when possible.
Second, define the base class version like so:
Code:
bool CollideBox::collide(Collision* other)
{
other->collide(this);
}
Now, it make take two collide() calls to fully resolve what's going on, but you can be sure that each call will resolve exactly one base class pointer into its concrete type via the virtual function table.
Unfortunately, even though each class derived from Collision must have the same implementation (above) in the generic case, I think you can't just write it once in the base class in this case. We're relying here on "this" being passed as the derived type, after all.
This sounds a good idea but I'm unsure how to implement it correctly, in my tests this I've got only a infinite recurive calling to the same method... which lead to a stack overflow (and a big red warning from my firewall).
Of course I could remove the responsability to check the collision from these classes and make another classe which dispatch the message correctly but (even if I think I'll have to do this way) I don't like it. First I have to move the responsabilities of the collision away from the collision classes and also I have to expose the internal method and/or data to this new class...
I think I'll try to find another solutions, if possible.
Last edited by BlueAngelTC; May 12th, 2010 at 09:58 AM.
Reason: wrong tipinh
-
May 12th, 2010, 10:48 AM
#6
Re: Function overloading
Originally Posted by BlueAngelTC
This sounds a good idea but I'm unsure how to implement it correctly, in my tests this I've got only a infinite recurive calling to the same method... which lead to a stack overflow (and a big red warning from my firewall).
Then you probably didn't override collide(Collision* other) in each subclass as Lindley pointed out.
Of course I could remove the responsability to check the collision from these classes and make another classe which dispatch the message correctly but (even if I think I'll have to do this way) I don't like it. First I have to move the responsabilities of the collision away from the collision classes and also I have to expose the internal method and/or data to this new class...
In my opinion it would be better to keep the logic you are trying to implement out of these classes. The key characteristic is that you always need to know about two objects in order to evaluate a collision. That means that each class must know about each other class in your original design. So your argument of not having to expose internal data doesn't hold, because a CollisionBox needs to have access to a CollisionShpere (and all other types) anyway.
I would use the collision classes to dispatch these calls to some sort of collision manager that has knowledge of all types. In the collision manager you can implement the logic for each pair of types. Much easier to manage if you ask me.
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
May 12th, 2010, 11:06 AM
#7
Re: Function overloading
Originally Posted by D_Drmmr
Then you probably didn't override collide(Collision* other) in each subclass as Lindley pointed out.
I did it, the collide(Collision*) in every sub class and then in every sub class the othe methods:
collide(CollisionBox*)
collide(CollisionSphere*)
Originally Posted by D_Drmmr
In my opinion it would be better to keep the logic you are trying to implement out of these classes. The key characteristic is that you always need to know about two objects in order to evaluate a collision. That means that each class must know about each other class in your original design. So your argument of not having to expose internal data doesn't hold, because a CollisionBox needs to have access to a CollisionShpere (and all other types) anyway.
This isn't necessary true: when I collide a box with a sphere I have to calculate a point and pass it to the sphere without expose the internal data of the box to the sphere.
Anyway you are right: in other cases this could lead to expose data to the other implementations.
I will solve my problem maybe this way but is there a solution for the problem as I described?
-
May 12th, 2010, 11:42 AM
#8
Re: Function overloading
Originally Posted by BlueAngelTC
I will solve my problem maybe this way but is there a solution for the problem as I described?
Can you provide a minimal complete example of the code that gives you infinite recursion?
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
May 12th, 2010, 12:00 PM
#9
Re: Function overloading
Astratta.h
Code:
#ifndef ASTRATTA_H_
#define ASTRATTA_H_
class Astratta
{
public:
Astratta();
virtual ~Astratta();
virtual void test(Astratta* test) { test->test(this); }
};
#endif /* ASTRATTA_H_ */
BetaImpl.h
Code:
#ifndef BETAIMPL_H_
#define BETAIMPL_H_
#include "Astratta.h"
class ImplOne;
class BetaImpl : public Astratta
{
public:
BetaImpl();
virtual ~BetaImpl();
void test(BetaImpl* boh);
void test(ImplOne* boh);
void test(Astratta* boh) { boh->test(this); }
void arconte();
};
#endif /* BETAIMPL_H_ */
BetaImpl.cpp
Code:
#include "BetaImpl.h"
#include "ImplOne.h"
#include <iostream>
using std::cout;
BetaImpl::BetaImpl()
{
}
void BetaImpl::test(BetaImpl* boh)
{
cout << "Beta: BetaImpl.\n";
}
void BetaImpl::test(ImplOne* boh)
{
cout << "Beta: ImplOne.\n";
BetaImpl::~BetaImpl()
{
}
ImplOne.h
Code:
#ifndef IMPLONE_H_
#define IMPLONE_H_
#include "Astratta.h"
class BetaImpl;
class ImplOne : public Astratta
{
public:
ImplOne();
virtual ~ImplOne();
void test(BetaImpl* boh);
void test(ImplOne* boh);
void test(Astratta* boh) { boh->test(this); }
};
#endif /* IMPLONE_H_ */
ImplOne.cpp:
Code:
#include "ImplOne.h"
#include "BetaImpl.h"
#include <iostream>
using std::cout;
ImplOne::ImplOne()
{
}
void ImplOne::test(BetaImpl* boh)
{
cout << "Impl: BetaImpl.\n";
}
void ImplOne::test(ImplOne* boh)
{
cout << "Impl: ImplOne.\n";
}
ImplOne::~ImplOne()
{
}
main.cpp:
Code:
#include "Astratta.h"
#include "BetaImpl.h"
#include "ImplOne.h"
BetaImpl* beta = new BetaImpl();
ImplOne* alfa = new ImplOne();
cout << "Alfa...\n";
beta->test(alfa);
cout << "Beta...\n";
beta->test(beta);
cout << "Gamma...\n";
Astratta* astr = new ImplOne();
beta->test(astr); // <--- CRASH
cout << "Salto!\n";
delete beta;
delete alfa;
-
May 12th, 2010, 12:22 PM
#10
Re: Function overloading
You need to provide (virtual) overloads of test for each derived class in Astratta. Else, it will always call the overload taking a Astratta*, because that is the only one available in the base class.
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
May 12th, 2010, 12:29 PM
#11
Re: Function overloading
Do you mean I should include each of the derived class in the base class? In Astratta?
This is too ugly. It goes against every good rule of programming.
I will use another class as dispatcher instead.
Thank you for your replies.
Last edited by BlueAngelTC; May 12th, 2010 at 01:59 PM.
Reason: wrong tiping again
-
May 12th, 2010, 07:38 PM
#12
Re: Function overloading
Yes, I'm not a fan of that aspect of the visitor pattern.
However, using forward declarations it isn't *all* that bad....
Code:
class BetaImpl;
class ImplOne;
class Astratta
{
public:
Astratta();
virtual ~Astratta();
virtual void test(BetaImpl *t) {}
virtual void test(ImplOne *t) {}
virtual void test(Astratta* test) = 0;
};
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|