Garciat
10/2/2016 - 2:07 PM

devirt2.cpp

#include <array>
#include <typeinfo>

extern volatile int poop;

struct A {
  virtual void f() { poop += 10; }
};

struct B : public A {
  virtual void f() { poop += 20; }
};

struct C : public A {
  virtual void f() { poop += 30; }
};

struct D final : public B {
  virtual void f() final { poop += 40; }
};

template<typename T>
struct pmf_traits;

template<typename C, typename R, typename A>
struct pmf_traits<R (C::*)(A&) const> {
  using arg_type = A;
};

template<typename... Fs>
struct devirt_help;

template<typename F, typename... Fs>
struct devirt_help<F, Fs...> {
  template<typename K>
  devirt_help(K &&obj, F &f, Fs... fs) {
    using T = typename pmf_traits<decltype(&F::operator())>::arg_type;
    if (typeid(obj) == typeid(T)) {
      f(static_cast<T&>(obj));
    } else {
      devirt_help<Fs...>(obj, fs...);
    }
  }
};

template<typename F>
struct devirt_help<F> {
  template<typename K>
  devirt_help(K &&obj, F f) {
    f(obj);
  }
};

template<typename T, typename... Fs>
void devirt(T &&obj, Fs&&... funcs) {
  devirt_help<Fs...>(obj, funcs...);
}

void hello(A &a) {
  devirt(a, [](D &o) { o.D::f(); }
          , [](C &o) { o.C::f(); }
          , [](A &o) { o.f(); });
}