Executing specialized code between two derived classes - c++

In an arbitrary physics engine for a game, there exists a "Shape" superclass and its derived subclasses such as "AABB" or "Triangle". Collision detection methods between each pair is unique, for example the AABB-Sphere test will obviously be different from the Triangle-AABB test. If I have two collections of shapes as such and I wish to check some collisions between them:
// somewhere in the program
Shape* shape_a;
Shape* shape_b;
....
if (shape_a->intersects(shape_b)) {
// do something
}
How can specific collision checks (e.g. AABB-ABBB, or AABB-Sphere) be performed? For example, if shape_a points to a derived Triangle object, and shape_b performs to a derived AABB object, how can the intersects function correctly compare a Triangle and AABB object? My current implementation is as follows but it does not compile as an include loop is created (likely due to bad OOP practice).
Shape.h
#include "AABB.h"
#include "triangle.h"
class Shape {
public:
virtual bool intersects(Shape* shape) = 0;
virtual bool intersects(AABB aabb) = 0;
virtual bool intersects(Triangle tri) = 0;
}
AABB.h
#include "shape.h"
class AABB : public Shape {
// overriden functions
bool intersects(Shape* shape);
bool intersects(AABB aabb); // aabb-aabb
bool intersects(Triangle tri); // aabb-tri
}
triangle.h
#include "shape.h"
class Triangle : public Shape {
// overriden functions
bool intersects(Shape* shape);
bool intersects(AABB aabb); // tri-aabb
bool intersects(Triangle tri); // tri-tri
}
The implementation for an intersects(Shape* shape) function looks like:
// in AABB.cpp
bool intersects(Shape* shape) {
return shape.intersects(*this); // where *this is an AABB object
}
And of course, I want to avoid code duplication: the tri-aabb test is the same as the aabb-tri test. Perhaps I am writing code that is fundamentally flawed and is bad OOP practice. In any case, I'd appreciate any help with this problem!

To avoid the include loop you can just forward-declare the AABB and Triangle classes in shape.h:
class AABB;
class Triangle;
To avoid duplicating code based on parameter order you can have a separate function with a canonical order for each overloading pair, and call it with the different orders from the member functions:
bool intersects( AABB *, Triangle * );
AABB::intersects( Triangle *t ) { return intersects( this, t ); }
Triangle::intersects( AABB *b ) { return intersects( b, this ); }

Related

Overload a function with a derived class argument if you only have a pointer to the base class in C++

I have seen people using containers of pointers to the base class to hold groups of objects which share the same virtual functions. Is it possible to use overloaded functions of the derived class with these base class pointers. It is hard to explain what I mean but (I think) easy to show with code
class PhysicsObject // A pure virtual class
{
// Members of physics object
// ...
};
class Circle : public PhysicsObject
{
// Members of circle
// ...
};
class Box : public PhysicsObject
{
// Members of box
// ...
};
// Overloaded functions (Defined elsewhere)
void ResolveCollision(Circle& a, Box& b);
void ResolveCollision(Circle& a, Circle& b);
void ResolveCollision(Box& a, Box& b);
int main()
{
// Container to hold all our objects
std::vector<PhysicsObject*> objects;
// Create some circles and boxes and add to objects container
// ...
// Resolve any collisions between colliding objects
for (auto& objA : objects)
for (auto& objB : objects)
if (objA != objB)
ResolveCollision(*objA, *objB); // !!! Error !!! Can't resolve overloaded function
}
My first idea was to make these functions be virtual class members also (shown below) but I quickly realised that it has exactly the same issue.
class Circle;
class Box;
class PhysicsObject // A pure virtual class
{
virtual void ResolveCollision(Circle& a) = 0;
virtual void ResolveCollision(Box& a) = 0;
// Members of physics object
// ...
};
class Box;
class Circle : public PhysicsObject
{
void ResolveCollision(Circle& a);
void ResolveCollision(Box& a);
// Members of circle
// ...
};
class Circle;
class Box : public PhysicsObject
{
void ResolveCollision(Circle& a);
void ResolveCollision(Box& a);
// Members of box
// ...
};
From googling the problem it seems like possibly it can be solved using casting but I can't figure out how to find the correct type to cast to (also it is ugly). I suspect I am asking the wrong question and there is a better way to structure my code which sidesteps this problem and achieves the same result.
With double dispatch, it would be something like:
class Circle;
class Box;
// Overloaded functions (Defined elsewhere)
void ResolveCollision(Circle& a, Box& b);
void ResolveCollision(Circle& a, Circle& b);
void ResolveCollision(Box& a, Box& b);
class PhysicsObject // A pure virtual class
{
public:
virtual ~PhysicsObject() = default;
virtual void ResolveCollision(PhysicsObject&) = 0;
virtual void ResolveBoxCollision(Box&) = 0;
virtual void ResolveCircleCollision(Circle&) = 0;
};
class Circle : public PhysicsObject
{
public:
void ResolveCollision(PhysicsObject& other) override { return other.ResolveCircleCollision(*this); }
void ResolveBoxCollision(Box& box) override { ::ResolveCollision(*this, box);}
void ResolveCircleCollision(Circle& circle) override { ::ResolveCollision(*this, circle);}
// ...
};
class Box : public PhysicsObject
{
public:
void ResolveCollision(PhysicsObject& other) override { return other.ResolveBoxCollision(*this); }
void ResolveBoxCollision(Box& box) override { ::ResolveCollision(box, *this);}
void ResolveCircleCollision(Circle& circle) override { ::ResolveCollision(circle, *this);}
// ...
};
The way I'd do this is to build a Extent class that tells you about the physical perimeter of an object, perhaps with respect to its barycentre. Additionally, you'd have
virtual const Extent& getExtent() const = 0;
in the PhysicsObject class. You then implement getExtent once per object type.
Your collision detection line becomes
ResolveCollision(objA->getExtent(), objB->getExtent());
Although, in a sense, this does little more than kick the can down the road as the complexity is pushed to the Extent class, the approach will scale well since you only need to build one method per object.
The alternative double dispatch mechanism is intrusive insofar that a new shape requires adjustment to all existing shapes. Having to recompile the Circle class, for example, if you introduce an Ellipse class, say, is a code smell to me.
I am going to sketch an implementation that does not rely on double-dispatch. Instead, it makes use of a table where all functions are registered. This table is then accessed using the dynamic type of the objects (passed as base class).
First, we have some example shapes. Their types are enlisted inside an enum class. Every shape class defines a MY_TYPE as their respective enum entry. Furthermore, they have to implement the base class' pure virtual type method:
enum class ObjectType
{
Circle,
Box,
_Count,
};
class PhysicsObject
{
public:
virtual ObjectType type() const = 0;
};
class Circle : public PhysicsObject
{
public:
static const ObjectType MY_TYPE = ObjectType::Circle;
ObjectType type() const override { return MY_TYPE; }
};
class Box : public PhysicsObject
{
public:
static const ObjectType MY_TYPE = ObjectType::Box;
ObjectType type() const override { return MY_TYPE; }
};
Next, you have your collision resolution functions, they have to be implemented depending on the shapes, of course.
void ResolveCircleCircle(Circle* c1, Circle* c2)
{
std::cout << "Circle-Circle" << std::endl;
}
void ResolveCircleBox(Circle* c, Box* b)
{
std::cout << "Circle-Box" << std::endl;
}
void ResolveBoxBox(Box* b1, Box* b2)
{
std::cout << "Box-Box" << std::endl;
}
Note, that we only have Circle-Box here, no Box-Circle, as I assume their collision is detected in the same way. More on the Box-Circle collision case later.
Now to the core part, the function table:
std::function<void(PhysicsObject*,PhysicsObject*)>
ResolveFunctionTable[(int)(ObjectType::_Count)][(int)(ObjectType::_Count)];
REGISTER_RESOLVE_FUNCTION(Circle, Circle, &ResolveCircleCircle);
REGISTER_RESOLVE_FUNCTION(Circle, Box, &ResolveCircleBox);
REGISTER_RESOLVE_FUNCTION(Box, Box, &ResolveBoxBox);
The table itself is a 2d array of std::functions. Note, that those functions accept pointers to PhysicsObject, not the derived classes. Then, we use some macros for easy registration. Of course, the respective code could be written by hand and I am quite aware of the fact that the use of macros is typically considered bad habit. However, in my opinion, these sorts of things are what macros are good for and as long as you use meaningful names that do not clutter your global namespace, they are acceptable. Notice again that only Circle-Box is registered, not the other way round.
Now to the fancy macro:
#define CONCAT2(x,y) x##y
#define CONCAT(x,y) CONCAT2(x,y)
#define REGISTER_RESOLVE_FUNCTION(o1,o2,fn) \
const bool CONCAT(__reg_, __LINE__) = []() { \
int o1type = static_cast<int>(o1::MY_TYPE); \
int o2type = static_cast<int>(o2::MY_TYPE); \
assert(o1type <= o2type); \
assert(!ResolveFunctionTable[o1type][o2type]); \
ResolveFunctionTable[o1type][o2type] = \
[](PhysicsObject* p1, PhysicsObject* p2) { \
(*fn)(static_cast<o1*>(p1), static_cast<o2*>(p2)); \
}; \
return true; \
}();
The macro defines a uniquely named variable (using the line number), but this variable merely serves to get the code inside the initializing lambda function to be executed. The types (from the ObjectType enum) of the passed two arguments (these are the concrete classes Box and Circle) are taken and used to index the table. The entire mechanism assumes that there is a total order on the types (as defined in the enum) and checks that a function for Circle-Box collision is indeed registered for the arguments in this order. The assert tells you if you are doing it wrong (accidentally registering Box-Circle). Then a lambda function is registered inside the table for this particular pair of types. The function itself takes two arguments of type PhysicsObject* and casts them to the concrete types before invoking the registered function.
Next, we can have a look at how the table is then used. It is now easy to implement a single function that checks collision of any two PhysicsObjects:
void ResolveCollision(PhysicsObject* p1, PhysicsObject* p2)
{
int p1type = static_cast<int>(p1->type());
int p2type = static_cast<int>(p2->type());
if(p1type > p2type) {
std::swap(p1type, p2type);
std::swap(p1, p2);
}
assert(ResolveFunctionTable[p1type][p2type]);
ResolveFunctionTable[p1type][p2type](p1, p2);
}
It takes the dynamic types of the argument and passes them to the function registered for those respective types inside the ResolveFunctionTable. Notice, that the arguments are swapped if they are not in order. Thus you are free to invoke ResolveCollision with Box and Circle and it will then internally invoke the function registered for Circle-Box collision.
Lastly, I will give an example of how to use it:
int main(int argc, char* argv[])
{
Box box;
Circle circle;
ResolveCollision(&box, &box);
ResolveCollision(&box, &circle);
ResolveCollision(&circle, &box);
ResolveCollision(&circle, &circle);
}
Easy, isn't it? See this for a working implementation of the above.
Now, what is the advantage of this approach? The above code is basically all you need to support an arbitrary number of shapes. Let's say you are about to add a Triangle. All you have to do is:
Add an entry Triangle to the ObjectType enum.
Implement your ResolveTriangleXXX functions, but you have to do this in all cases.
Register them to your table using the macro: REGISTER_RESOLVE_FUNCTION(Triangle, Triangle, &ResolveTriangleTriangle);
That's it. No need to add further methods to PhysicsObject, no need to implement methods in all existing types.
I am aware of some 'flaws' of this approach like using macros, having a central enum of all types and relying on a global table. The latter case might lead to some trouble if the shape classes are built into multiple shared libraries. However, in my humble opinion, this approach is quite practical (except for very special use cases) since it does not lead to the explosion of code as is the case with other approaches (e.g. double-dispatch).

typecasting and check is an instance exists in a vector

I have a virtual class Shape. class Rectangle, Triangle, Circle inherit from Shape and have different characteristics (size, number of side...).
I want to store different shape in a structure (let's say a vector).
so my vector would be: vector<Shape*> myvec;
I would like to know how it is possible to know if a given instance of a shape is present in the vector (ex: new Circle(diameter 10);).
I have heard about dynamic cast but I don't understand at all how to use it?
class Shape
{
public:
Shape(){}
virtual int Size()=0;
virtual int Sides()=0;
virtual bool isequal(const Shape & rhs)=0;
int size,sides;
};
class Circle : public Shape
{
public:
Circle(int diameter):size(diameter)
{
sides=0;
}
bool isequal(const Circle &rhs)
{
return size==rhs.size;
}
int size,sides;
};
class Rectangle: public Shape
{
Rectangle(int nbsides,int size1,int size2 ): sides(nbsides),size1(size1),size2(size2){}
bool isequal(const Rectangle &rhs)
{
return (size1==rhs.size1 && rhs.size2==size2);
}
int sides,size1,size2;
};
dynamic_cast is right:
Shape* shape = myvec[0];
Circle* circle = dynamic_cast<Circle*>(shape);
if(circle != nullptr)
; // it's a Circle! Do something circly.
else
; // it's not a Circle. Do something else.
But a better answer to the question is that, in a perfect world, you should use polymorphism such that it is rarely if ever necessary to do this.
Based on the additional comments below, I think you might want to use a pure virtual isequal() in Shape, with dynamic_casts inside the various implementations (the inside of a polymorphic comparison function is one of the few places I can use dynamic_cast without feeling the need to wash my hands afterwards :) ).
// edited out code that isn't directly relevant
class Shape
{
public:
virtual bool isequal(const Shape& rhs)=0;
};
class Circle : public Shape
{
public:
// snip
virtual bool isequal(const Shape &rhs) override
{
Circle* rhsAsCircle = dynamic_cast<Circle*>(&rhs);
if(rhsAsCircle == nullptr)
return false; // not a Circle; can't be equal
return size==rhsAsCircle->size;
}
};
Then elsewhere:
Circle searchCircle(10);
for(Shape* shape : myvec)
if(shape->isequal(searchCircle))
; // we have a match!

How can I iterate over a vector of base class objects?

I have an problem where we need to have a number of shapes, such as a Circle and Square which can be position on a flat, 2 dimensional plane. All shapes, such as Circle and Square inherit from an abstact base class Shape; as such I have a vector of pointers to shapes.
However, I need to be able to iterate over the plane and find any shapes which collide, such that they are intersecting or touching. If I get a shape from the vector, I don't know if it is a Square or a Circle because it has been sliced to the Shape base class.
How would I be best solving this problem?
#ifndef Plane_h
#define Plane_h
#include <vector>
#include "Shape.h"
class Plane {
public:
Plane(std::vector<Shape*>);
Plane(const Plane&);
~Plane();
void add(Shape*);
std::vector<Shape*> all() const;
protected:
std::vector<Shape*> shapes;
};
#endif
Your classes have not been sliced. This would result in a sliced object:
vector<Shape> vec;
Circle circ;
vec.push_back(circ);
http://en.wikipedia.org/wiki/Object_slicing
In your instance the instantiated objects remain whole and the pointers point to whole objects - but it is almost certainly true that in order to calculate intersections you will need to do some downcasting. While this is to be done as little as possible it's not a crime in itself.
Your best bet would be to provide a method in the base class to return a value indicating the object type - maybe use an enumeration - and use that to downcast a particular Shape pointer or reference to a pointer/reference to the correct derived type.
An abstract method in the base Shape class like bool Intersects( const Shape& obj ) could be overridden by the derived classes, the overrides downcasting the parameter to the correct derived type.
Alternatively you might prefer to provide a global/static method taking two shapes, or privately implement that method and call it from the instance method Intersects()
( Detecting the intersections is not exactly a trivial task. :-) )
You have to use polymorphism. Add a virtual method on your Shape class:
class Shape {
...
virtual bool intersects(Shape const* otherShape);
...
}
Then you implement it for each different shape. Then if it's used like:
Shape* A = getShapeA();
Shape* B = getShapeB();
if (A->intersects(B))
doSomething();
The correct version is called, i.e if A is a Circle, Circle::intersects is called. But in there, you still don't know what shape B actually is. You can find this out by trying to do a dynamic cast:
Circle* circle = dynamic_cast<Circle*>(otherShape);
if (circle)
intersectsCircle(circle);
Here is another method, that does not need dynamic casts (or any explicit cast at all), or an ugly enum listing the subclasses. It is based on double dispatch, which basically works by going through two virtual methods in order to determine the types of the two objects you want to work on.
#include <iostream>
using namespace std;
class Circle;
class Square;
struct Shape
{
virtual void intersect(Shape* otherShape) = 0;
virtual void intersect(Circle* otherCircle) = 0;
virtual void intersect(Square* otherSquare) = 0;
};
struct Circle : public Shape
{
virtual void intersect(Shape* otherShape)
{
otherShape->intersect(this);
}
virtual void intersect(Circle* otherCircle)
{
cout << "Intersecting Circle with Circle" << endl;
}
virtual void intersect(Square* otherSquare)
{
cout << "Intersecting Circle with Square" << endl;
}
};
struct Square : public Shape
{
virtual void intersect(Shape* otherShape)
{
otherShape->intersect(this);
}
virtual void intersect(Circle* otherCircle)
{
otherCircle->intersect(this);
}
virtual void intersect(Square* otherSquare)
{
cout << "Intersecting Square with Square" << endl;
}
};
int main()
{
Circle circle;
Square square;
circle.intersect(&square);
Shape* shapeA = &circle;
Shape* shapeB = &square;
shapeA->intersect(shapeA);
shapeA->intersect(shapeB);
shapeB->intersect(shapeA);
shapeB->intersect(shapeB);
}
Note that here you still have to list all possible subclasses within the base class, but in this case in the form of overloads of the intersect for each base class. If you fail to add all (say, you make a class Triangle : public Shape, but no Shape::intersect(Triangle*)), you end up with infinite call loops.
Also note that in this example I did a 'triple' dispatch, so I don't have to implement the logic to intersect a Circle with a Square twice.

Drawing objects - Better class design?

I have a problem designing a class that will allow me to draw objects of various shapes.
Shape is the base class
Triangle, Square, Rectangle are derived classes from Shape class
I have a vector<Shape*> ShapeCollection that stores the derived objects i.e. Triangle,Square, Rectangle
Once I pick the an object from the vector I need to draw the object onto the screen.
At this point I am stuck at what the design of a class should be where as a single 'Drawing' class will do the drawing, consuming an object of 'Shape' class. As the vector will contain different objects of the same base class Shape. As I have a thread that picks up an object from the vector and once I have an object I must be able to draw it properly.
So more or less below is what I say
class Drawing
{
public:
void Draw(Shape* shape, string objectName)
{
// Now draw the object.
// But I need to know which Object I am drawing or use
// switch statements to identify somehow which object I have
// And then draw. I know this is very BAD!!!
// e.g.
switch(objectName)
{
case "rectangle":
DrawRectangle((Rectangle*) shape)
break;
//Rest of cases follow
}
}
}
Where as I will have a DrawSquare, DrawTriangle function which will do the drawing.
This must be something that has been solved. There must be a better way of doing this as
all this switch statement has to go away somehow!
Any guidance is much appreciated.
Thanks
#Adrian and #Jerry suggested to use virtual function, I thought of it, but I need to have my Drawing away from the base class Shape
You would use polymorphism.
Make a pure virtual function in your base class (i.e. when declaring the function assign it to 0 as in void DrawShape() = 0;)
Declare and define that function in your derived classes.
That way you can just call DrawShape() on each of these objects even if it is passed as a Shape object.
Alternatives (NOTE: code has not been tested):
Function pointer, which is like building your own vtable aka delegate.
struct square
{
void (*draw)(square&);
};
void drawSquare(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
square s;
s.draw = drawSquare;
s.draw(s);
Functor, which is a class that overrides operator() and also is like a delegate
struct square
{
// Note that std::function can hold a function pointer as well as a functor.
function<void(square&)> draw;
};
struct drawSquare
{
void oparator()(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
};
square s;
square s.draw = drawSquare();
s.draw(s);
NOTE: 1 and 2 can also be initialised with lambda functions:
square s;
s.draw = [](square& obj) {
// draw square code
// there is no 'this'. must access members via `obj`.
};
s.draw(s);
NOTE: 1 could be done with a template:
struct square;
template <void (*DRAW)(square&)>
struct square
{
void draw()
{
DRAW(*this);
}
};
void drawSquare(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
square s<&drawSquare>;
s.draw();
NOTE: 2 could be done with a template as well:
template <typename DRAW>
struct square
{
void draw()
{
// First set of parentheses instantiate the DRAW object.
// The second calls the functor.
DRAW()(*this);
}
};
struct drawSquare
{
void oparator()(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
};
square s<drawSquare>;
s.draw();
Or alternatively, which would allow the passing of a stateful functor:
template <typename DRAW>
struct square
{
DRAW draw;
};
struct drawSquare
{
void operator()(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
};
square s<drawSquare>;
s.draw = drawSquare();
s.draw(s);
Inherit from another class that implements the function you want either with a templated base class (IIRC, this was done in the ATL). This is just rolling your own hard-coded vtable and is called the Curiously Recurring Type Pattern (CRTP).
template <class D>
struct shape
{
inline void draw() { return static_cast<D&>(*this).draw(); }
};
void draw(square& obj)
{
// draw square code
// No 'this' available. must access shape members via `obj`.
}
struct square : public D<square>
{
void draw()
{
drawSquare(*this);
}
};
Other examples can be found here and here.
Have your draw class inherit from the type of shape class which inherits from the base shape class.
struct shape
{
virtual void draw() = 0;
};
struct square : public shape
{
};
struct drawSquare : public square
{
virtual void draw()
{
// draw square code
// you access the square's public or protected members from here
}
};
Use a std::unordered_map
#include <unordered_map>
#include <typeinfo>
#include <functional>
struct shape { };
struct square : public shape { };
void drawSquare(shape& o)
{
// this will throw an exception if dynamic cast fails, but should
// never fail if called from function void draw(shape& obj).
square& obj = dynamic_cast<square&>(o);
// draw square code
// must access shape members via `obj`.
}
std::unordered_map<size_t, std::function<void(shape&)>> draw_map
{
{ type_id(square).hash(), drawSquare }
};
void draw(shape& obj)
{
// This requires the RTTI (Run-time type information) to be available.
auto it = draw_map.find(type_id(obj).hash());
if (it == draw_map.end())
throw std::exception(); // throw some exception
(*it)(obj);
}
NOTE: if you are using g++ 4.7, be warned unordered_map has been shown to have performance issues.
This is pretty much the classic demonstration of when you want a virtual function. Define a draw in your base class, then override it in each derived class. Then to draw all the objects, you step through the collection and call the draw() member for each.
class shape {
// ...
virtual void draw(canvas &c) = 0;
};
class square : public shape {
int x, y, size;
// ...
virtual void draw(canvas &c) {
c.move_to(x, y);
c.draw_to(x+size, y);
c.draw_to(x+size, y+size);
c.draw_to(x, y+size);
c.draw_to(x, y);
}
};
...and so on for each type of shape you care about.
Edit: using a strategy class, you'd end up with code vaguely along this line:
template <class draw>
class shape {
// ...
virtual void draw(canvas &c) = 0;
};
template <class d>
class square : public shape<d> {
// ...
virtual void draw(canvas &c) {
d.square(x, y, size, c);
}
};
Another possibility would be to use a Visitor pattern. This is typically used when you need/want to traverse a more complex structure instead of a simple linear sequence, but could be used here as well. This is enough more complex that it's probably a bit much to go into here, but if you search for "Visitor pattern", you should turn up a fair amount of material.

Most Elegant Way to Get Around This Polymorphism Issue

EDIT: I'm working with C++.
So, I am creating methods/functions to test intersection between shapes. I essentially have this:
class Shape {};
class Rectangle : public Shape {};
class Circle : public Shape {};
class Line : public Shape {};
Now, I need to decide on the best way to write the actual methods/functions to test intersection. But all my shapes will be stored in a list of Shape pointers, so I will be calling a method/function of the basic form:
bool intersects (Shape* a, Shape* b);
At that point, I need to determine which types of shapes 'a' and 'b' are, so I can properly detect collisions. I can easily do one of them, by just using some virtual methods:
class Shape
{
virtual bool intersects (Shape* b) = 0;
}
That would determine one of the shapes ('a' is now 'this'). However, I would still need to get the type of 'b'. The obvious solution is to give Shape an 'id' variable to classify which shape it is, and then 'switch' through those, and then use dynamic_cast. However, that is not very elegant, and it feels like there should be a more OO way to do this.
Any suggestions?
As #Mandarse pointed out, this is typical double dispatch issue. In Object Oriented languages, or like C++ languages that can implement Object Oriented concepts, this is usually solved using the Visitor Pattern.
The Visitor interface itself defines one callback per concrete type, in general.
class Circle;
class Rectangle;
class Square;
class Visitor {
public:
virtual void visit(Circle const& c) = 0;
virtual void visit(Rectangle const& r) = 0;
virtual void visit(Square const& s) = 0;
};
Then, the Shape hierarchy is adapted for this. We need two methods: one to accept any type of visitor, the other to create the "appropriate" intersection visitor.
class Visitor;
class Intersecter;
class Shape {
public:
virtual void accept(Visitor&) const = 0; // generic
virtual Intersecter* intersecter() const = 0;
};
The intersecter is simple:
#include "project/Visitor.hpp"
class Intersecter: public Visitor {
public:
Intersecter(): result(false) {}
bool result;
};
For example, for Circle it will give:
#include "project/Intersecter.hpp"
#include "project/Shape.hpp"
class Circle;
class CircleIntersecter: public Intersecter {
public:
explicit CircleIntersecter(Circle const& c): _left(c) {}
virtual void visit(Circle const& c); // left is Circle, right is Circle
virtual void visit(Rectangle const& r); // left is Circle, right is Rectangle
virtual void visit(Square const& s); // left is Circle, right is Square
private:
Circle const& _left;
}; // class CircleIntersecter
class Circle: public Shape {
public:
virtual void accept(Visitor& v) const { v.visit(*this); }
virtual CircleIntersecter* intersecter() const {
return new CircleIntersecter(*this);
}
};
And the usage:
#include "project/Intersecter.hpp"
#include "project/Shape.hpp"
bool intersects(Shape const& left, Shape const& right) {
boost::scope_ptr<Intersecter> intersecter(left.intersecter());
right.accept(*intersecter);
return intersecter->result;
};
If other methods need a double-dispatch mechanism, then all you need to do is create another "Intersecter-like" class that wrap the result and inherits from Visitor and a new "Factory" method rooted in Shape that is overriden by derived classes to provide the appropriate operation. It is a bit long-winded, but does work.
Note: it is reasonable to except intersect(circle, rectangle) and intersect(rectangle, circle) to yield the same result. You can factor the code is some methods and have CircleIntersecter::visit delegates to the concrete implementation. This avoid code duplication.
Andrei Alexandrescu detailed this problem in his classic Modern C++ Design. The companion library Loki contains the the implementation for Multi-Methods.
Update
Loki provides three implementations of Multi-Methods, depending on the user's needs. Some are for simplicity, some are for speed, some are good for low coupling and some provide more safety than others. The chapter in the book spans nearly 40 pages, and it assumes the reader is familiar with many of the book's concepts -- if you are comfortable using boost, then Loki may be down your alley. I really can't distill that to an answer acceptable for SO, but I've pointed you to the best explanation of the subject for C++ that I know of.
C++ run-time polymorphism has a single dispatch (the base class vtable).
There are various solution to your problem but none of them is "elegant", since they all try to force the language to do more that it can natively support (Alexandrescu Loki multimethods is a very goodly hidden set of hacks: it encapsulates the "bad things", but doesn't make then good)
The concept, here, it that you need to write all the N2 functions of the possible combinations and find a way to call them based on the actual runtime-type of TWO parameters.
The "visitor pattern" (call back a virtual unction from another virtual function), the "mutimethod" technic (use a generic dspatch table), the "dynamic cast" into a virtual function or the "dual dynamic_cast" out all of the functions all do the same thing: call a function after two indirection. None of them can technically be defined "better then the other" since the resulting performance is mostly the same.
But some of them cost more then the other in code writing and other cost more in code maintenance.
You have most likely to try to estimate in your case what the trade-off is. How many other classes do you think you may need to add in the future?
You can add a field shapeType to each Shape
For example:
class Shape {
virtual shapetype_t getShapeType() const;
// ...
}
I've played with the shapes intersection resolving dispatch approach just for fun. I didn't like the idea of extending classes each time new shape appear. I thought of the collection of intersection resolvers which is iterated to find out if there is one that supports given pair of shapes. If new shape appear new intersection resolvers are to be added to the collection.
I don't think that it is a most optimal approach in terms of performance, since the resolvers iterated through and dynamic casts executed until proper resolver is found.
But, nevertheless...
Intersection resolver takes two shapes and return resolving result which contain supported and intersect flags.
struct Intersection_resolution {
bool supported;
bool intersect;
};
class IIntersection_resolver {
public:
virtual Intersection_resolution intersect(Shape& shape1, Shape& shape2) = 0;
};
Resolver implementation. Template class, takes two shapes, checks if it supports them and if so invokes check_intersection method. Latter should be defined in specification. Note that the the pair should be specified only ones, i.e. if Rectangle-Circle specified, no need to specify Circle-Rectangle.
template<typename S1, typename S2>
class Intersection_resolver : public IIntersection_resolver {
private:
bool check_intersection(S1& s1, S2& s2);
public:
Intersection_resolution intersect(Shape& shape1, Shape& shape2) override final {
S1* s1 = dynamic_cast<S1*>(&shape1);
S2* s2{nullptr};
if (s1)
s2 = dynamic_cast<S2*>(&shape2);
else {
s1 = dynamic_cast<S1*>(&shape2);
if (s1)
s2 = dynamic_cast<S2*>(&shape1);
}
bool supported{false};
bool intersect{false};
if (s1 && s2) {
supported = true;
intersect = check_intersection(*s1, *s2);
}
return Intersection_resolution{supported, intersect};
}
};
Couple of specifications...
template<>
bool Intersection_resolver<Rectangle, Rectangle>::check_intersection(Rectangle& r1, Rectangle& r2) {
cout << "rectangles intersect" << endl;
return true;
}
template<>
bool Intersection_resolver<Rectangle, Circle>::check_intersection(Rectangle& r1, Circle& r2) {
cout << "rectangle intersect circle" << endl;
return true;
}
Resolvers collection.
class Intersection_resolvers {
std::vector<IIntersection_resolver*> resolvers_;
public:
Intersection_resolvers(std::vector<IIntersection_resolver*> resolvers) :resolvers_{resolvers} {}
Intersection_resolution intersect(Shape& s1, Shape& s2) {
Intersection_resolution intersection_resolution;
for (IIntersection_resolver* resolver : resolvers_) {
intersection_resolution = resolver->intersect(s1, s2);
if (intersection_resolution.supported)
break;
}
return intersection_resolution;
}
};
Intersection_resolver<Rectangle, Rectangle> rri;
Intersection_resolver<Rectangle, Circle> rci;
Intersection_resolvers intersection_resolvers{{&rri, &rci}};
Usage.
int main() {
Rectangle r;
Triangle t;
Circle c;
Shape* shapes[]{&r, &t, &c};
for (auto shape : shapes) {
shape->draw();
}
for (auto shape : shapes) {
for (auto other : shapes) {
auto intersection_resolution = intersection_resolvers.intersect(*shape, *other);
if (!intersection_resolution.supported) {
cout << typeid(*shape).name() << " - " << typeid(*other).name() << " intersection resolving not supported" << endl;
}
}
}
}
Output.
rectangle drawn
triangle drawn
circle drawn
rectangles intersect
9Rectangle - 8Triangle intersection resolving not supported
rectangle intersect circle
8Triangle - 9Rectangle intersection resolving not supported
8Triangle - 8Triangle intersection resolving not supported
8Triangle - 6Circle intersection resolving not supported
rectangle intersect circle
6Circle - 8Triangle intersection resolving not supported
6Circle - 6Circle intersection resolving not supported