I have my own multimethod implementation in C++11 based on ideas from this book and I hope
this solution will be useful to someone.
MultiMethod template class is parametrized with multimethod return value type and base types of its
polymorphic arguments. This class represents abstract multimethod with operator() interface function.
Concrete methods are registered by Add template function. This function template parameters are
derived types of registered method arguments.
The following is MultiMethod class source code:
//////////////////////////////////////////////////////////////////////////////
// MultiMethod.h
#ifndef _MULTI_METHOD_H_
#define _MULTI_METHOD_H_
#include "TypeInfo.h"
#include <functional>
#include <tuple>
#include <map>
template <typename>
class MultiMethod;
template <typename Res, typename... ArgsBase>
class MultiMethod<Res(ArgsBase...)> {
template <class T>
using ArgId = TypeInfo;
using CallbackId = std::tuple<ArgId<ArgsBase>...>;
using Callback = std::function<Res(ArgsBase&...)>;
using Callbacks = std::map<CallbackId, Callback>;
Callbacks callbacks;
public:
// Method registration.
template <typename... Args, typename Fn>
void Add(Fn fn) {
callbacks[CallbackId(TypeInfo(typeid(Args))...)] = [fn](ArgsBase&... args) -> Res {
return fn(dynamic_cast<Args&>(args)...);
};
}
// Multimethod call.
template <typename... Args>
Res operator()(Args&... args) {
auto it = callbacks.find(CallbackId(TypeInfo(typeid(args))...));
if (it != callbacks.end()) {
return it->second(args...);
}
return Callback()(args...);
}
};
#endif // _MULTI_METHOD_H_
Minimalistic helper class TypeInfo is used for identification of concrete method
arguments. This class is implemented in following source code:
//////////////////////////////////////////////////////////////////////////////
// TypeInfo.h
#ifndef _TYPE_INFO_H_
#define _TYPE_INFO_H_
#include <typeinfo>
class TypeInfo {
const std::type_info& ti;
public:
TypeInfo(const std::type_info& ti) : ti(ti)
{}
friend bool operator<(const TypeInfo& t1, const TypeInfo& t2);
};
bool operator<(const TypeInfo& t1, const TypeInfo& t2);
#endif // _TYPE_INFO_H_
//////////////////////////////////////////////////////////////////////////////
// TypeInfo.cpp
#include "TypeInfo.h"
bool operator<(const TypeInfo& t1, const TypeInfo& t2)
{ return t1.ti.before(t2.ti); }
Here is the example of using MultiMethod class:
//////////////////////////////////////////////////////////////////////////////
// main.cpp
#include "MultiMethod.h"
#include <iostream>
#include <memory>
// Number base class.
class Number {
public:
virtual ~Number()
{}
};
// Integer number class.
class Integer : public Number {
int val;
public:
Integer(int v) : val {v}
{}
int Value() const
{ return val; }
};
// Real number class.
class Real : public Number {
double val;
public:
Real(double v) : val {v}
{}
double Value() const
{ return val; }
};
int main(int argc, char* argv[]) {
// Single number printing multimethod.
MultiMethod<bool(Number)> print1;
print1.Add<Real>(
[](Real& r)
{ return (std::cout << r.Value() << std::endl, true); });
print1.Add<Integer>(
[](Integer& i)
{ return (std::cout << i.Value() << std::endl, true); });
// Two numbers printing multimethod.
MultiMethod<bool(Number, Number)> print2;
print2.Add<Real, Real>(
[&print2](Real& r1, Real& r2)
{ return (std::cout << r1.Value() << " " << r2.Value() << std::endl, true); });
print2.Add<Real, Integer>(
[&print2](Real& r1, Integer& i2)
{ return (std::cout << r1.Value() << " " << i2.Value() << std::endl, true); });
print2.Add<Integer, Real>(
[&print2](Integer& i1, Real& r2)
{ return (std::cout << i1.Value() << " " << r2.Value() << std::endl, true); });
print2.Add<Integer, Integer>(
[&print2](Integer& i1, Integer& i2)
{ return (std::cout << i1.Value() << " " << i2.Value() << std::endl, true); });
// Two numbers addition multimethod.
MultiMethod<std::unique_ptr<Number>(Number, Number)> add;
add.Add<Real, Real>(
[](Real& r1, Real& r2)
{ return std::unique_ptr<Number> {new Real {r1.Value() + r2.Value()}}; });
add.Add<Integer, Integer>(
[](Integer& i1, Integer& i2)
{ return std::unique_ptr<Number> {new Integer {i1.Value() + i2.Value()}}; });
add.Add<Real, Integer>(
[&add](Real& r1, Integer& i2)
{ return add(i2, r1); });
add.Add<Integer, Real>(
[&add](Integer& i1, Real& r2) {
std::unique_ptr<Real> r1 {new Real(i1.Value())};
return add(*r1, r2);
}
);
// Multimethod call examples.
std::unique_ptr<Number> n1 {new Real {12.3}};
std::unique_ptr<Number> n2 {new Integer {4}};
print1(*n1);
print1(*n2);
print2(*n1, *n1);
print2(*n1, *n2);
print2(*n2, *n1);
print2(*n2, *n2);
print1(*add(*n1, *n1));
print1(*add(*n1, *n2));
print1(*add(*n2, *n1));
print1(*add(*n2, *n2));
return 0;
}