This is actually a good question. I'm not sure the best way to approach it. All the ways I can think of are maintenance nightmares.
RTTI is one option. Another would be to have nested virtual calls:
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
|
class GeometricPlace
{
//...
// generic collision routine
virtual foo intersect(const GeometricPlace& v) const = 0;
// specific collision routines
virtual foo intersectfine(const CircleArc& v) const = 0;
virtual foo intersectfine(const LineSegment& v) const = 0;
virtual foo intersectfine(const SemiPlane& v) const = 0;
};
class CircleArc
{
//..
// generic collision routine
virtual foo intersect(const GeometricPlace& v) const { return v.intersectfine(*this); } // hand off to specific collision routine
// specific collision routines
virtual foo intersectfine(const CircleArc& v) const { /* do CircleArc/CircleArc collision */ }
virtual foo intersectfine(const LineSegment& v) const { /* do LineSegment/CircleArc collision */ }
virtual foo intersectfine(const SemiPlane& v) const { /* do SemiPlane/CircleArc collision */ }
};
// repeat for classes LineSegment, SemiPlane
| |
EDIT:
I wonder if there's a way to template that and make it easier. If I get some more time today I'll look at it more.
EDIT 2:
OK here's my new idea:
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
|
// global collision functions, using specific types
foo intersect(const CircleArc& a,const CircleArc& b);
foo intersect(const CircleArc& a,const LineSegment& b);
foo intersect(const CircleArc& a,const SemiPlane& b);
foo intersect(const LineSegment& a,const LineSegment& b);
foo intersect(const LineSegment& a,const SemiPlane& b);
foo intersect(const SemiPlane& a,const SemiPlane& b);
// ... add more as needed
// .. add transitive ones (LineSegment,CircleArc) and just call the matching one of the above
// fill out their bodies as you'd expect
class GeometricPlace
{
public:
virtual foo intersect(const GeometricPlace& b) const = 0;
virtual foo intersectfine(const CircleArc& b) const = 0;
virtual foo intersectfine(const LineSegment& b) const = 0;
virtual foo intersectfine(const SemiPlane& b) const = 0;
};
template <typename T>
class IntersectDispatch
{
public:
foo intersect(const GeometricPlace& b) const
{
return b.intersectfine(static_cast<const T&>(*this));
}
foo intersectfine(const CircleArc& b) const { return intersect( static_cast<const T&>(*this),b ); }
foo intersectfine(const LineSegment& b) const { return intersect( static_cast<const T&>(*this),b ); }
foo intersectfine(const SemiPlane& b) const { return intersect( static_cast<const T&>(*this),b ); }
};
class CircleArc : public GeometricPlace, public IntersectDispatch<CircleArc>
// derive from IntersectDispatch< _thisclass_ >
// so LineSegment would derive from IntersectDispatch<LineSegment>
{
// no need to write any intersect functions here
};
| |
The gimmick here is that GeometricPlace's virtual functions will get delegated to the sister class IntersectDispatch.
This still requires you to change multiple things (3) when you add a new Geometric type, but that's the best I can imagine. As a bonus, failure to add any one of the 3 will yield a compiler error instead of compiling and resulting in broken code.
The 3 things you'd have to change for adding new types:
- Add all appropriate global intersect() functions
- Add 1 pure virtual intersectfine() function to GeometricPlace
- Add 1 intersectfine() function to IntersectDispatch. Make it identical to the others, but using the appropriate type as the parameter