Implementing abstract 'overlaps' method for different shapes? - c++

I have an abstract base class called Shape, which looks something like this:
class Shape {
public:
Shape(Point center);
virtual bool overlaps(Shape *other) = 0;
private:
Point m_center; // has getter&setter
};
I'm having problems with the overlaps(Shape *other); method; I have no idea how to implement it in subclasses.
Let's take two examples, (I will probably have no more than two or three shapes) Circle and Rect.
Basically what I've tried is to create a two overloads in both classes after using forward declaration to allow Circle and Rect to "know" each other:
virtual bool Rect::overlaps(Circle *other);
virtual bool Rect::overlaps(Rect *other);
virtual bool Circle::overlaps(Circle *other);
virtual bool Circle::overlaps(Rect *other) { return other->overlaps(this); }
It's now easy to implement the maths inside all the overloads; however, I will get an error cannot allocate an object of abstract type 'Circle' and note: virtual bool Unit::overlaps(Unit *).
This is because my Circle and Rect classes only have methods with Circle * and Rect * as their parameters, but none with Unit *.
I also tried forward declarating Circle and Rect in my shape.h, but since forward declarations aren't the same classes as my actual Circle and Rect, I will only get the same error.
Without removing the common base class, is there a way to implement such behavior?
Or is there a workaround to make it work?
Additional Information
I have a 2D World class which contains vector<Shape *> m_shapes; and I will need to see if two shapes overlap each other;
for (unsigned int i = 0; i < m_shapes.size(); i++) {
if (certainShape->overlaps(m_shapes[i])) {
collapse();
}
}

Welcome to multiple dispatch! Essentially, you are asking for a method that is virtual with respect to the runtime type of more than one object - in your case, the types of two shapes being tested for overlap.
There are several common ways of implementing double dispatch in C++: for example, you could use the visitor pattern, or make a map based on RTTI. Selecting one or the other is up to you.
If you decide to go with the visitor pattern, you make the Shape "visitable" by adding the visit method.
Here is an example of the visitor-based approach. It is admittedly rather verbose, but it also addresses a complex task, so it is fair for it to require lots of code. I stripped the example below to the bare minimum - only two shapes with no data members, and methods that do not do anything except printing. This should be sufficient to get you started, though:
#include <iostream>
using namespace std;
class ShapeVisitor;
struct Shape {
virtual void accept(ShapeVisitor& v) = 0;
virtual bool overlaps(Shape& other) = 0;
};
class Circle;
class Square;
struct ShapeVisitor {
virtual void visitCircle(Circle& c) = 0;
virtual void visitSquare(Square& s) = 0;
};
// These three methods do the actual work
bool checkOverlap(Square& s, Circle& c) {
cout << "Checking if square overlaps circle" << endl;
return false;
}
bool checkOverlap(Square& a, Square& b) {
cout << "Checking if square overlaps square" << endl;
return false;
}
bool checkOverlap(Circle& a, Circle& b) {
cout << "Checking if circle overlaps circle" << endl;
return false;
}
class Square : public Shape {
struct OverlapVisitor : public ShapeVisitor {
OverlapVisitor(Square& _my) : result(false), my(_my) {}
virtual void visitCircle(Circle& c) {
result = checkOverlap(my, c);
}
virtual void visitSquare(Square& s) {
result = checkOverlap(my, s);
}
bool result;
Square& my;
};
public:
virtual void accept(ShapeVisitor& v) {
v.visitSquare(*this);
}
virtual bool overlaps(Shape& other) {
OverlapVisitor v(*this);
other.accept(v);
return v.result;
}
};
class Circle : public Shape {
struct OverlapVisitor : public ShapeVisitor {
OverlapVisitor(Circle& _my) : result(false), my(_my) {}
virtual void visitCircle(Circle& c) {
result = checkOverlap(my, c);
}
virtual void visitSquare(Square& s) {
// Important: note how I switched the order of arguments
// compared to Square::OverlapVisitor! There is only one
// square/circle overlap function checker, and it expects
// the square to be the first argument.
result = checkOverlap(s, my);
}
bool result;
Circle& my;
};
public:
virtual void accept(ShapeVisitor& v) {
v.visitCircle(*this);
}
virtual bool overlaps(Shape& other) {
OverlapVisitor v(*this);
other.accept(v);
return v.result;
}
};
Here is this running demo on ideone.
With RTTI approach you would make a map<pair<type_info,type_info>,checker> where checker is a type of a function that takes two pointers to Shape, and returns true or false depending on whether or not the shapes overlap. You make one such function for each pair of object types, populate the map with pointers to these functions based on type_info of their expected parameter types, and use this map at runtime to call the desired function.
Item 31 of the More Effective C++ book explains both these approaches in depth, with some great examples. In fact, the use case discussed in the book, detecting collisions between a pair of game objects, is similar to the one that you are implementing.

What you need is a "how big is other" type function. If we make it real simple, and just use a bounding box (a rectangle that is big enough to cover the entire shape), then we could do something like this:
(For simplicy, I'm using rect as a term for a rectangle)
class Shape
{
...
virtual rect BoundingBox() = 0;
bool overlaps(const Shape& other)
{
return BoundingBox.FitsInside(other.BoundingBox());
}
};
Obviously, you'll then have to write the function of fitsinside for two rectangles and BoundingBox for each shape, but it shouldn't be too hard.
To make a "is this Star completely covered by this Oval?" makes for a slightly more challenging solution [you will need to have a complete outline of both shapes, and an Oval outline may be quite a lot of points to be precisely oval].

Making the subclasses know about each other is a bad idea. If you want pixel-perfect collision, then you are going to have to iterate through every pixel in the shape and compare with the other shape's pixels. Create a virtual function to get a pixel N from the shape, where N is an index, and another function to return the number of pixels. For each pixel N in the current shape, compare with all pixels 0..Nmax in the other shape for collision.
The order of pixels from the index N can be any order. If you alternate pixels between different sides of the shape over nearby N, and start with the outer pixels first, you may be more likely to detect a collision on a lower N.
Now this simple approach is slow, especially if you have many shapes. The solution is to use a cheaper algorithm to check whether the perfect algorithm is necessary. A rectangle bounding box is the cheapest way. Work out the coordinates of a rectangle which is just large enough to hold your shape. I don't know how to work this out for a circle (geometry not my strong suit). You could even cache the bounding box sizes in the class to prevent recalculation for complex shapes. Checking whether two rectangles overlap is very quick and easy.
Only then move onto the costly algorithm if the bounding boxes overlap.
You can make faster checks between certain pairs of objects. For example, two rectangles overlap if their bounding boxes do. It's overkill to move onto pixel comparison. But you may not need this level of performance.

Related

Function inheritance and return types in C++

I'm learning C++ and I came across a problem that I can tackle with my previous programming experience (mainly C and Java; some but limited OOP experience), but I'd like to know what would be a proper, modern C++ solution to it. The problem concerns inheritance and derived classes' versions of a virtual function with different return types. Based on multiple Stack Overflow threads such a thing isn't possible. So how should I go about the following?
To practice C++ features, I'm writing a ray tracer. I have a virtual base class Object and derived classes Polyhedron and Polygon to describe the objects Rays of light can interact with. (In reality I have intermediate virtual classes Solid and Face, and derived classes Sphere, Cylinder, Circle alongside Polyhedron and Polygon, but let's forget about them here to keep things simple.) Currently, I've only implemented emission and absorption of light, i.e., a Ray only goes straight without any refraction or reflections. Absorption within a Polyhedron is proportional to intensity (exponential decay), so I have to figure out the objects a Ray passes through and integrate the Ray's intensity forward from its source to where it hits the detector. I have a vector std::vector<std::shared_ptr<Intersection>> intersections to store all these intersections of a Ray with the objects in a simulated scene. An intersection needs to contain the intersection Points, the intersected Polygon faces and the Polyhedron itself for a Polyhedron object, or alternatively the intersection Point and the Polygon face itself for a Polygon object. Consequently, I'd like to have derived classes Intersection_Polyhedron and Intersection_Polygon to override the call to Intersection::modulate_intensity(const double intensity_before) const which is supposed to return a Ray's intensity after passing the object in question. In other words, I'd like to avoid checking the type of the intersected objects and instead take advantage of inheritance when calculating the modulation to a Ray's intensity.
I would like to have each Ray simply loop through a vector std::vector<std::shared_ptr<Object>> objects containing all the objects in a simulated scene, call the virtual function Object::get_intersection(const Ray& ray) const and get either Intersection_Polyhedron or Intersection_Polygon in return based on the type of the intersection (if it's with a Polyhedron or a Polygon). Pointers to these derived intersection objects would be pushed back into intersections, intersections would be sorted based on the distance from the Ray's origin and then looped through to call and override Intersection::modulate_intensity() to determine a Ray's final intensity on the detector. To me, this would sound like the C++/OOP way of achieving this, but it doesn't seem possible because derived classes' versions of a base class's virtual function must all have the same return type. So how should I do it?
(Currently, I return a singular type of Intersection from get_intersection() for both Polyhedrons and Polygons. As its members, an Intersection has vectors for intersection Points and intersected std::shared_ptr<Polygon> faces, and an std::shared_ptr<Polyhedron> (which is a nullptr for Polygons as there's no bulk). To distinguish between intersections of Polyhedrons and Polygons, I simpy check if there are one or two intersection Points. This isn't too inelegant, but modern C++ has to offer a better way of achieving this with inheritance, right?)
Some very C++-like pseudocode to further clarify what I'd like to achieve:
// ...
// create objects in a scene
std::vector<std::shared_ptr<Object>> objects;
// ...
// find a ray's intersections with the objects
std::vector<std::shared_ptr<Intersection>> intersections;
for(const auto& object : objects) {
// virtual class Object's function overridden with that of Polyhedron or Polygon
// returns std::shared_ptr<Intersection_Polyhedron> or std::shared_ptr<Intersection_Polygon> based on type of object
auto intersection = object->get_intersection(ray);
intersections.push_back(intersection);
}
// sort the intersections with std::sort and a lambda expression
// ...
// calculate a ray's intensity
double intensity = 0.0;
for(const auto& intersection : intersections) {
// virtual class Intersection's function overridden with that of Intensity_Polyhedron or Intensity_Polygon
intensity = intersection->modulate_intensity(intensity);
}
// ...
Returning interface is fine in general:
class Ray;
struct Intersection
{
virtual ~Intersection() = default;
virtual double modulate_intensity(double intensity) = 0;
};
struct Intersection_Polygon : Intersection
{
double modulate_intensity(double intensity) override {/**/}
};
struct Intersection_Polyhedron : Intersection
{
double modulate_intensity(double intensity) override {/**/}
};
struct Object
{
virtual ~Object() = default;
virtual std::shared_ptr<Intersection> get_intersection(const Ray&) = 0;
};
struct Polygon : Object
{
std::shared_ptr<Intersection> get_intersection(const Ray&) override {
return std::make_shared<Intersection_Polygon>();
}
};
struct Polyhedron : Object
{
std::shared_ptr<Intersection> get_intersection(const Ray&) override {
return std::make_shared<Intersection_Polyhedron>();
}
};
Return type can be improved with covariance, but C++ only handle it for references and (non-smart) pointers. So it requires some boiler plate to simulate it for smart pointers:
struct Object
{
virtual ~Object() = default;
std::shared_ptr<Intersection> get_intersection(const Ray& ray)
{
return std::shared_ptr<Intersection>{get_intersection_ptr(ray)};
}
protected:
virtual Intersection* get_intersection_ptr(const Ray&) = 0;
};
struct Polygon : Object
{
std::shared_ptr<Intersection_Polygon> get_intersection(const Ray& ray)
{
return std::shared_ptr<Intersection_Polygon>{get_intersection_ptr(ray)};
}
protected:
Intersection_Polygon* get_intersection_ptr(const Ray&) override {
return new Intersection_Polygon();
}
};
template (possibly CRTP) might help to factorize boiler plate:
template <typename IntersectionType>
struct ObjectT : Object
{
std::shared_ptr<IntersectionType> get_intersection(const Ray& ray)
{
return std::shared_ptr<IntersectionType>{get_intersection_ptr(ray)};
}
protected:
IntersectionType* get_intersection_ptr(const Ray&) override {
return new IntersectionType();
}
};
struct Polygon : ObjectT<Intersection_Polygon> {};
struct Polyhedron : ObjectT<Intersection_Polyhedron> {};

Inheritance and friend functions, accessing protected members from base class

Let say that I have a big class Circle with a lot of members and functions. To proceed a large amount of data I decided to create class PotentialCirlce (with only 3 members - x, y, r), do most of preprocessing based on PotentialCirlce and in the last stage create objects Circle.
a) is it correct approach? do It influence on performance or rather should I use only Circle.
It seems to me that I can use inheritance:
class potentialCircle {
protected:
point_t center;
unsigned int radius;
public:
potentialCircle(int a, int b, unsigned int r) : center{ point_t(a,b) }, radius{ r } {}
potentialCircle() = delete;
potentialCircle(const potentialCircle&) = default;
potentialCircle(potentialCircle&&) = default;
potentialCircle& operator=(const potentialCircle&) = default;
potentialCircle& operator=(potentialCircle&&) = default;
virtual ~potentialCircle() = default;
};
class Circle : public potentialCircle {
// members detected based on Hough Circle Transform
//point_t center; // coordinates of center point
point_t alternative_center; // needed when center is out of frame
//unsigned int radius; // radius
// members calculated based on Flood Fill algorithm (more realistic)
unsigned int area = 0;
float diameter = 0;
float perimeter = 0;
....
};
b) where should I put method which needs to compare two difference objects? one object of type Circle and one of PotentialCirle?
currently, I have defined below function as part of Circle
bool Circle::is_greater(const std::pair<potentialCircle, int>& point_pair) const;
but I don't have access to protected data members of potentialCircle, although Circle is inheriting from potentialCircle.
Maybe I should defined is_greater() as part of namepsace and make it a friend to Circle and potentialCircle.
Do you have better idea?
There are not really a good approach to compare objects of different types as it make little sense in practice. What would be the purpose of such comparisons.
Now even if you have a single class, if the ordering is not intransic to the type, it would be better to use an external class for sorting.
class CircleDiameterLess
{
public:
bool operator()(const Circle &lhs, const Circle &rhs)
{
return lhs.diameter < rhs.diameter;
}
};
That way, you can have multiple ways to sort data and it play nice with STL.
Another problem with your code if that it make little sense to have a class circle with a diameter that derives from a class potentialCircle with a radius. Your code will be hard to maintain because it is hard to understand.
You want to store either the diameter or the radius and compute the other one.
unsigned int get_diameter() const { return radius * 2; }
Member like alternative_center make no sense. A circle has only one center. If your class does not respect basic expectations, it will make the code hard to maintain as nobody would known that a circle has 2 centers including you in 3 months!
In a case like yours, it make make sense to add public accessors.
class potentialCircle
{
public:
unsigned int get_radius() const { return radius; }
....
};
That way, you can still make data private (or sometime protected) while having read only access to it. That way, you can write you comparison function as you wish. And in practice, if you have a class that represent a circle, you usually want at least being able to get basic properties like radius, aread, bounding rectangle by the way of a function.
Another thing is that public derivation as your (from potentialCircle) would only make senses if you have other classes that derives from it. However, if this is the case, then how would you compare the other kind of circles?
Notes:
With C++ 20, three way comparison would be even better.

How can I access to derived members from a pure virtual base class function?

I want to have a Collider interface class in which will have a overloaded -> operator to have access directy to the BoxCollider derived class. I want to have access to the members of box collider through the interface and chnage the type of collider at run-time.
So I thought of using templates:
template<typename T>
class ColliderV2 {
public:
virtual T* operator ->() = 0;
};
class BoxColliderV2 : public ColliderV2<BoxColliderV2> {
public:
float width;
float height;
BoxColliderV2* operator ->() {
return this;
}
};
int main()
{
ColliderV2<BoxColliderV2>* col = new BoxColliderV2;
(*col)->width = 1;
}
This works. But templates , as far as I know, will generate a brand new Collider class in compile-time filling T with Box Collider, correct? Thats why it worked. But later it prevents me from changing the collider type. I also thought of just making a virtual Collider class with Collider* operator->() ; overload in the derived class BoxCollider* operator->() ;
But if I tried :
Collider<BoxCollider>* col = new BoxCollider;
(*col)->width = 1; // won't work
doesn't work since Collider is not BoxCollider. And I don't want to dynamic_cast every possible collider type I could have. So, what can be done here?
As you've already found out, this doesn't work. Templates and runtime behavior are kind of contradicting mechanics. You can't create a common base class and let it act like a generic pointer to give you access to its derived types' members.
An interface specifies a contract against which you can code. You don't code against a specific implementation but the interface, so the interface has to provide all the members that you'd like to access. In your case this would result in width and height beeing part of ColliderV2 instead of BoxColliderV2. However this defeates the logic you are trying to mimic.
There are a few approaches that you can take:
Either make your collider type a variant, like
using ColliderType = std::variant<BoxColliderV2, MyOtherCollider, ...>;
and check for the actual type when you want to access the member
ColliderType myCollider = /* generate */;
if (auto boxCollider = std::get_if<BoxColliderV2>(&myCollider); boxCollider)
boxCollider->width = 0;
Or, keep the base class that you have, remove the operator-> and the template and do a dynamic cast on it:
ColliderV2* col = new BoxColliderV2;
if (auto boxCollider = dynamic_cast<BoxColliderV2*>(col); boxCollider)
boxCollider->width = 0;
You can also hide details like width or height behind more generic functions that are part of the interface. For example:
class ColliderV2 {
public:
virtual void setBounds(float width, float height) = 0;
};
class BoxColliderV2 : public ColliderV2 {
public:
void setBounds(float width, float height) override {
this->width = width;
this->height = height;
}
private:
float width;
float height;
};
int main()
{
ColliderV2* col = new BoxColliderV2;
col->setBounds(1, 1);
}
What you are trying to do is discouraged by C++. What you are trying to do is to change the type of something based on the return value of a function. The type system is designed to stop you from writing code like this.
One important restriction of a function is that can only return one type-of-thing. You can return one of a list of things if you wrap those possibilities in a class, and return that. In C++17, a ready-made class for this is std::variant. The restriction on this is that the list of things must be fixed (or a closed-set). If you want an arbitrary set of return values (open-set), you must use a different approach. You must restate your problem in terms a function that is done on the return value.
class BoxColliderV2 : public MyBaseCollider {
public:
void SetWidth(float new_width) override;
};
You may find this video useful. The bit of interest starts at around 40 minutes (but watch the whole video if you can). If you are interested in advice, I would suggest starting with std::variant, and if it works, move to virtual functions. Problems like collision detection get really complicated really quickly, and you will almost certainly require double dispatch at some stage. Start simple, because it's only going to get more complicated.
These excerpts from the ISO-Guidelines may help
1. When you change the semantic meaning of an operator, you make it
harder for other programmers to understand you code. guideline.
2. Dynamic casting is verbose and ugly, but deliberately so, because dynamic casting is dangerous, and should stand out. guideline
I think you are approaching the problem from the wrong direction. The purpose of an interface is that you don't have to know about the exact type or the implementation.
For example: You are using Axis-Aligned Bounding Boxes for collision detection. So, even if your CircleCollider uses a radius, you are still able to calculate its width and height from it. Now, you don't have to worry about if you are dealing with a BoxCollider or a CircleCollider, you have everything to make a Bounding Box.
class Collider
{
public:
virtual float x() const = 0;
virtual float y() const = 0;
virtual float width() const = 0;
virtual float height() const = 0;
};
class BoxCollider : public Collider
{
// Implementation...
};
class CircleCollider : public Collider
{
// Implementation...
};
Of course, you are maybe using something else, and not AABBs. I just wanted to demonstrate how you can use interfaces effectively.

benefits of interface c++?

look at this code
#include<iostream>
using namespace std;
//Shape is an Interface Class. No data and everything pure virtual
class Shape {
public:
virtual void Area(int length, int breadth) = 0;
virtual void Perimeter(int length, int breadth) = 0;
//Note, no data
};
//Derived class - Inherits Shape as Public
class Rectangle : public Shape {
public:
void Area(int length, int breadth);
void Perimeter(int length, int breadth);
private:
int someData;
};
//Derived class - Inherits Shape as Public
class Triangle : public Shape {
public:
void Area(int length, int breadth);
void Perimeter(int length, int breadth);
private:
int someData;
};
int main()
{
Rectangle r;
Triangle t;
cout<<"\n\n";
r.Area(3,4);
r.Perimeter(3,4);
t.Area(3,4);
t.Perimeter(3,4);
cout<<"\n\n";
return 0;
}
void Rectangle::Area(int length, int breadth)
{
cout<<"\nThe Area of Rectangle for length = "<<length<<" and\
breadth = "<<breadth<<" is "<<(length * breadth)<<endl;
}
void Rectangle::Perimeter(int length, int breadth)
{
cout<<"\nThe Perimeter of Rectangle for length = "<<length<<" and\
breadth = "<<breadth<<" is "<<2 * (length + breadth)<<endl;
}
void Triangle::Area(int length, int breadth)
{
cout<<"\nThe Area of Triangle for length = "<<length<<" and\
breadth = "<<breadth<<" is "<<(length * breadth)/2<<endl;
}
void Triangle::Perimeter(int length, int breadth)
{
cout<<"\nThe Perimeter of Triangle for length = "<<length<<" and\
breadth = "<<breadth<<" is "<<(length * breadth)/3<<endl;
}
I use interface in the code , but my question is what i should use it and what is the benefits from it , no performance , no real needed it , why i should i use it ( the interfaces ) . what is the point to use it , an you explain it please .
and thank you !
Abstract interfaces separate the interface from the implementation. Just as pimpl idiom it
decreases compilation time, and
lets you change the implementation without breaking the ABI.
Both are important advantages in large programs.
An interface could be used to have, say, a vector of different Shape objects. Otherwise you couldn't have a collection that mixes triangles and rectangles for example. Or another class could have a Shape member, which could then either be a triangle or rectangle. These are just some examples.
Edit:
Let me give a concrete example. Say you have an interface called Car. Imagine you want to have a class Garage that has room for a single Car. You've implemented different types of cars, that all use the Car interface. Then the Garage class could be something like:
class Garage {
public:
Car getCar(); // returns _theCar
private:
Car _theCar:
}
A common mistake when programming C++ (and other object oriented languages) is to use inheritance too much. Interfaces is inheritance done right. The reason is that the strength of interfaces is to be able to handle objects of different type in another system as if they were the same type. Triangle and Circle can both be Shapes for instance and can be passed to a graphics engine for drawing on the screen.
The reason interfaces are 'better' than inheritance that also includes inherited functionality is that it quickly becomes very hard to understand what a class really does, to debug it and verify that the internal state of the objects cannot be destroyed by using the external methods.
The need for this type of structure where you use interfaces more than sporadically is hard to motivate in a small example, but becomes obvious when a projects grows big. Besides making things like I describes above possible they are also good to make it easier to test the program since you can then replace the implementation of part of your program (lets say the database access for instace) with a stubbed implementation and by doing that enable you to write automatic tests that verifies other parts of the program (processing the data for instance)
There are no performance reasons to choose interface over direct access to members, rather the opposite since you will call methods that are virtual. This is however a very minor performance penalty in the majority of cases.
Have a look here for more on C++ MI - Why should I avoid multiple inheritance in C++?.
Building up on the "3 Interfaces" section and ybungalobill's answer, consider the typical Observer pattern:
class MyClass : public IScreenListener
{
public:
MyClass()
{
PowerManager::RegisterScreenListener(*this);
}
// Overriding from IScreenListener
void OnScreenOn()
{
// do as you like
}
void OnScreenOff()
{
// do as you like
}
}
Here, the IScreenListener interface provides the two pure virtual methods OnScreenOff and OnScreenOn which are to be implemented in your code. This example is based on Bada's screen listener: it allows you to get notified by such events.
Of course, there are other benefits. Like hiding a code library implementation details from its users, and more.
Interfaces (Pure Abstract classes) provide general functionality. In your example, the class Shape is generic. Which means you cannot have a actual instance (or object) from the class Shape. Where as you can say a Rectangle is a Shape or a Triangle is a Shape. You cannot calculate Area or Perimeter of a Shape unless you know what Shape it is.
Interfaces (Pure Abstract classes) enforce a protocol that a class that is derived from it must implement all of its methods. Otherwise, it also becomes an interface. Interfaces pointers can be sent to functions or other classes and from there you can call the actual derived classes functionality.
For example, if there is a class called Animal from where you derive all animals like dogs, snakes, humans etc, you can send the array of Animal pointers (which are actually instances of it's derived classes) and then call the functionality like Run(), Walk(), Eat() etc. Animal in this case is generic class.

Adding virtual functions without modifying the original classes

Let's say we already have a hierarchy of classes, e.g.
class Shape { virtual void get_area() = 0; };
class Square : Shape { ... };
class Circle : Shape { ... };
etc.
Now let's say that I want to (effectively) add a virtual draw() = 0 method to Shape with appropriate definitions in each sub-class. However, let's say I want to do this without modifying those classes (as they are part of a library that I don't want to change).
What would be the best way to go about this?
Whether or not I actually "add" a virtual method or not is not important, I just want polymorphic behaviour given an array of pointers.
My first thought would be to do this:
class IDrawable { virtual void draw() = 0; };
class DrawableSquare : Square, IDrawable { void draw() { ... } };
class DrawableCircle : Circle, IDrawable { void draw() { ... } };
and then just replace all creations of Squares and Circles with DrawableSquares and DrawableCircles, respectively.
Is that the best way to accomplish this, or is there something better (preferably something that leaves the creation of Squares and Circles intact).
Thanks in advance.
(I do propose a solution down further... bear with me...)
One way to (almost) solve your problem is to use a Visitor design pattern. Something like this:
class DrawVisitor
{
public:
void draw(const Shape &shape); // dispatches to correct private method
private:
void visitSquare(const Square &square);
void visitCircle(const Circle &circle);
};
Then instead of this:
Shape &shape = getShape(); // returns some Shape subclass
shape.draw(); // virtual method
You would do:
DrawVisitor dv;
Shape &shape = getShape();
dv.draw(shape);
Normally in a Visitor pattern you would implement the draw method like this:
DrawVisitor::draw(const Shape &shape)
{
shape.accept(*this);
}
But that only works if the Shape hierarchy was designed to be visited: each subclass implements the virtual method accept by calling the appropriate visitXxxx method on the Visitor. Most likely it was not designed for that.
Without being able to modify the class hierarchy to add a virtual accept method to Shape (and all subclasses), you need some other way to dispatch to the correct draw method. One naieve approach is this:
DrawVisitor::draw(const Shape &shape)
{
if (const Square *pSquare = dynamic_cast<const Square *>(&shape))
{
visitSquare(*pSquare);
}
else if (const Circle *pCircle = dynamic_cast<const Circle *>(&shape))
{
visitCircle(*pCircle);
}
// etc.
}
That will work, but there is a performance hit to using dynamic_cast that way. If you can afford that hit, it is a straightforward approach that is easy to understand, debug, maintain, etc.
Suppose there was an enumeration of all shape types:
enum ShapeId { SQUARE, CIRCLE, ... };
and there was a virtual method ShapeId Shape::getId() const = 0; that each subclass would override to return its ShapeId. Then you could do your dispatch using a massive switch statement instead of the if-elsif-elsif of dynamic_casts. Or perhaps instead of a switch use a hashtable. The best case scenario is to put this mapping function in one place, so that you can define multiple visitors without having to repeat the mapping logic each time.
So you probably don't have a getid() method either. Too bad. What's another way to get an ID that is unique for each type of object? RTTI. This is not necessarily elegant or foolproof, but you can create a hashtable of type_info pointers. You can build this hashtable in some initialization code or build it dynamically (or both).
DrawVisitor::init() // static method or ctor
{
typeMap_[&typeid(Square)] = &visitSquare;
typeMap_[&typeid(Circle)] = &visitCircle;
// etc.
}
DrawVisitor::draw(const Shape &shape)
{
type_info *ti = typeid(shape);
typedef void (DrawVisitor::*VisitFun)(const Shape &shape);
VisitFun visit = 0; // or default draw method?
TypeMap::iterator iter = typeMap_.find(ti);
if (iter != typeMap_.end())
{
visit = iter->second;
}
else if (const Square *pSquare = dynamic_cast<const Square *>(&shape))
{
visit = typeMap_[ti] = &visitSquare;
}
else if (const Circle *pCircle = dynamic_cast<const Circle *>(&shape))
{
visit = typeMap_[ti] = &visitCircle;
}
// etc.
if (visit)
{
// will have to do static_cast<> inside the function
((*this).*(visit))(shape);
}
}
Might be some bugs/syntax errors in there, I haven't tried compiling this example. I have done something like this before -- the technique works. I'm not sure if you might run into problems with shared libraries though.
One last thing I'll add: regardless of how you decide to do the dispatch, it probably makes sense to make a visitor base class:
class ShapeVisitor
{
public:
void visit(const Shape &shape); // not virtual
private:
virtual void visitSquare(const Square &square) = 0;
virtual void visitCircle(const Circle &circle) = 0;
};
What you're describing is somewhat like the decorator pattern. Which is very suitable to change runtime behaviour of existing classes.
But I don't really see how to implement your practical example, if shapes have no way to be drawn, then there's no way to change drawing behaviour at runtime either...
But I suppose this is just a very simplified example for stackoverflow? If all the basic building blocks for the desired functionality are available, then implementing the exact runtime behaviour with such a pattern is certainly a decent option.
One 'off the wall' solution you might like to consider, depending on the circumstance, is to use templates to give you compile time polymorphic behaviour. Before you say anything, I know that this will not give you traditional runtime polymorphism so it may well not be useful but depending on the limitations of the environment in which you're working, it can prove useful:
#include <iostream>
using namespace std;
// This bit's a bit like your library.
struct Square{};
struct Circle{};
struct AShape{};
// and this is your extra stuff.
template < class T >
class Drawable { public: void draw() const { cout << "General Shape" << endl; } };
template <> void Drawable< Square >::draw() const { cout << "Square!" << endl; };
template <> void Drawable< Circle >::draw() const { cout << "Circle!" << endl; };
template < class T >
void drawIt( const T& obj )
{
obj.draw();
}
int main( int argc, char* argv[] )
{
Drawable<Square> a;
Drawable<Circle> b;
Drawable<AShape> c;
a.draw(); // prints "Square!"
b.draw(); // prints "Circle!"
c.draw(); // prints "General Shape" as there's no specific specialisation for an Drawable< AShape >
drawIt(a); // prints "Square!"
drawIt(b); // prints "Circle!"
drawIt(c); // prints "General Shape" as there's no specific specialisation for an Drawable< AShape >
}
The drawIt() method is probably the key thing here as it represents generic behaviour for any class meeting the requirement of having a draw() method. Do watch out for code bloat here though as the compiler will instantiate a separate method for each type passed.
This can be useful in situations where you need to write one function to work on many types which have no common base class. I'm aware that this is not the question you asked, but I thought I'd throw it just as an alternative.