基于本书的想法,我在 C++11 中实现了自己的多方法,我希望这个解决方案对某人有用。
MultiMethod 模板类使用多方法返回值类型和其多态参数的基类型进行参数化。该类表示具有 operator() 接口函数的抽象多方法。具体方法由添加模板函数注册。此函数模板参数是已注册方法参数的派生类型。
以下是MultiMethod类源码:
//////////////////////////////////////////////////////////////////////////////
// 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_
简约的辅助类 TypeInfo 用于识别具体的方法参数。该类在以下源代码中实现:
//////////////////////////////////////////////////////////////////////////////
// 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); }
下面是使用 MultiMethod 类的示例:
//////////////////////////////////////////////////////////////////////////////
// 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;
}