#include <typeinfo>
#include <memory>
#include <iostream>
#include <string>
using namespace std;


class TDirectoryEntryPtrBase {
public:
	virtual ~TDirectoryEntryPtrBase() {}

	virtual void* GetObjectAddr() const = 0;

	virtual const std::type_info& GetTypeInfo() const = 0;

	template<class U>
	U* Cast() const;

private:
	virtual void CastImpl() const = 0;
};

template<class U>
U* TDirectoryEntryPtrBase::Cast() const
{
	try{ CastImpl(); }
	catch (U* ptr) { return static_cast<U*>(ptr); }
	catch (...) {}
	return nullptr;
}

template <class T>
class TDirectoryEntryPtr: public TDirectoryEntryPtrBase {
public:
	TDirectoryEntryPtr(T* obj): fObj(obj) {}

	TDirectoryEntryPtr(const shared_ptr<T>& ptr): fObj(ptr) {}

	void* GetObjectAddr() const final { return fObj.get(); }

	const std::type_info& GetTypeInfo() const final { return typeid(T); }

private:
	std::shared_ptr<T> fObj;

	void CastImpl() const final { throw fObj.get(); }
};


template <class T>
string Find(const TDirectoryEntryPtrBase* ptr)
{
	if (ptr->GetTypeInfo() == typeid(T))
		return "kValidValue";
	if (ptr->Cast<T>())
		return "kValidValueBase";
	return "kTypeMismatch";
}


class U/*nrelated*/ {};
/* non-virtual */
class B {};
class D: public B {};
/* virtual */
class B_v {public: virtual ~B_v() {}};
class D_v: public B_v {};
/* deep hierarchy, non-virtual */
class Top {};
class Mid: public Top {};
class Bot: public Mid {};
/* deep hierarchy, virtual */
class Top_v {public: virtual ~Top_v() {}};
class Mid_v: public Top_v {};
class Bot_v: public Mid_v {};
/* multiple inheritance */
class B1 {};
class B2 {};
class Dm: public B1, public B2 {};
/* virtual base */
class Bv1: public virtual B_v {};
class Bv2: public virtual B_v {};
class Dvm: public Bv1, public Bv2 {};



int main()
{
	TDirectoryEntryPtr<D> d(new D);
	TDirectoryEntryPtr<B> b(new B);
	TDirectoryEntryPtr<D_v> d_v(new D_v);
	TDirectoryEntryPtr<B_v> b_v(new B_v);

	TDirectoryEntryPtr<Top> top(new Top);
	TDirectoryEntryPtr<Mid> mid(new Mid);
	TDirectoryEntryPtr<Bot> bot(new Bot);
	TDirectoryEntryPtr<Top_v> top_v(new Top_v);
	TDirectoryEntryPtr<Mid_v> mid_v(new Mid_v);
	TDirectoryEntryPtr<Bot_v> bot_v(new Bot_v);

	TDirectoryEntryPtr<B1> b1(new B1);
	TDirectoryEntryPtr<B2> b2(new B2);
	TDirectoryEntryPtr<Dm> dm(new Dm);

	TDirectoryEntryPtr<Bv1> bv1(new Bv1);
	TDirectoryEntryPtr<Bv2> bv2(new Bv2);
	TDirectoryEntryPtr<Dvm> dvm(new Dvm);

	cout << "Non-virtual:\n";
	cout << "Find<D>(d) [exact]    " << Find<D>(&d) << endl;
	cout << "Find<B>(d) [upcast]   " << Find<B>(&d) << endl;
	cout << "Find<D>(b) [downcast] " << Find<D>(&b) << endl;
	cout << "Find<B>(b) [exact]    " << Find<B>(&b) << endl;
	cout << endl;

	cout << "Virtual:\n";
	cout << "Find<D_v>(d_v) [exact]    " << Find<D_v>(&d_v) << endl;
	cout << "Find<B_v>(d_v) [upcast]   " << Find<B_v>(&d_v) << endl;
	cout << "Find<D_v>(b_v) [downcast] " << Find<D_v>(&b_v) << endl;
	cout << "Find<B_v>(b_v) [exact]    " << Find<B_v>(&b_v) << endl;
	cout << endl;

	cout << "Unrelated class:\n";
	cout << "Find<U>(d)   " << Find<U>(&d) << endl;
	cout << "Find<U>(b)   " << Find<U>(&b) << endl;
	cout << "Find<U>(d_v) " << Find<U>(&d_v) << endl;
	cout << "Find<U>(b_v) " << Find<U>(&b_v) << endl;
	cout << endl;

	cout << "Deep hierarchy, non-virtual:\n";
	cout << "Find<Top>(top) [ 0 cast(s)] " << Find<Top>(&top) << endl;
	cout << "Find<Top>(mid) [ 1 cast(s)] " << Find<Top>(&mid) << endl;
	cout << "Find<Top>(bot) [ 2 cast(s)] " << Find<Top>(&bot) << endl;
	cout << "Find<Mid>(top) [-1 cast(s)] " << Find<Mid>(&top) << endl;
	cout << "Find<Mid>(mid) [ 0 cast(s)] " << Find<Mid>(&mid) << endl;
	cout << "Find<Mid>(bot) [ 1 cast(s)] " << Find<Mid>(&bot) << endl;
	cout << "Find<Bot>(top) [-2 cast(s)] " << Find<Bot>(&top) << endl;
	cout << "Find<Bot>(mid) [-1 cast(s)] " << Find<Bot>(&mid) << endl;
	cout << "Find<Bot>(bot) [ 0 cast(s)] " << Find<Bot>(&bot) << endl;
	cout << endl;

	cout << "Deep hierarchy, virtual:\n";
	cout << "Find<Top_v>(top_v) [ 0 cast(s)] " << Find<Top_v>(&top_v) << endl;
	cout << "Find<Top_v>(mid_v) [ 1 cast(s)] " << Find<Top_v>(&mid_v) << endl;
	cout << "Find<Top_v>(bot_v) [ 2 cast(s)] " << Find<Top_v>(&bot_v) << endl;
	cout << "Find<Mid_v>(top_v) [-1 cast(s)] " << Find<Mid_v>(&top_v) << endl;
	cout << "Find<Mid_v>(mid_v) [ 0 cast(s)] " << Find<Mid_v>(&mid_v) << endl;
	cout << "Find<Mid_v>(bot_v) [ 1 cast(s)] " << Find<Mid_v>(&bot_v) << endl;
	cout << "Find<Bot_v>(top_v) [-2 cast(s)] " << Find<Bot_v>(&top_v) << endl;
	cout << "Find<Bot_v>(mid_v) [-1 cast(s)] " << Find<Bot_v>(&mid_v) << endl;
	cout << "Find<Bot_v>(bot_v) [ 0 cast(s)] " << Find<Bot_v>(&bot_v) << endl;
	cout << endl;

	cout << "Multiple inheritance:\n";
	cout << "Find<B1>(dm) [upcast]   " << Find<B1>(&dm) << endl;
	cout << "Find<B2>(dm) [upcast]   " << Find<B2>(&dm) << endl;
	cout << "Find<B1>(b2) [sidecast] " << Find<B1>(&b2) << endl;
	cout << "Find<B2>(b1) [sidecast] " << Find<B2>(&b1) << endl;
	cout << endl;

	cout << "Virtual base:\n";
	cout << "FInd<B_v>(dvm) [virtual base upcast] " << Find<B_v>(&dvm) << endl;
	cout << "FInd<B_v>(bv1) [virtual base upcast] " << Find<B_v>(&bv1) << endl;
	cout << "Find<B_v>(bv2) [virtual base upcast] " << Find<B_v>(&bv2) << endl;
	cout << "Find<Bv1>(bv1) [exact]               " << Find<Bv1>(&bv1) << endl;
	cout << "Find<Bv2>(bv2) [exact]               " << Find<Bv2>(&bv2) << endl;
	cout << "Find<Bv1>(dvm) [upcast]              " << Find<Bv1>(&dvm) << endl;
	cout << "Find<Bv2>(dvm) [upcast]              " << Find<Bv2>(&dvm) << endl;
	cout << "Find<Bv1>(bv2) [sidecast]            " << Find<Bv1>(&bv2) << endl;
	cout << "Find<Bv2>(bv1) [sidecast]            " << Find<Bv2>(&bv1) << endl;
	cout << endl;
}