Skip to content

Instantly share code, notes, and snippets.

@jbelloncastro
Last active October 4, 2019 17:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbelloncastro/f2f2cfb709c74494145588fa06221436 to your computer and use it in GitHub Desktop.
Save jbelloncastro/f2f2cfb709c74494145588fa06221436 to your computer and use it in GitHub Desktop.
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