Opentopia Directory Encyclopedia Tools

Double dispatch

Encyclopedia : D : DO : DOU : Double dispatch


Double dispatch is a mechanism that dispatches a function call to different concrete functions depending on the runtime types of multiple objects involved in the call. A related concept are multimethods. In most object-oriented systems, the concrete function that is called from a function call in the code depends on the dynamic type of a single object and therefore they are known as single dispatch calls, or simply virtual function calls.

Examples

Double dispatch is useful in situations where the result of some computation depends on the runtime types of its arguments. For example, one could use double dispatch in the following situations:

A Common Idiom

The common idiom in the examples presented above is that we have made the selection of the appropriate algorithm based on the call's argument types at runtime. Therefore the call is subject to all the usual performance trade-offs that are associated with dynamic resolving of calls. Usually, more so than in a language supporting single method dispatch. In C++, for example, a dynamic function call is usually resolved by a single offset calculation which is possible because the compiler knows the location of the function in the object's method table and therefore can statically calculate the offset. In a language supporting double dispatch, this is more costly, because the compiler must calculate the method's offset into the method table at runtime.

Double dispatch is more than function overloading

At first glance, double dispatch appears to be a natural result of function overloading. Function overloading allows the function called to depend on the type of the argument as well as the class on which it is called, but calling an overloaded function goes through at most one virtual table, so dynamic dispatch is only based on the type of the calling object. Consider an example of collisions in a game:
class SpaceShip ;
class GiantSpaceShip : public SpaceShip ;

class Asteroid virtual void CollideWith(GiantSpaceShip&) };

class ExplodingAsteroid : public Asteroid virtual void CollideWith(GiantSpaceShip&) };

If you have
Asteroid theAsteroid;
SpaceShip theSpaceShip;
GiantSpaceShip theGiantSpaceShip;
then, because of function overloading,
theAsteroid.CollideWith(theSpaceShip);
theAsteroid.CollideWith(theGiantSpaceShip);
will print Asteroid hit a SpaceShip and Asteroid hit a GiantSpaceShip respectively, without using any dynamic dispatch. Furthermore
ExplodingAsteroid theExplodingAsteroid;
theExplodingAsteroid.CollideWith(theSpaceShip);
theExplodingAsteroid.CollideWith(theGiantSpaceShip);
will print ExplodingAsteroid hit a SpaceShip and ExplodingAsteroid hit a GiantSpaceShip respectively, again without dynamic dispatch.

With a reference to an Asteroid, dynamic dispatch is used and

Asteroid& theAsteroidReference = theExplodingAsteroid;
theAsteroidReference.CollideWith(theSpaceShip);
theAsteroidReference.CollideWith(theGiantSpaceShip);
prints ExplodingAsteroid hit a SpaceShip and ExplodingAsteroid hit a GiantSpaceShip, again as expected. However,
SpaceShip& theSpaceShipReference = theGiantSpaceShip;
theAsteroid.CollideWith(theSpaceShipReference);
theAsteroidReference.CollideWith(theSpaceShipReference);
prints Asteroid hit a SpaceShip and ExplodingAsteroid hit a SpaceShip, neither of which is correct. The problem is that, while virtual functions are dispatched dynamically in C++, function overloading is done statically.

Double dispatch in C++

The problem described above can be resolved with a technique similar to that used by the visitor pattern. Suppose SpaceShip and GiantSpaceShip both have the function
virtual void CollideWith(Asteroid& inAsteroid) 
Then, while the previous example still does not work correctly, the following does:
SpaceShip& theSpaceShipReference = theGiantSpaceShip;
Asteroid& theAsteroidReference = theExplodingAsteroid;
theSpaceShipReference.CollideWith(theAsteroid);
theSpaceShipReference.CollideWith(theAsteroidReference);
It prints out Asteroid hit a GiantSpaceShip and ExplodingAsteroid hit a GiantSpaceShip, as expected. The key is that theSpaceShipReference.CollideWith(theAsteroidReference); does the following at run time:
  1. theSpaceShipReference is a reference, so C++ looks up the correct method in the vtable. In this case, it will call GiantSpaceShip::CollideWith(Asteroid&).
  2. Within GiantSpaceShip::CollideWith(Asteroid&), inAsteroid is a reference, so inAsteroid.CollideWith(*this) will result in another vtable lookup. In this case, inAsteroid is a reference to an ExplodingAsteroid so ExplodingAsteroid::CollideWith(GiantSpaceShip&) will be called.

See also

 


From Wikipedia, the Free Encyclopedia. Original article here. Support Wikipedia by contributing or donating.
All text is available under the terms of the GNU Free Documentation License See Wikipedia Copyrights for details.

Search Titles
0123456789
ABCDEFGHIJ
KLMNOPQRST
UVWXYZ?

E-mail this article to:

Personal Message: