Double dispatch
#include <memory> | |
#include <tuple> | |
#include <algorithm> | |
#include <experimental/array> | |
#include <type_traits> | |
namespace { | |
// This fails to evaluate if any element in Args is not a valid type. | |
// Otherwise it evaluates to void. | |
template <typename... Args> using void_type = void; | |
// In general terms, not everything is invocable. | |
template <typename Result, typename Ret, typename = void> | |
struct is_invocable_impl : std::false_type {}; | |
// Specialization for is_invocable_impl used when Result is a valid | |
// specialization of std::result_of<...>. | |
template <typename Result, typename Ret> | |
struct is_invocable_impl<Result, Ret, void_type<typename Result::type>> | |
: std::is_void<Ret> {}; | |
// Provides a boolean constant type that specifies whether or not the type F | |
// provides a function call operator that takes Arg argument | |
template <typename F, typename Arg> | |
struct is_invocable : is_invocable_impl<std::result_of<F &(Arg)>, void> {}; | |
template <bool b, class T = void> | |
using Requires = typename std::enable_if<b, T>::type; | |
template <class Base, class Derived, class F, bool Invocable> | |
struct DispatcherImpl; | |
template <class Base, class Derived, class F> | |
struct DispatcherImpl<Base,Derived,F,true> { | |
using Arg = std::unique_ptr<Derived>; | |
using Result = std::pair<bool, std::unique_ptr<Base>>; | |
static Result dispatch( F &&f, std::unique_ptr<Base> p ) { | |
bool canDispatch = p.get()->template get_as<Derived>(); | |
if(!canDispatch) | |
return {false, std::move(p)}; | |
std::unique_ptr<Derived> pd(static_cast<Derived*>(p.release())); | |
return {true, std::forward<F>(f)(std::move(pd))}; | |
} | |
}; | |
template <class Base, class Derived, class F> | |
struct DispatcherImpl<Base,Derived,F,false> { | |
using Arg = std::unique_ptr<Derived>; | |
using Result = std::pair<bool, std::unique_ptr<Base>>; | |
static Result dispatch( F &&f, std::unique_ptr<Base> p ) { | |
return {false, std::move(p)}; | |
} | |
}; | |
template <class Base, class Derived, class F> | |
struct Dispatcher : DispatcherImpl<Base, Derived, F, is_invocable<F,std::unique_ptr<Derived>>::value> {}; | |
template <class... Ds, class Base, class F> | |
static std::unique_ptr<Base> dispatch( std::unique_ptr<Base> p, F &&f ) { | |
for( auto dsp : {Dispatcher<Base,Ds,F>::dispatch...} ) { | |
bool dispatched; | |
std::tie(dispatched, p) = dsp(std::forward<F>(f), std::move(p)); | |
if(dispatched) | |
return p; | |
} | |
return p; | |
} | |
} // namespace | |
template <class Base, class Derived> | |
struct derived_tag; | |
template <class Base> | |
struct enable_get_as { | |
struct tag { constexpr tag() = default; }; | |
enable_get_as() : t(t) {} | |
const tag* get_tag() const { | |
return t; | |
} | |
template <class T> | |
T* get_as(); | |
private: | |
template <class B, class D> friend class derived_tag; | |
const tag *t; | |
}; | |
template <class Base, class Derived> | |
class derived_tag : public Base { | |
public: | |
static_assert( std::is_base_of<enable_get_as<Base>, Base>::value ); | |
derived_tag() | |
{ | |
enable_get_as<Base>::set(&get_type_tag()); | |
} | |
private: | |
friend class enable_get_as<Base>; | |
using tag = typename enable_get_as<Base>::tag; | |
static tag *get_type_tag() { static tag type_tag; return &type_tag; } | |
}; | |
template <class Base> | |
template <class Derived> | |
Derived *enable_get_as<Base>::get_as() { | |
if (get_tag() == derived_tag<Base,Derived>::get_type_tag()) | |
return static_cast<Derived*>(this); | |
return nullptr; | |
} | |
class Base : public enable_get_as<Base> { | |
public: | |
virtual ~Base() {} | |
}; | |
struct A : derived_tag<Base, A> {}; | |
struct B : derived_tag<Base, B> {}; | |
struct C : derived_tag<Base, C> {}; | |
struct D : derived_tag<Base, D> {}; | |
struct CB { | |
std::unique_ptr<Base> operator()( std::unique_ptr<B> ) { return nullptr; } | |
}; | |
std::unique_ptr<Base> foo (std::unique_ptr<Base> a) { | |
return dispatch<A,B,D>(std::move(a), CB()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment