Dynamic ad hoc polymorphism in C++ - c++

In my C++ code, I have a collection of classes A1, A2, ..., all derived from a class A. I also have a vector v<*A> holding pointers to objects all of type A. I want to implement a function foo(A *x, A *y) that is dynamically ad hoc polymorphic in the type of x and y. To make this more concrete, imagine A is Shape, A1, A2, ..., are Circle, Rect, ..., and foo is intersects(Shape *x, Shape *y).
I could overload foo with declarations like foo(A1 *x, A2 *y) for the combinations of derived types of A that I care about, however this will not work for the objects referenced by my vector v since function overloading, unlike virtual methods, is handled statically. I also cannot use virtual methods of the form A1::foo(A2 *y) since this only dynamically resolves the type of the method's class (A1) and not the type of the argument (A2).
The only solution I've thought of is to implement foo as follows:
void foo(A *x, A*y) {
if (A1* a1 = dynamic_cast<A1*>(x)) {
if (A1* a1 = dynamic_cast<A1*>(y)) {
...
}
...
}
if (A2* a2 = dynamic_cast<A2*>(x)) {
if (A1* a1 = dynamic_cast<A1*>(y)) {
...
}
...
}
...
}
However, I've always been told that resorting to dynamic casts is rarely a good idea. Is there a more idiomatic way to achieve this?

This is double dispatch. The visitor pattern can accomplish this. You need a virtual function which makes the first type concrete to apply a visitor. And then you need another virtual function to make the second type concrete and return a visitor:
struct Shape
{
// Each derived class simply calls visit with the concrete type:
// return visitor.visit(*this);
virtual bool accept(const IntersectionVisitor& visitor) const = 0;
// Each derived class return a visitor which knows how to calculate
// the intersection of this particular class type with all types of
// shapes. The visit() overrides of this visitor have access to both
// concrete shape types.
virtual IntersectionVisitor intersection_visitor() const = 0;
};
struct IntersectionVisitor
{
// Calculate the intersection of this concrete shape with a Circle
virtual bool visit(const Circle&) = 0;
// Calculate the intersection of this concrete shape with a Rect
virtual bool visit(const Rect&) = 0;
};
bool intersects(const Shape& shape1, const Shape& shape2)
{
return shape2.accept(shape1.intersection_visitor());
}
Just because you can doesn't mean you should. You can do this more simply with variants:
using Shape = std::variant<Circle, Rect, ...>;
bool intersects(const Circle&, const Circle&) { ... }
bool intersects(const Circle&, const Rect&) { ... }
// all shape combinations, like before
// Visitation is just std::visit:
bool intersects(const Shape& shape1, const Shape& shape2)
{
return std::visit([](const auto& s1, const auto& s2) {
return intersects(s1, s2);
}, shape1, shape2);
}

Related

Passing object of base class to function of reference to derived class

I'm trying to write code which can find the distance between lots of different types of shapes. I've defined a base class Shape with a virtual distance(Shape& otherShape) function to find the distance to another shape and then want to define that for all my derived classes.
The problem is that there are lots of possible pairs of shapes, so my solution was to define a set of distance functions outside the classes (circle-circle, circle-square, square-tri etc.) and then call the corresponding one from the distance function. I've added a mini example of what I mean below, with just one derived class Circle to demonstrate the problem.
When I try and call my specific circleCircleDistance function I get an error because it can't convert the base class into the derived class. Is there any way I can address this or will my design as it stands just not work?
enum ShapeType{CIRCLE, SQUARE};
class Shape {
public:
ShapeType type;
virtual double distance(Shape& otherShape) = 0;
};
class Circle : public Shape {
public:
ShapeType type = CIRCLE;
double distance(Shape& otherShape) override;
};
double circleCircleDistance(Circle& circle1, Circle& cirlce2){
return 0; //pretend this does the calculation
};
double Circle::distance(Shape &otherShape) {
switch (otherShape.type){
case CIRCLE:
//Here I get the error
//cannot bind base class object of type Shape to derived class reference Circle& for 2nd argument
return circleCircleDistance(*this, otherShape);
}
}
You would have to cast the Shape& to a Circle&
return circleCircleDistance(*this, static_cast<Circle&>(otherShape));
As an aside, I'd handle your types a bit differently
class Shape {
public:
virtual ShapeType get_type() const = 0; // derived classes must override this
virtual double distance(Shape& otherShape) = 0;
};
class Circle : public Shape {
public:
ShapeType get_type() const override { return CIRCLE; } // here's your override
double distance(Shape& otherShape) override;
};
...
{
switch (otherShape.get_type()){
Otherwise you're going to get into a situation where type is shadowed from the derived/base classes depending how you access it.
Multiple dispatch is not natively supported in C++.
We only have single dispatch thanks to virtual method.
So you can implement double dispatch for your cases.
An (C++17) "alternative" option is to use std::variant, which has std::visit which implement multiple dispatch:
You can keep inheritance or drop it.
struct Circle {
Point center;
float radius;
};
struct Rectangle {
Point topLeft;
Point bottomRight
};
using Shape = std::variant<Square, Rectangle>;
double distance(const Square&, const Square&);
double distance(const Square&, const Rectangle&);
double distance(const Rectangle&, const Square&);
double distance(const Rectangle&, const Rectangle&);
double distance(const Shape& shape1, const Shape& shape2)
{
return std::visit([](const auto& shape1, const auto& shape2){
return distance(shape1, shape2);
},
shape1,
shape2);
}
In c++20 you could use template specialization with concepts for this kind of problem

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).

C++ virtual function override

I have a class that contains the following virtual method:
struct point {
template<typename T>
virtual typename std::enable_if<std::is_base_of<point, T>::value, double>::type distTo(T &other) const = 0;
};
The above doesn't work because:
error: templates may not be ‘virtual’
The plan is to specialize the class by making more specific instances of it like point2D, point3D. However, I only want the function to work with types of the same class. So if point2D where to inherit this class, the method distTo should only take parameter of type point2D. How can I accomplish this?
This is what I had tried before I did the above:
virtual double distTo(point& other) = 0;
But when I override this method in the point2D class and try to replace the parameter with one of type point2D, I run into compiler errors.
Thanks for your time
This sounds like the Curiously Recurring Template Pattern. Furthermore, this is completely incompatible with dynamic indirection, as the compiler cannot statically verify the dynamic type (obviously). But the CRTP can only be used to implement the function, not declare it.
template<typename T> class Point {
public:
double distTo(T other) {
/* stuff */
}
};
class Point2D : public Point<Point2D> {
// distTo automatically defined
};
Fundamentally, the interface you are trying to declare is completely impossible because you're asking the compiler to statically typecheck dynamic types. There is no solution that offers all the properties you want.
I think your requirements make no sense for a static typed language such as C++.
Think about how would you be able to use your virtual function:
point2d p1, p2;
point3d p3;
point &p = p1;
p.distTo(p2); //ok?
p.distTo(p3); //error?
That is simply not possible, because at compile time the compiler will not know if p is a reference to a point2d or a point3d, only at runtime.
You could add an explicit cast and a runtime assertion if you do it wrong, but I think that it make little sense. Simply do:
struct point { /*...*/ };
struct point2d : point {
double distTo(const point2d &other);
};
struct point3d : point {
double distTo(const point3d &other);
};
And do not call distTo using base point references.
UPDATE: If you know that your list is homogeneous, but you don't know the cardinality, then you can do:
struct point {
virtual double distTo(const point &other) =0;
};
struct point2d : point {
double distTo(const point2d &other) { /*...*/ }
virtual double distTo(const point &other) {
const point2d &other2 = static_cast<const point2d &>(other);
return distTo(other2);
}
};
struct point3d : point {
double distTo(const point3d &other) { /*...*/ }
virtual double distTo(const point &other) {
const point3d &other3 = static_cast<const point3d &>(other);
return distTo(other3);
}
};
But beware! If you call point::distTo with a wrong object, the result will be undefined!

Call templated function with derived class arguments using base class pointers

I have different geometries, which all derived from a base class in order to collect them in a vector. Now I would like to detect collisions between two geometries in this vector. The intersect function is templated with the two geometry types (polymorphism works only with one object as far as I know).
How is it possible to call intersect() correctly? Is there a way without dynamic_cast and checking for != nullptr? Save the enum as constexpr inside the geometry class and use static_cast?
Thank you very much.
enum class GeometryType
{
BOX,
SPHERE,
CAPSULE,
CONE,
CYLINDER,
HALFSPACE
};
class GeometryBase
{
public:
GeometryBase() {}
virtual ~GeometryBase() {}
};
template<enum GeometryType GeomType>
class Geometry : public GeometryBase
{
public:
Geometry() {}
virtual ~Geometry() {}
};
template<enum GeometryType GeomType1, enum GeometryType GeomType2>
void intersect(Geometry<GeomType1>* geom1, Geometry<GeomType2>* geom2)
{
// math stuff
}
void detectCollisions(GeometryBase* geomBase1, GeometryBase* geomBase2)
{
// what can I do here to call the correct intersect<...,...>(...,...)?
}
EDIT: The intersect function is provided by the FCL library, so I cannot change it...
The guideline
Polymorphism should generally be preferred over switch statements which check for a type, because the resulting code is much more type-safe and you get compile-time errors instead of runtime-errors which is generally a good thing. You have the interesting case of a function which takes two objects and which should be dispatched depending dynamically on both argument types.
This is how it's done
Here's one way how to do that: First you need to forward declare all derived classes and write the base class in the following way:
class Box;
class Sphere;
class Cone;
// ...
class GeometryBase
{
public:
virtual bool collidesWith( const GeometryBase & other ) const = 0;
protected:
virtual bool dispatchCollidesWith( const Box & other ) const = 0;
virtual bool dispatchCollidesWith( const Sphere & other ) const = 0;
virtual bool dispatchCollidesWith( const Cone & other ) const = 0;
// ...
};
The function collidesWith() must be implemented to call the dispatchCollidesWith() on other with *this as the argument. Note that *this has different types in the derived classes and hence the different overloads are called. In order to omit too much typing we use a template which does the implementation for us:
template <typename T>
class GeometryImpl : public GeometryBase
{
public:
virtual bool collidesWith( const GeometryBase & other ) const
{
assert( typeid(*this) == typeid(T) );
return other.dispatchCollidesWith( static_cast<const T&>(*this) );
}
};
Now the derived classes can be implemented in the following way:
class Box : public GeometryImpl<Box>
{
protected:
virtual bool dispatchCollidesWith( const Box & other ) const { /* do the math */ }
virtual bool dispatchCollidesWith( const Sphere & other ) const { /* do the math */ }
virtual bool dispatchCollidesWith( const Cone & other ) const { /* do the math */ }
// ...
private:
// data ...
};
Given two geometries geom1 and geom2 you can now test for collision with
geom1.collidesWith( geom2 );
And everything is perfectly type-safe.
A more extensible pattern
There's a backside to this approach: You have to add tons of functions to your base class and it will get crowded easily. Here's how you can make your base class extensible for virtual dispatch, so you don't need to add a virtual function to it everytime you need new functionality:
class GeometryDispatcher;
class GeometryBase
{
public:
void run( GeometryDispatcher & dispatcher ) const = 0;
};
class GeometryDispatcher
{
public:
virtual void dispatch( const Box & shape ) = 0;
virtual void dispatch( const Sphere & shape ) = 0;
virtual void dispatch( const Cone & shape ) = 0;
};
By deriving from GeometryDispatcher you can add a new functionality to your class hierarchy. The run() function must be implemented by the derived classes of GeometryBase in the following way:
void Box::run( GeometryDispatcher & dispatcher ) const
{
dispatcher.dispatch( *this );
}
(This is also known as the first half of the visitor pattern. Read about it, so you can understand my code!) Now you could add a function collidesWithBox() in the following way
class CollidesWithBoxDispatcher : public GeometryDispatcher
{
public:
CollidesWithBoxDispatcher( const Box & box ) : box(box) {}
bool getResult() const { return result; }
virtual void dispatch( const Box & shape ) { ... }
virtual void dispatch( const Sphere & shape ) { ... }
virtual void dispatch( const Cone & shape ) { ... }
private:
bool result;
const Box & box;
};
bool collidesWithBox( const GeometryBase & shape, const Box & box )
{
CollidesWithBoxDispatcher d( box );
shape.run( d );
return d.result;
}
You can implement the functions collidesWithSphere() and collidesWithCone() analogously. Finally, you can implement the function collidesWith() this way:
class CollidesWithDispatcher : public GeometryDispatcher
{
public:
CollidesWithDispatcher( const GeometryBase & shape ) : shape(shape) {}
bool getResult() const { return result; }
virtual void dispatch( const Box & box )
{
result = collidesWithBox( shape, box );
}
virtual void dispatch( const Sphere & sphere ) { ... }
virtual void dispatch( const Cone & cone ) { ... }
private:
bool result;
const GeometryBase & shape;
};
bool collidesWith( const GeometryBase & shape1, const GeometryBase shape2 )
{
CollidesWithDispatcher d( shape2 );
shape1.run( d );
return d.result;
}
A lot of code to write but you get a double dispatch this way by facilitating the visitor pattern. Happy end. :)
In intersect, I'm guessing that:
// math stuff
is doing three things:
Get some values from geom1
Get some values from geom2
Do some math using the above values and process the result
If that's the case, here's how I'd do this.
First, make intersect a non-template function that takes two parameters, both of type GeometryBase*.
Add pure virtual methods to GeometryBase which defines an interface to return the "values" needed in steps 1 & 2 above. You may need to give a bit of a think to how best to represent these values in a sufficiently generic way so that all subclasses can return the same type of things.
Implement the pure virtaul in each of the GeometryBase-derived concrete classes.
Implement intersect in terms of calling these virtual methods (via the GeometryBase pointer; no need to cast in any way or apply the visitor pattern).
You could make intersect a free function (in a namespace, please!), or possibly implement it in a class. It might (or might not) to make this a member function of GeometryBase, in which case this would be used in leiu of one of the GeometryBase* parameters.
You should better use inheritance instead of using an enum. Then you could create a generic intersect function that takes GeometryBase objects as arguments: it will be generic for GeometryBase children.
It means that you must provide the needed interface in GeometryBase in order to compute the intersection:
class GeometryBase
{
//...
public: // your interface
virtual Vertices getVertices() const; // can be virtual, or not
//...
}
void intersect(GeometryBase* geom1, GeometryBase* geom2)
{
// math stuff, calling geom1->getVertices() & ...
}
//specializations
class Box: public GeometryBase
{
public:
virtual Vertices getVertices() const; // implement what you need to specialize
}
class Cone : public GeometryBase
{
}
// etc...
Then you vector should look like:
std::vector<GeometryBase*> geometries;
The way I would approach this would be manual double dispatch with help from a type mapping.
Here is a really simple version:
class GeometryBase
{
public:
GeometryBase() {}
virtual GeometryBase() {}
virtual void doIntersect( GeometryBase* other ) = 0;
virtual GeometryType GetGeometryType() const = 0;
};
// forward decl:
template<enum GeometryType GeomType1, enum GeometryType GeomType2>
void intersect(Geometry<GeomType1>* geom1, Geometry<GeomType2>* geom2);
template<enum GeometryType GeomType>
class Geometry : public GeometryBase
{
public:
Geometry() {}
virtual Geometry() {}
GeometryType GetGeometryType() const { return GeomType; }
virtual void doIntersect( GeometryBase* other ) {
switch (other->GetGeometryType()) {
case GeometryType::BOX:
intersect( this, static_cast<GeometryType<GeometryType::BOX>*>(other) );
break;
// etc
}
}
};
where we do manual dynamic double dispatch in a switch statement.
We can build some infrastructure to make this easier in a number of ways. You can write a fast_cast that supports casting your base class into a number of subclasses using only a virtual function lookup and call (which is faster than a dynamic_cast typically). You can write a function that replaces the copy/paste case expressions above with a magic switch that does it (but the boilerplate to do that is longer than the above code).
You could write a macro to write out the cases (thereby obeying DRY, and reducing some categories of errors).
But the basic problem is one of double dispatch, and double dispatch requires manual work on the part of the programmer in C++.

Adding class functionality via composition

Suppose we have an abstract class Element from which classes Triangle and Quadrilateral are derived from.
Suppose yet that these classes are used in conjunction with interpolation methods that depend on the shape of the element. So, basically we create an abstract class InterpolationElement from which we derive InterpolationTriangle and InterpolationQuadrilateral.
Then, to include the interpolation functionality in the Triangle and Quadrilateral classes, we add a const-reference data member in class Element of type InterpolationElement, that is:
class Element
{
public:
Element(const InterpolationElement& interp);
const InterpolationElement& getInterpolation() const;
private:
const InterpolationElement& interpolation;
};
We then create a method (as described by Scott Meyers, Effective C++) that instanciate a local static object of class InterpolationTriangle as
const InterpolationTriangle& getInterpolationTriangle()
{
static InterpolationTriangle interpolationTriangle;
return interpolationTriangle;
}
So that class Triangle can be constructed like:
class Triangle : public Element
{
public:
Triangle() : Element( getInterpolationTriangle() ) {}
};
Here is my question: is this approach correct in order to incorporate interpolation methods on my class Element? Is this used in professional scenarios?
I could implement directly all the interpolation methods on class Element (as pure virtual) and the override them in the derived classes Triangle and Quadrilateral. However, this approach seems to me to be cumbersome, since every time I need to improve or implement new interpolation functionalities I would have to do that on these classes. Moreover, the classes get bigger and bigger (many methods) using this approach.
I would like to hear from you some tips and comments
Thanks in advance.
Additional details:
class InterpolationElement
{
public:
InterpolationElement();
virtual double interpolationMethod1(...) = 0;
:
virtual double interpolationMethodN(...) = 0;
}
class InterpolationTriangle : public InterpolationElement
{
public:
InterpolationTriangle () {}
virtual double interpolationMethod1(...) { // interpolation for triangle }
:
virtual double interpolationMethodN(...) { // interpolation for triangle }
}
class InterpolationQuadrilateral : public InterpolationElement
{
public:
InterpolationTriangle () {}
virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
:
virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
}
The classes are used in conjunction with interpolation methods. Why do those methods need to be in a singleton object? The singleton here looks very problematic.
class Element
{
public:
virtual double interpolationMethod1(...) = 0;
:
virtual double interpolationMethodN(...) = 0;
};
class Triangle : public Element
{
public:
virtual double interpolationMethod1(...) { // interpolation for triangle }
:
virtual double interpolationMethodN(...) { // interpolation for triangle }
}
Also, welcome to SO!
This is reminiscent of a question that I had answered here. The same idea about the separation of data containers and the strategies.
There is one little issue with your proposal: you have added an interpolation related method to your base class and you've changed the constructor...
So first of all, if you wish to do it this way, here is how you should do it:
class Element
{
public:
private:
// similar signature to a `clone` method
virtual InterpolationElement* interpolation() const = 0;
};
class Triangle
{
public:
private:
virtual InterpolationTriangle* interpolation() const
{
return new InterpolationTriangle();
}
};
There are 2 advantages here:
It's no longer necessary to change the constructor of each of the derived objects
The strategy object is no longer const, which allows it to maintain state during the computation... like a reference to the current object being interpolated.
However, this still requires to change the Element class, and each of its derived classes. Doesn't it bother you ;) ?
Well, it's time (for once) to call upon a Design Pattern: Visitor.
It's a little different from the strategy idea, relying on double dispatch to work properly. However it allows you to tweak the hierarchy of Elements ONCE (with an accept method) and then to add as many operations as you wish. And that is great.
You can always mess a little bit with templates.
First we have a top class.
class Element {
public:
virtual void calculate() const = 0;
};
... but then we also have a class in the middle of the hierarchy which is actually a template. Template can't be the top level class, as templates with different parameters are different classes. The idea is that we give an interpolation class as a type parameter to the element.
template <typename Interpolation>
class Element_Impl : public Element {
protected:
Interpolation m_interpolation;
};
And interpolation classes. Notice, they aren't siblings, because they don't need to.
class InterpolationTriangle {
public:
double interpolate(double a, double b) const {
std::cout << "interpolation triangle" << std::endl;
}
};
class InterpolationQuadrilateral {
public:
double interpolate(double a, double b) const {
std::cout << "interpolation quadrilateral" << std::endl;
}
};
And finally the real elements and the small main procedure.
class Triangle : public Element_Impl<InterpolationTriangle> {
public:
void calculate() const {
m_interpolation.interpolate(1.0, 2.0);
}
};
class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
public:
void calculate() const {
m_interpolation.interpolate(2.0, 3.0);
}
};
int main() {
const Element &a = Triangle();
const Element &b = Quadrilateral();
a.calculate();
b.calculate();
}
Summary:
you can easily switch interpolation class for each element if needed.
there aren't double vtable access (first for Element's calculate and then for InterpolationElement's intepolate methods) as in the Matthieu's example. Each element knows at compile time which interpolation class it is using.
Element_Impl is an ugly bit, but it saves us from copypasta. You can expand it even further by implementing interpolation method wrappers
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
One way is to use static methods, and defining a wrapper in Element_Impl - still only in one place.
class Element {
public:
virtual void calculate() const = 0;
};
template <typename Interpolation>
class Element_Impl : public Element {
protected:
void interpolate(double, double) const {
Interpolation::interpolate(1, 1);
}
};
class InterpolationTriangle {
public:
static double interpolate(double a, double b) {
std::cout << "interpolation triangle" << std::endl;
}
};
class InterpolationQuadrilateral {
public:
static double interpolate(double a, double b) {
std::cout << "interpolation quadrilateral" << std::endl;
}
};
class Triangle : public Element_Impl<InterpolationTriangle> {
public:
void calculate() const {
interpolate(1.0, 2.0);
}
};
class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
public:
void calculate() const {
interpolate(2.0, 3.0);
}
};
int main() {
const Element &a = Triangle();
const Element &b = Quadrilateral();
a.calculate();
b.calculate();
}
What first comes to my mind is the GoF Design Pattern Visitor
From what I understand of your problem, this pattern is conceived to exactly solve this issue.
Each Visitor object defines an interpolation technique, or an algorithm to apply to your object.
Thus the Element class doesn't grow at all with each new functionnality. Once in place, the Visitor pattern enables to enrich functionnality without touching to the Base class definition.