#include <iostream>
#include <cstdint>
#include <cassert>

#define  w(expr)  cout << #expr << ": " << expr << endl

class B {
public:
    uint64_t sum() const { return b; }
private:
    uint64_t b = 100;
};

class C {
public:
    uint64_t sum() const { return c; }
private:
    uint64_t c = 150;
};

class D : public B, public C {
public:
    uint64_t sum() const { return B::sum() + C::sum() + d; }
private:
    uint64_t d = 200;
};

int main() {
    using namespace std;

    // 1.  Multiple inheritance object layout

    D x;
    w( x.sum() );

    cout << "*** Object layout" << endl;
    w( sizeof(x) );
    uint64_t* p = reinterpret_cast<uint64_t*>(&x);
    w( p[0] );
    w( p[1] );
    w( p[2] );

    /*
    // 2.  Base pointers have different addresses

    cout << "*** Base pointers have different addresses" << endl;
    B* pb = &x;
    C* pc = &x;
    D* pd = &x;
    w( pb );
    w( pc );
    w( pd );
    w( pb->sum() );
    w( pc->sum() );
    w( pd->sum() );

    // 3.  static_cast

    cout << "*** static_cast" << endl;
    {
        // static_cast validates that you can downcast from C* to D*
        D* pd2 = static_cast<D*>(pc);
        w( pc );
        w( pd2 );

        // static_cast prevents you from casting to unrelated types
        std::string s("hi");
        // pd2 = static_cast<D*>(&s); // Compiler error
        pd2 = (D*)&s; // No compiler error

        // ...but static_cast isn't perfect
        C y;
        pd2 = static_cast<D*>(&y);
        // w( pd2->sum() ); // Unpredictable result
    }

    // 4.  Multiple inheritance with virtual functions
    //
    // Make these code changes and observe the output.
    //
    //  1) Add "virtual" to B::sum() and C::sum()
    //  2) Add "override" to D::sum()
    //  3) Write the following lines after "uint64_t* p = ..." in main():
    //
    //      w( reinterpret_cast<void*>(p[0]) );
    //      w( p[1] );
    //      w( reinterpret_cast<void*>(p[2]) );
    //      w( p[3] );
    //      w( p[4] );

    // 5.  dynamic_cast
    cout << "*** dynamic cast" << endl;
    {
        // dynamic_cast validates crosscasting and crosscasting
        assert(dynamic_cast<B*>(pc) == pb);
        assert(dynamic_cast<D*>(pc) == pd);

        // ...and catches incorrect casts
        C y;
        C* pc2 = &y;
        assert(dynamic_cast<B*>(pc2) == nullptr);
        assert(dynamic_cast<D*>(pc2) == nullptr);
    }

    // 6.  (Optional) vtable layout

    {
        cout << "*** vtable layout" << endl;

        uint64_t* p = reinterpret_cast<uint64_t*>(&x);
        void** vtbl = reinterpret_cast<void**>(p[0]);

        w( reinterpret_cast<int64_t>(vtbl[-2]) );
        w( vtbl[-1] );
        w( vtbl[0] );
        w( reinterpret_cast<int64_t>(vtbl[1]) );
        w( vtbl[2] );
        w( vtbl[3] );

        // First vptr points to vtbl[0]
        assert(reinterpret_cast<void**>(p[0]) == &vtbl[0]);
        // Second vptr points to vtbl[3]
        assert(reinterpret_cast<void**>(p[2]) == &vtbl[3]);
    }
    */
}
