#ifndef _CVISITOR_H_
#define _CVISITOR_H_

#include <stddef.h>
#include < vector >
#include < utility >

template< typename Derv >
struct DefaultCastPolicy {

template < typename Base >
static Derv* Cast( Base* b ) { return static_cast< Derv* >( b ); }

};

template< typename Base, template< class > class CastPolicy = DefaultCastPolicy  >
class Dispatch {

static size_t s_tag_;

template< typename Derv >
friend struct Tag;

template< typename Derv >
struct NewTag {
const size_t tag_;

NewTag( ) : tag_( Dispatch::s_tag_++ ) {}
operator size_t() { return tag_; }

static NewTag s_new_tag_;
};

public:

template< typename Derv >
struct Tag {
	operator size_t() { return NewTag< Derv >::s_new_tag_.tag_; }
};

private:

template< typename Func >
class VTable {
public:

VTable( Func def ) : default_( def ) { add< Base >( default_ ); }

template< typename Derv >
void add( Func func )
{
	const size_t index = Dispatch::Tag< Derv >();
	if( vtable_.size() < index + 1) {
		const size_t count = index + 1 - vtable_.size();
		vtable_.insert( vtable_.end(), count, default_ );
	}
	vtable_[ index ] = func;
}

Func get( Base* b )
{
	size_t index = b->__tag();
	if( index >= vtable_.size() )
		index = Dispatch::Tag< Base >();
	return vtable_[ index ];
}

private:
	typedef std::vector< Func > Table;
	Table vtable_;
	Func default_;
};


public:

template< 
	typename Ret = void, 
	typename A1 = void,
	typename A2 = void,
	typename A3 = void,
	typename A4 = void,
	typename A5 = void,
	typename A6 = void
>
class Visitor {
private:
	typedef Ret (Visitor::*Func)( Base*, A1, A2, A3, A4, A5, A6 );
	VTable< Func > vtable_;

protected:
template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3, A4, A5, A6) >
Ret thunk( Base* b, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6 )
{
	return ( static_cast< VisitorDerv* >( this)->*func ) ( CastPolicy< Derv >::Cast( b ), a1, a2, a3, a4, a5, a6 );
}

public:

Visitor( Func def ) : vtable_( def ) { }

Visitor& override() { return *this; }

template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3, A4, A5, A6) >
Visitor& add()
{
	vtable_.add< Derv >( &Visitor::thunk< VisitorDerv, Derv, func> );
	return *this;
}


Ret operator()( Base* b, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6 )
{
	return ( this->*vtable_.get( b ) ) ( b, a1, a2, a3, a4, a5, a6 );
}

};


template< 
	typename Ret , 
	typename A1,
	typename A2,
	typename A3,
	typename A4,
	typename A5
>
class Visitor< Ret, A1, A2, A3, A4, A5, void > {
private:
	typedef Ret (Visitor::*Func)( Base*, A1, A2, A3, A4, A5 );
	VTable< Func > vtable_;

protected:
template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3, A4, A5 ) >
Ret thunk( Base* b, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5 )
{
	return ( static_cast< VisitorDerv* >( this)->*func ) ( CastPolicy< Derv >::Cast( b ), a1, a2, a3, a4, a5 );
}

public:

Visitor( Func def ) : vtable_( def ) { }

Visitor& override() { return *this; }

template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3, A4, A5 ) >
Visitor& add()
{
	vtable_.add< Derv >( &Visitor::thunk< VisitorDerv, Derv, func> );
	return *this;
}


Ret operator()( Base* b, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5 )
{
	return ( this->*vtable_.get( b ) ) ( b, a1, a2, a3, a4, a5 );
}

};

template< 
	typename Ret , 
	typename A1,
	typename A2,
	typename A3,
	typename A4
>
class Visitor< Ret, A1, A2, A3, A4, void, void > {
private:
	typedef Ret (Visitor::*Func)( Base*, A1, A2, A3, A4 );
	VTable< Func > vtable_;

protected:
template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3, A4 ) >
Ret thunk( Base* b, A1 a1, A2 a2, A3 a3, A4 a4 )
{
	return ( static_cast< VisitorDerv* >( this)->*func ) ( CastPolicy< Derv >::Cast( b ), a1, a2, a3, a4 );
}

public:

Visitor( Func def ) : vtable_( def ) { }

Visitor& override() { return *this; }

template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3, A4 ) >
Visitor& add()
{
	vtable_.add< Derv >( &Visitor::thunk< VisitorDerv, Derv, func> );
	return *this;
}


Ret operator()( Base* b, A1 a1, A2 a2, A3 a3, A4 a4 )
{
	return ( this->*vtable_.get( b ) ) ( b, a1, a2, a3, a4 );
}

};

template< 
	typename Ret , 
	typename A1,
	typename A2,
	typename A3
>
class Visitor< Ret, A1, A2, A3, void, void, void > {
private:
	typedef Ret (Visitor::*Func)( Base*, A1, A2, A3 );
	VTable< Func > vtable_;

protected:
template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3 ) >
Ret thunk( Base* b, A1 a1, A2 a2, A3 a3 )
{
	return ( static_cast< VisitorDerv* >( this)->*func ) ( CastPolicy< Derv >::Cast( b ), a1, a2, a3 );
}

public:

Visitor( Func def ) : vtable_( def ) { }

Visitor& override() { return *this; }

template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2, A3 ) >
Visitor& add()
{
	vtable_.add< Derv >( &Visitor::thunk< VisitorDerv, Derv, func> );
	return *this;
}


Ret operator()( Base* b, A1 a1, A2 a2, A3 a3 )
{
	return ( this->*vtable_.get( b ) ) ( b, a1, a2, a3 );
}

};

template< 
	typename Ret , 
	typename A1,
	typename A2
>
class Visitor< Ret, A1, A2, void, void, void, void > {
private:
	typedef Ret (Visitor::*Func)( Base*, A1, A2 );
	VTable< Func > vtable_;

protected:
template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2 ) >
Ret thunk( Base* b, A1 a1, A2 a2 )
{
	return ( static_cast< VisitorDerv* >( this)->*func ) ( CastPolicy< Derv >::Cast( b ), a1, a2 );
}

public:

Visitor( Func def ) : vtable_( def ) { }

Visitor& override() { return *this; }

template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1, A2 ) >
Visitor& add()
{
	vtable_.add< Derv >( &Visitor::thunk< VisitorDerv, Derv, func> );
	return *this;
}


Ret operator()( Base* b, A1 a1, A2 a2 )
{
	return ( this->*vtable_.get( b ) ) ( b, a1, a2 );
}

};

template< 
	typename Ret , 
	typename A1
>
class Visitor< Ret, A1, void, void, void, void, void > {
private:
	typedef Ret (Visitor::*Func)( Base*, A1 );
	VTable< Func > vtable_;

protected:
template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1 ) >
Ret thunk( Base* b, A1 a1 )
{
	return ( static_cast< VisitorDerv* >( this)->*func ) ( CastPolicy< Derv >::Cast( b ), a1 );
}

public:

Visitor( Func def ) : vtable_( def ) { }

Visitor& override() { return *this; }

template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv*, A1 ) >
Visitor& add()
{
	vtable_.add< Derv >( &Visitor::thunk< VisitorDerv, Derv, func> );
	return *this;
}


Ret operator()( Base* b, A1 a1 )
{
	return ( this->*vtable_.get( b ) ) ( b, a1 );
}

};

template< 
	typename Ret
>
class Visitor< Ret, void, void, void, void, void, void > {
protected:
	typedef Ret (Visitor::*Func)( Base* );
	VTable< Func > vtable_;

protected:
template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv* ) >
Ret thunk( Base* b )
{
	return ( static_cast< VisitorDerv* >( this)->*func ) ( CastPolicy< Derv >::Cast( b ) );
}

public:

Visitor( Func def ) : vtable_( def ) { }

Visitor& override() { return *this; }

template< typename VisitorDerv, typename Derv, Ret (VisitorDerv::*func)( Derv* ) >
Visitor& add()
{
	vtable_.add< Derv >( &Visitor::thunk< VisitorDerv, Derv, func> );
	return *this;
}


Ret operator()( Base* b )
{
	return ( this->*vtable_.get( b ) ) ( b );
}

};

};

template< typename Base, template< class > class CastPolicy >
size_t Dispatch< Base, CastPolicy >::s_tag_ = 0;

template< typename Base, template< class > class CastPolicy >
template< typename Derv >
typename Dispatch< Base, CastPolicy >::template NewTag< Derv >
typename Dispatch< Base, CastPolicy >::template NewTag< Derv >::s_new_tag_;


#endif
