Last active
October 4, 2019 17:24
-
-
Save jbelloncastro/f2f2cfb709c74494145588fa06221436 to your computer and use it in GitHub Desktop.
Double dispatch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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