HOME

Multimethods in C++

The problem: Let's say we have a polymorphic shape class with functions to calculate intersections with other shapes. I want to choose in runtime the correct function based on the shape even using the base class shape. How to do it? Here, I will present a way to do it using RTTI capabilities from C++. ```cpp

namespace shapes {

struct shape 
{
    virtual ~shape(){}
    virtual void collide(const shape &) const = 0;
};

struct rectangle;

struct circle : public shape
{
    void collide(const circle &) const { std::cout << "circle->circle" << std::endl; }
    void collide(const rectangle &) const { std::cout << "circle->rectangle" << std::endl; }
    void collide(const shape &) const { std::cout << "shape->shape" << std::endl;  }
};

struct rectangle : public shape
{    
    void collide(const rectangle &) const { std::cout << "rectangle->rectangle" << std::endl; }
    void collide(const circle &) const { std::cout << "rectangle->circle" << std::endl; }
    void collide(const shape &) const { std::cout << "shape->shape" << std::endl;  }
};

}

struct doubletypeinfokey { const typeinfo* linfo; const typeinfo* rinfo; doubletypeinfokey(const typeinfo* l, const typeinfo*r) : linfo(l), rinfo(r) {} };

bool operator < (const doubletypeinfokey &l, const doubletypeinfokey &r) { if ( *l.linfo == *r.linfo ) return l.rinfo->before(*r.rinfo); return l.linfo->before(*r.linfo); }

template struct docast { void operator()(const shapes::shape &l, const shapes::shape &r) const { dynamiccast(l).collide(dynamic_cast(r)); } };

typedef std::tr1::function< void (const shapes::shape&, const shapes::shape&) > collidefptr; typedef std::map<doubletypeinfokey, collidefptr> Map; Map typeinfo_map;

void checkcollision(const shapes::shape & l, const shapes::shape &r) { Map::constiterator it = typeinfomap.find(doubletypeinfokey(&typeid(l), &typeid(r))); if (it == typeinfo_map.end()) { l.collide(r); return; }

(*it).second(l, r);

}

template std::pair makekey() { return makepair(doubletypeinfokey(&typeid(T1), &typeid(T2)), collidefptr(do_cast())); }

int main() { typeinfomap.insert( makekey ()); typeinfomap.insert( makekey ()); typeinfomap.insert( makekey ()); typeinfomap.insert( makekey ()); typeinfomap.insert( make_key ());

shapes::rectangle rectangle;
shapes::circle circle;

check_collision(rectangle, circle);
check_collision(rectangle, rectangle);

check_collision(circle, rectangle);
check_collision(circle, circle);

} ```