Did Loki multimethods turn it into C ++ 11?

I am reading Modern C ++ Design General programming and design patterns applied by Andrei Alexandrescu and chapter 11 on multimethods deals with a problem that 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 quite old (2001) and has limitations that are no longer true in C ++ 11 (for example, the number of template parameters cannot be variable). I tried to find if Loki was rewritten using C ++ 11, but the last modification dates from 2009, and there are no updates to the Andrei Alexandrescu homepage . However, after some research, I got the impression that Loki is something like Boost in the sense that it is included in the standard library.

Rape multimethod or some parts of it were adopted in C ++ 11?

+7
c ++ c ++ 11 loki multiple-dispatch
source share
2 answers

This is not standard, but it would be easy to construct using a map of function objects indexed by a pair of types.

For completeness, here is 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; } 
+5
source share

I have my own implementation in several languages ​​in C ++ 11 based on the ideas from this book, and I hope this solution will be useful to someone.

The MultiMethod template class is parameterized with a return type of multimethod type and its base types with polymorphic arguments. This class represents an abstract multimethod with an operator () interface function. Specific methods are registered by the Add Template function. These function template parameters are derived types of registered method arguments.

The following is the source code for the MultiMethod class:

 ////////////////////////////////////////////////////////////////////////////// // 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_ 

The minimalist helper class TypeInfo is used to identify a specific method arguments. This class is implemented in the 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 an example of using the 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; } 
+1
source share

All Articles