#include <iostream>
#include <unordered_map>
#include <map>
#include <iostream>
#include <typeinfo>
#include <unistd.h>
#include <sys/times.h>
 
struct Base {
    typedef std::map<const char *, void *> MapType;
    MapType children_;
 
    Base () { std::cout << __func__ << std::endl; }
    virtual ~Base () {}
 
    template <typename MAYBE_DERIVED>
    MAYBE_DERIVED * isTypeOrSubtype () {
        auto x = children_.find(typeid(MAYBE_DERIVED).name());
        return ((x != children_.end())
                ? static_cast<MAYBE_DERIVED *>(x->second)
                : 0);
    }
};
 
#define FOO(X) \
    X () : count_(0) { children_[typeid(X).name()] = this; } \
    void foo () { if (this) { count_ += 1; } } \
    int count () { return this ? count_ : 0; } \
    private: int count_
 
struct A : virtual Base { FOO(A); };
struct B : A { FOO(B); };
struct C : A { FOO(C); };
struct D : virtual Base { FOO(D); };
struct E : D { FOO(E); };
struct F : D { FOO(F); };
struct G : B, F { FOO(G); };
 
void foo (Base *b) {
    for (int i = 0; i < 20000000; ++i) {
        b->isTypeOrSubtype<E>()->foo();
    }
}
 
void foo_dc (Base *b) {
    for (int i = 0; i < 2000000; ++i) {
        dynamic_cast<E *>(b)->foo();
    }
}
 
void bar (Base *b) {
    for (int i = 0; i < 20000000; ++i) {
        b->isTypeOrSubtype<C>()->foo();
    }
}
 
void bar_dc (Base *b) {
    for (int i = 0; i < 2000000; ++i) {
        dynamic_cast<C *>(b)->foo();
    }
}
 
void report (struct tms &start, struct tms &finish, Base *x, int f = 1) {
    double ticks_per_sec = sysconf(_SC_CLK_TCK);
    unsigned long long elapsed = 0;
    elapsed += finish.tms_utime;
    elapsed += finish.tms_stime;
    elapsed -= start.tms_utime;
    elapsed -= start.tms_stime;
    int countC = x->isTypeOrSubtype<C>()->count();
    int countE = x->isTypeOrSubtype<E>()->count();
    std::cout << countC + countE << " hits, "
              << f * elapsed/ticks_per_sec  << " seconds"
              << std::endl;
}
 
void run_test (void (*test)(Base *), Base *x, int f = 1) {
    struct tms start;
    struct tms finish;
    times(&start);
    test(x);
    times(&finish);
    report(start, finish, x, f);
}
 
int main () {
    G g;
    C c;
    E e;
 
    run_test(foo_dc, &g, 10);    //--> 0          g is not an E
    run_test(bar_dc, &g, 10);    //--> 0          g is not a C
 
    run_test(foo_dc, &c, 10);    //--> 0          c is not an E
    run_test(bar_dc, &c, 10);    //--> 2000000   c is a C
 
    run_test(foo_dc, &e, 10);    //--> 2000000   e is an E
    run_test(bar_dc, &e, 10);    //--> 2000000   e is not a C
}