Double Dispatch Revisited
by Nat Goodspeed

Listing One
class EntryBase
{
public:
    virtual bool matches(const GameObject& param1,
                         const GameObject& param2) const = 0;
};


Listing Two
class Entry: public EntryBase
{
public:
    virtual bool matches(const GameObject& param1,
                         const GameObject& param2) const
    {
        return (dynamic_cast<const SpaceShip*>(&param1) != 0 &&
                dynamic_cast<const  Asteroid*>(&param2) != 0);
    }
};


Listing Three
template<typename Type1, typename Type2>
class Entry: public EntryBase
{
public:
    virtual bool matches(const GameObject& param1,
                         const GameObject& param2) const
    {
        return (dynamic_cast<const Type1*>(&param1) != 0 &&
                dynamic_cast<const Type2*>(&param2) != 0);
    }
};


Listing Four
try
{
    dubiousRoutine();
}
catch (const ErrorRuntime& e)
{
    ...
}
catch (const ErrorBounds& e)        // whoops, never reached!
{
    ...
}
catch (const Error& e)
{
    ...
}


Listing Five
try
{
    dubiousRoutine();
}
catch (const ErrorBounds& e)        // better
{
    ...

}
catch (const ErrorRuntime& e)
{
    ...
}
catch (const Error& e)
{
    ...
}


Listing Six
typedef boost::shared_ptr<EntryBase> as EntryPtr;
typedef std::list<EntryPtr> DispatchTable;
DispatchTable mDispatch;


Listing Seven
// Look up the first matching entry.
EntryPtr lookup(const GameObject& param1, const GameObject& param2) const
{
    DispatchTable::const_iterator found =
        std::find_if(mDispatch.begin(), mDispatch.end(),
                     boost::bind(&EntryBase::matches, _1,
                                 boost::ref(param1), boost::ref(param2)));
    if (found != mDispatch.end())
        return *found;
    return 0;
}


Listing Eight
template<typename Type1, typename Type2, class Functor>
class Entry: public EntryBase
{
    // Bind whatever function or function object the instantiator passed.
    Functor mFunc;
public:
    Entry(Functor func): mFunc(func) {}
    virtual bool matches(const GameObject& param1,
                         const GameObject& param2) const { ... }
};


Listing Nine
class EntryBase
{
public:
    ...
    virtual void operator()(GameObject& param1,
                            GameObject& param2) const = 0;
};

template<typename Type1, typename Type2, class Functor>
class Entry: public EntryBase
{
    Functor mFunc;
public:
    Entry(Functor func): mFunc(func) {}
    ...
    virtual void operator()(GameObject& param1,
                            GameObject& param2) const
    {
        mFunc(dynamic_cast<Type1&>(param1),
              dynamic_cast<Type2&>(param2));
    }
};


Listing Ten
void call(GameObject& param1, GameObject& param2) const
{
    EntryPtr found = lookup(param1, param2);
    if (found.get() == 0)
        return;
    (*found)(param1, param2); // call the Functor we found
}


Listing Eleven
template<typename Type1, typename Type2, class Functor>
void insert(const Type<Type1>&, const Type<Type2>&, Functor func)
{
    mDispatch.insert(mDispatch.end(),
                     EntryPtr(new Entry<Type1, Type2, Functor>(func)));
}


Listing Twelve
template<typename Type1, typename Type2, class Functor>
void add(const Type<Type1>& t1, const Type<Type2>& t2, Functor func,
         bool symmetrical = false)
{
    insert(t1, t2, func);
    if (symmetrical)
        insert(t2, t1, boost::bind(func, _2, _1));
}


Listing Thirteen
void shipAsteroid(SpaceShip& ship, Asteroid& rock)
{
  cout << rock.stringize() << " has pulverized " << ship.stringize() << endl;
}


Listing Fourteen
typedef DoubleDispatch<int, GameObject> DD;
DD dispatcher;
dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(),
               shipAsteroid, true);
dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(),
               shipStation, true);
dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(),
               asteroidStation, true);

    // Instantiate a few GameObjects.  Make sure we refer to them
    // polymorphically, and don't let them leak.
    std::auto_ptr<GameObject> home(new SpaceStation("Terra Station"));
    std::auto_ptr<GameObject> obstacle(new Asteroid("Ganymede"));
    std::auto_ptr<GameObject> tug(new CommercialShip("Pilotfish"));
    std::auto_ptr<GameObject> patrol(new MilitaryShip("Enterprise"));

    // Try colliding them.
    dispatcher(*home, *tug);        // reverse params, SpaceShip subclass
    dispatcher(*patrol, *home);     // forward params, SpaceShip subclass
    dispatcher(*obstacle, *home);   // forward params
    dispatcher(*home, *obstacle);   // reverse params
    dispatcher(*tug, *obstacle);    // forward params, SpaceShip subclass
    dispatcher(*obstacle, *patrol); // reverse params, SpaceShip subclass


Listing Fifteen
class CommercialShip Pilotfish has docked at class SpaceStation Terra Station
class MilitaryShip Enterprise has docked at class SpaceStation Terra Station
class Asteroid Ganymede has damaged class SpaceStation Terra Station
class Asteroid Ganymede has damaged class SpaceStation Terra Station
class Asteroid Ganymede has pulverized class CommercialShip Pilotfish
class Asteroid Ganymede has pulverized class MilitaryShip Enterprise


Listing Sixteen
ReturnType operator()(ParamBaseType& param1, ParamBaseType& param2) const
{
  EntryPtr found = lookup(param1, param2);
   if (found.get() == 0)
     return ReturnType();          // return void() ?!?
  return (*found)(param1, param2); //return (value returned by void function)
}



