Thiago R. Adams website

Home Blog Code-Blog Twitter Downloads Links / Books About

Websites

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++.


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 double_type_info_key
{
    const type_info* l_info;
    const type_info* r_info;
    double_type_info_key(const type_info* l, const type_info*r) :
    l_info(l),
    r_info(r)
    {}
};

bool operator < (const double_type_info_key &l,
                 const double_type_info_key &r)
{
    if ( *l.l_info == *r.l_info )
        return l.r_info->before(*r.r_info);
    return l.l_info->before(*r.l_info);
}

template<class T1, class T2>
struct do_cast {
   void operator()(const shapes::shape &l,
                   const shapes::shape &r) const
   {
      dynamic_cast<const T1&>(l).collide(dynamic_cast<const T2&>(r));
   }
};

typedef std::tr1::function< void (const shapes::shape&, const shapes::shape&) >  collide_fptr;
typedef std::map<double_type_info_key, collide_fptr> Map;
Map type_info_map;

void check_collision(const shapes::shape & l, const shapes::shape &r)
{
    Map::const_iterator it = type_info_map.find(double_type_info_key(&typeid(l), &typeid(r)));
    if (it == type_info_map.end())
    {
        l.collide(r);
        return;
    }

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

template<class T1, class T2>
std::pair<double_type_info_key, collide_fptr> make_key()
{
  return make_pair(double_type_info_key(&typeid(T1), &typeid(T2)),
                   collide_fptr(do_cast<T1, T2>()));
}

int main()
{
    type_info_map.insert( make_key<shapes::rectangle, shapes::circle> ());
    type_info_map.insert( make_key<shapes::circle, shapes::rectangle> ());
    type_info_map.insert( make_key<shapes::rectangle, shapes::rectangle> ());
    type_info_map.insert( make_key<shapes::circle, shapes::circle> ());
    type_info_map.insert( make_key<shapes::shape, shapes::shape> ());

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

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

Want to see more? Go to the CodeBlog section.

About the author: I am Thiago Adams. I work as a professional C++ software engineer. I have created this website to share ideas and source code with other people with similar interests.
I would like to hear from you comments, critics, questions and suggestions about this topic or any other part of this website. Email: thiago.adams at gmail dot com