#include <iostream>

template <class C> const char* tn( C ) { return "unknown"; }
template <> const char* tn( int ) { return "int"; }
template <> const char* tn( long ) { return "long"; }

template <class C>
class a
{
public:
	virtual void f( C n )
	{ 
		std::cout << "a<" << tn( n ) << ">::f(" << tn( n ) << ")" << std::endl;
	}

	a<C>* getA( C )
	{
		return this;
	}
};

template <class C>
class b : virtual public a<C>
{
public:
	virtual void f( C n )
	{ 
		a<C>::f( n );
		std::cout << "b<" << tn( n ) << ">::(" << tn( n )<< ")" << std::endl;
	}
};

class c : public b<int>, public b<long>
{
public:
	virtual void f( int n )
	{ return f_impl( n ); }
	virtual void f( long n )
	{ return f_impl( n ); }

	template <class D>
	void f_impl( D n )
	{
		b<D>::f( n );
		std::cout << "c::f(" << tn( n ) << ")" << std::endl;
	}

	using a<int>::getA;
	using a<long>::getA;
/*
	virtual a<int>* getA( int n )
	{
		return b<int>::getA( n );
	}
	virtual a<long>* getA( long n )
	{
		return b<long>::getA( n );
	}
*/
};

class d : public c
{
public:
	virtual void f( int n )
	{ return f_impl( n ); }
	virtual void f( long n )
	{ return f_impl( n ); }

	template <class D>
	void f_impl( D n )
	{
		c::f( n );
		std::cout << "d::f(" << tn( n )<< ")" << std::endl;
	}
};

void test_cast( c* p )
{
	b<int>* bi = p;
	b<long>* bl = p;
	
	a<int>* ai = p;
	a<long>* al = p;
	
	a<int>* bai = bi;
	a<long>* bal = bl;
	
	a<int>* gai = p->getA( int(0) );
	a<long>* gal = p->getA( long(0) );

	a<int>* dai = dynamic_cast<a<int>*>(p);
	a<long>* dal = dynamic_cast<a<long>*>(p);
	b<int>* dbi = dynamic_cast<b<int>*>(p);
	b<long>* dbl = dynamic_cast<b<long>*>(p);
	
	a<int>* sai = static_cast<a<int>*>(p);
	a<long>* sal = static_cast<a<long>*>(p);
	b<int>* sbi = static_cast<b<int>*>(p);
	b<long>* sbl = static_cast<b<long>*>(p);

	p->f( long(0) );
	std::cout << "=================>calling p->f( long )" << std::endl;
	al->f( long(0) );
	std::cout << "=================>calling al->f( long )" << std::endl;

	std::cout << "dai=" << dai << " dal=" << dal << std::endl;
	std::cout << "sai=" << sai << " sal=" << sal << std::endl;
	std::cout << "bai=" << bai << " bal=" << bal << std::endl;
	std::cout << "gai=" << gai << " gal=" << gal << std::endl;
	std::cout << " ai=" <<  ai << "  al=" <<  al << std::endl;
	std::cout << "dbi=" << dbi << " dbl=" << dbl << std::endl;
	std::cout << "sbi=" << sbi << " sbl=" << sbl << std::endl;
	std::cout << " bi=" <<  bi << "  bl=" <<  bl << std::endl;
	std::cout << "  p=" << p << std::endl;
}

int main ( void )
{
	d d_o;
	c c_o;
	
	test_cast( &c_o );
	std::cout << "&c_o=" << &c_o << std::endl;
	std::cout << std::endl 
		<< "------------------------------------------" << std::endl;
	test_cast( &d_o );
	std::cout << "&d_o=" << &d_o << std::endl;

	return 0;
}

