1

I want to create a collision system, in which a base class represents an object in the scene, implements all the collision logic, and when a collision is detected, a derived class function is called for the program logic. The problem that i am facing, is that the base class needs to know about all the derived classes, for the dispatch in different functions to work correctly.

Example, base class, OnCollisionDetected will be overridden by the derived class to handle the collision

#include <iostream>

class BasePhysicsObject {
public:

    void Collides(BasePhysicsObject * another_object) {
        /* ... */
        bool collides = true;

        if (collides) this->OnCollisionDetected(another_object);
        return;
    }

    /* Function to be overriden */
    virtual void OnCollisionDetected(BasePhysicsObject * another_object) = 0;
};

Two dummy classes in the scene, with the function OnCollisionDetected(BasePhysicsObject * another_object) overridden, to dispatch the call to the appropriate function based on the this argument.

class Fire;

class Player : public BasePhysicsObject {
public:

    virtual void OnCollisionDetected(BasePhysicsObject * another_object) {
        /* double dispatch to specific implementation */
        another_object->OnCollisionDetected(this);
    }

    virtual void OnCollisionDetected(Fire * fire) {
        /* Collision with fire object*/
    }
};

class Fire : public BasePhysicsObject {
public:

    virtual void OnCollisionDetected(BasePhysicsObject * another_object) {
        /* double dispatch to specific implementation */
        another_object->OnCollisionDetected(this);
    }

    virtual void OnCollisionDetected(Player * player) {
        /* Collision with player object */
    }
};

Main function creates two objects, and checks their collision.

int main(int argc, char ** argv){

    Player * player = new Player();
    Fire * fire = new Fire();

    fire->Collides(player);
}

What ends up happening, is that Fire::OnCollisionDetected(BasePhysicsObject * another_object) which is called from Collides() does not call the function with the derived class as argument i.e Player::OnCollisionDetected(Fire * fire), but rather the Player::OnCollisionDetected(BasePhysicsObject * another_object) which again calls back the function, resulting in a stack overflow. As i am given to understand, in order for double dispatch to work, I need to declare OnCollisionDetected(Derived *) in the base class for all derived classes, but this is a daunting solution. Is there any other way to do it?

k_kaz
  • 596
  • 1
  • 9
  • 20
  • 2
    Have a look at https://eli.thegreenplace.net/2016/a-polyglots-guide-to-multiple-dispatch/ –  Jul 19 '18 at 14:49
  • 1
    Looks like `visitor` pattern to me. – SergeyA Jul 19 '18 at 14:49
  • @NeilButterworth Thank you for the response, so if the then else is the answer for multiple dispatch? – k_kaz Jul 19 '18 at 15:03
  • 1
    For this approach to work, You would need `BasePhysicsObject` to have a `virtual void OnCollisionDetected(Fire *) = 0;` and `virtual void OnCollisionDetected(Player *) = 0;`. As it is, when you call `another_object->OnCollisionDetected(this);` the *only* overload that a `BasicPhysicsObject` has is `OnCollisionDetected(BasePhysicsObject * another_object)`. The derived types overloads are never taken into account. They have `virtual` members but don't override anything. Using `override` would have helped identify the problem. The general solution to this problem would be to use visitors. – François Andrieux Jul 19 '18 at 15:07
  • You can't override a virtual method with one set of parameters using a descendant method with a different set of parameters, even if the parameter types are related. The signature of the overriding method must exactly match the method that it is overriding. Only [return values are allowed to be covariant](https://en.wikipedia.org/wiki/Covariant_return_type), but not parameters. – Remy Lebeau Jul 19 '18 at 17:58

1 Answers1

0

For double dispatch pattern, you have to have a virtual dispatcher. first dispatch is done via virtual call on lhs instance, which than does the dispatch with rhs instance:

class BasePhysicsObject {
public:
    virtual ~BasePhysicsObject() = default;

    virtual void CollideDispatcher(BasePhysicsObject* ) = 0;

    // The true collision code.
    virtual void OnCollisionDetected(Fire*) = 0;
    virtual void OnCollisionDetected(Player*) = 0;
};

class Player : public BasePhysicsObject {
public:
    // Always same implementation
    // but `this` type is different for each class
    // Which allow correct overload resolution
    void CollideDispatcher(BasePhysicsObject* rhs) override { rhs->OnCollisionDetected(this); }

    void OnCollisionDetected(Fire* rhs) override { /* Player/Fire collision */ }
    void OnCollisionDetected(Player* rhs) override { /*Player/Player collision*/ }
};

class Fire : public BasePhysicsObject {
public:
    // Always same implementation
    // but `this` type is different for each class
    // Which allow correct overload resolution
    void CollideDispatcher(BasePhysicsObject* rhs) override { rhs->OnCollisionDetected(this); }

    void OnCollisionDetected(Fire* rhs) override { /* Fire/Fire collision */ }
    virtual void OnCollisionDetected(Player* rhs) override {
         // Fire/Player collision:
         // might be symmetrical to Player/Fire collision and so:
         rhs->OnCollisionDetected(this);
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thank you, I kinda wished there was some way to avoid declaring all the derived classes inside the base class, since in a complex scene these can be a lot, and you would have to change the base class every time you add a new derived class... – k_kaz Jul 19 '18 at 15:19
  • I have done a [multiple dispath visitor](https://stackoverflow.com/a/29345504/2684539) which minimizes the needed dependencies. Might be overkill for your needs. – Jarod42 Jul 19 '18 at 15:47