I am reading Modern C++ Design Generic Programming and Design Patterns Applied by Andrei Alexandrescu and chapter 11 on multimethods deals exactly with the problem I am trying to solve. All source code from the book is published in a library named Loki.
The problem is that the book is fairly old (2001) and deals with restrictions that are no longer true in C++11 (e.g. that the number of template parameters cannot be variable). I tried to look up if Loki has been rewritten using C++11, but the last modification is dated 2009 and there are no updates on Andrei Alexandrescu's home page. However, after doing some research I have the impression that Loki is something like Boost in the sense that it gets incorporated into the standard library.
Has the multimethod idiom or some of its parts been adopted into C++11?
It's not in the standard but would be reasonably easy to build using a map of function objects indexed by a pair of typeids.
For completeness, here's my first attempt:
#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <map>
#include <functional>
#include <memory>
struct Animal {
virtual std::type_index type() const = 0;
};
template <class T> struct AnimalImpl : public Animal {
std::type_index type() const override {
return typeid(T);
}
};
struct Dog : AnimalImpl<Dog> {
};
struct Cat : AnimalImpl<Cat> {
};
struct Mouse : AnimalImpl<Mouse> {
};
using Types = std::tuple<std::type_index, std::type_index>;
using Outcome = std::function<void (Animal&, Animal&)>;
using DispatchMap = std::map<Types, Outcome>;
using namespace std;
void catVDog(Animal& cat, Animal& dog) {
cout << "dog wins\n";
}
void catVMouse(Animal& cat, Animal& mouse)
{
cout << "cat wins\n";
}
DispatchMap makeOutcomes()
{
DispatchMap result;
result.emplace( make_pair( Types {typeid(Cat), typeid(Dog)}, catVDog) );
result.emplace( make_pair( Types {typeid(Dog), typeid(Cat)},
[](Animal&a1,Animal&a2) { return catVDog(a2,a1); }) );
result.emplace( make_pair( Types {typeid(Cat), typeid(Mouse)}, catVMouse) );
result.emplace( make_pair( Types {typeid(Mouse), typeid(Cat)},
[](Animal&a1,Animal&a2) { return catVMouse(a2,a1); }) );
return result;
}
const DispatchMap outcomes = makeOutcomes();
void fight(Animal& a1, Animal& a2)
{
auto it = outcomes.find(Types{ a1.type(), a2.type() });
if (it == outcomes.end()) {
cout << typeid(a1).name() << " " << typeid(a2).name() << " ";
std::cout << "no fight\n";
}
else {
it->second(a1, a2);
}
}
int main()
{
unique_ptr<Animal> cat { new Cat {} };
unique_ptr<Animal> dog { new Dog {} };
unique_ptr<Animal> mouse { new Mouse {} };
fight(*cat, *dog);
fight(*cat, *mouse);
fight(*dog, *cat);
fight(*dog, *mouse);
return 0;
}