0
votes

I'm sorry if this question has already been asked, but I've been looking for hours now and I only find answers to similar but not-quite-the-same problems, and none of them work in my case. I'm a novice programmer, so it's likely that my code doesn't work either due to cringe-worthy design or due to a very simple, stupid mistake. I have an alternative design in mind, but I would like to know whether the design is literally impossible or whether I'm just implementing it incorrectly.

So here's what I'm trying to do: I'm writing 2D collision detection code, which needs to work for both polygons and circles. Polygons need to be able to detect collision on circles and on other polygons, and circles need to be able to detect collision on polygons and other circles. I thought it would be nice to be able to put all collidable objects (polygons and circles) into one std::vector called "Collidables", rather than having a separate vector for each class. So I created the abstract Shape class, which would be the parent of both Circle and Polygon and which would hold virtual collision detection functions. The Collidables vector would then be a sequence of Shape pointers. Here's what these classes look like:

//in shape.h:
#ifndef __SHAPE_H_INCLUDED__
#define __SHAPE_H_INCLUDED__
class Polygon;
class Circle;

class Shape {
//number of functions and variables irrelevant to the issue
//relevant functions, note that Vector with capital 'V' is not std::vector but a class I wrote myself:
    virtual Vector* detectCollision(const Shape& Collidable) const = 0;
    virtual Vector* detectCollision(const Polygon& Collidable) const = 0;
    virtual Vector* detectCollision(const Circle& Collidable) const = 0;
};
#endif


//in polygon.h
#ifndef __POLYGON_H_INCLUDED__
#define __POLYGON_H_INCLUDED__
#include "shape.h"
//edit: deleted #include "circle.h", this is now included in polygon.cpp
//edit: deleted redundant Circle forward declaration

class Polygon: public Shape {
    //irrelevant stuff
    Vector* detectCollision(const Shape& Collidable) const {return Collidable.detectCollision(*this)};
    Vector* detectCollision(const Polygon& Collidable) const;
    Vector* detectCollision(const Circle& Collidable) const;
};
#endif


//in circle.h
#ifndef __CIRCLE_H_INCLUDED__
#define __CIRCLE_H_INCLUDED__
#include "shape.h"
//edit: deleted #include "polygon.h", this is now included in circle.cpp
class Circle: public Shape {
    //irrelevant stuff
    Vector* detectCollision (const Shape& Collidable) const {return Collidable.detectCollision(*this);}
    Vector* detectCollision (const Polygon& Collidable) const;
    Vector* detectCollision (const Circle& Collidable) const;
};
#endif

The idea here is that any Shape should be able to detect collision on any other Shape without even being aware whether it's detecting collision on a polygon or on a circle at the time of calling the detectCollision function. Imagine you're a circle and you're iterating over the Collidables std::vector. The first element is a Polygon, but you don't know that, you only know that you are a circle and that the parameter your detectCollision function is getting is a reference to a Shape. So you tell that Shape "Look, I don't know what you are, but I'm a circle. Here, I'll pass myself as a parameter to your detectCollision function and so you can return the result to me". So the Shape gets its virtual detectCollision(const &Circle) called, which is then passed on to the detectCollision(const &Circle) of its child, Polygon, which has an actual working implementation and which should then return a pointer to a point of intersection (or a null pointer if there is no intersection).

This worked fine when only the Shape and Polygon classes were written, but once I added the Circle class, my code wouldn't compile anymore. The error I'm getting is:

In file included from polygon.cpp:1:0: polygon.h: In member function ‘virtual Vector* Polygon::detectCollision(const Circle&) const’: polygon.h:45:78: error: invalid use of incomplete type ‘const struct Circle’ shape.h:8:7: error: forward declaration of ‘const struct Circle’

I'm assuming this is a design issue because of the ridiculous amount of circular dependency going on here: Shape depends on its children because some of its virtual functions accept children as parameter, Polygon depends on Shape (because it inherits from it) and Circle (because it has a detectCollision function accepting a Circle& parameter) and Circle depends on Shape (because it inherits from it) and Polygon (because it has a detectCollision function accepting a Polygon& parameter). The easiest way out is probably just giving up the idea of putting Polygons and Circles into one sequence container and just putting them into two separate std::vectors. Still, I would like to know whether my first design is just inherently impossible, possible but ugly, or solid but incorrectly implemented. I usually prefer to find this kind of stuff out by myself, but after several hours of searching and trying different solutions, I have to admit that I simply do not understand this problem.

(edit: some code changes suggested by Dave)

EDIT, link to full source, if someone would want more information: https://github.com/KoenP/medieval-melee-combat-cpp

I am only compiling the files polygon.cpp, circle.cpp, vector.cpp and line.cpp; test.cpp is currently irrelevant. Not that vector and line matter all that much for this error, not compiling them (only compiling polygon and circle) gives exactly the same error. The exact command I'm using is "g++ -o test circle.cpp polygon.cpp line.cpp vector.cpp".

1

1 Answers

1
votes

This kind of design is somewhat common. The way to make it work is to forward declare in the headers (don't #include) and to #include in the cpps. You were on the right track...

In circle.h and polygon.h include only shape.h. Remove class Circle; from polygon.h (it's redundant since you already got it from including Shape.h). Then, in polygon.cpp, #include "circle.h". And in circle.cpp, #include "polygon.h".