
Desventajas de una implementación típica
El artículo no proporciona intencionalmente un ejemplo de una implementación típica del patrón de visitante en C ++.
Si no está familiarizado con este patrón, puede familiarizarse con él aquí .
En pocas palabras, este patrón es muy útil si necesita atravesar una colección de punteros a una clase base abstracta, aplicándoles algún tipo de operación dependiendo del tipo que está oculto detrás de la abstracción.
Por tanto, vayamos directamente a las deficiencias que nos gustaría eliminar.
( ), . ( - , .)
, , . - visit ( , - ). - visit .
.
?
, - , dynamic_cast static_cast .
, .
.
.
Source:
template< class T >
struct AbstractVisitor
{
virtual ~AbstractVisitor() = default;
virtual void visit( T& ) = 0;
};
(: - visit , AbstractVisitor .. T , )
. .
TypeList AbstractVisitors. AbstractVisitors , .
Source:
template< class ... T >
struct TypeList
{
};
template< class T >
struct AbstractVisitor
{
virtual ~AbstractVisitor() = default;
virtual void visit( T& ) = 0;
};
template< class ...T >
struct AbstractVisitors;
template< class ... T >
struct AbstractVisitors< TypeList< T... > > : AbstractVisitor< T >...
{
};
.. , ( , ). Dispatcher.
Source:
template< class Functor, class ... T >
struct Dispatcher;
template< class Functor, class ... T >
struct Dispatcher< Functor, TypeList< T... > > : AbstractVisitors< TypeList< T... > >
{
Dispatcher( Functor functor ) : functor( functor ) {}
Functor functor;
};
- visit.
Resolver, . Dispatcher Resolver-.
, (CRTP) Dispatcher Resolver.
Source:
template< class Dispatcher, class T >
struct Resolver : AbstractVisitor< T >
{
void visit( T& obj ) override
{
static_cast< Dispatcher* >( this )->functor( obj );
};
};
template< class Functor, class ... T >
struct Dispatcher< Functor, TypeList< T... > > : AbstractVisitors< TypeList< T... > >, Resolver< Dispatcher< Functor, TypeList< T... > >, T >...
{
Dispatcher( Functor functor ) : functor( functor ) {}
Functor functor;
};
.
Dispatcher. , Dispatcher , ?
, Resolver, Dispatcher .
Source:
template< class ... T >
struct AbstractVisitors< TypeList< T... > > : virtual AbstractVisitor< T >...
{
};
template< class Dispatcher, class T >
struct Resolver : virtual AbstractVisitor< T >
{
void visit( T& obj ) override
{
static_cast< Dispatcher* >( this )->functor( obj );
};
};
(AbstractObject) - (Object1, Object2), .
test test, .
:
Source:
struct Object1;
struct Object2;
using ObjectList = TypeList< Object1, Object2 >;
struct AbstractObject
{
virtual void accept( AbstractVisitors< ObjectList >& visitor ) = 0;
};
struct Object1 : AbstractObject
{
void accept( AbstractVisitors< ObjectList >& visitor ) override
{
static_cast< AbstractVisitor< Object1 >& >( visitor ).visit( *this );
};
};
struct Object2 : AbstractObject
{
void accept( AbstractVisitors< ObjectList >& visitor ) override
{
static_cast< AbstractVisitor< Object2 >& >( visitor ).visit( *this );
};
};
void test( Object1& obj )
{
std::cout << "1" << std::endl;
}
template< class T >
void test( T& obj )
{
std::cout << "2" << std::endl;
}
int main()
{
Object1 t1,t2,t3,t4;
Object2 e1,e2,e3;
std::vector< AbstractObject* > vector = { &t1, &e1, &t2, &t3, &e2, &e3, &t4 };
auto l = []( auto& obj ){ test(obj); };
Dispatcher<decltype(l), ObjectList> dispatcher;
for( auto* obj : vector )
{
obj->accept( dispatcher );
}
}
(: visitor.visit( *this )
, , .)
Dispatcher - , .
, - accept AbstractObject, Object1 Object2, .. , .
Dispatchable. C - accept - . Dispatcher.
DISPATCHED, - accept Object1 Object2.
Source:
template< class TypeList >
struct Dispatchable
{
virtual ~Dispatchable() = default;
virtual void accept( AbstractVisitors< TypeList >& ) = 0;
template< class Functor >
void dispatch( Functor functor )
{
static Dispatcher< decltype(functor), TypeList > dispatcher( functor );
accept( dispatcher );
};
};
#define DISPATCHED( TYPE, TYPE_LIST ) \
void accept( AbstractVisitors< TYPE_LIST >& visitor ) override \
{ \
static_cast< AbstractVisitor< TYPE >& >( visitor ).visit( *this ); \
}
AbstractObject Dispatchable. Object1 Object2 DISPATCHED.
Source:
struct Object1;
struct Object2;
using ObjectList = TypeList< Object1, Object2 >;
struct AbstractObject : Dispatchable< ObjectList >
{
};
struct Object1 : AbstractObject
{
DISPATCHED( Object1, ObjectList )
};
struct Object2 : AbstractObject
{
DISPATCHED( Object2, ObjectList )
};
, - accept . .
:
Source:
void test( Object1& obj )
{
std::cout << "1" << std::endl;
}
template< class T >
void test( T& obj )
{
std::cout << "2" << std::endl;
}
int main()
{
Object1 t1,t2,t3,t4;
Object2 e1,e2,e3;
std::vector< AbstractObject* > vector = { &t1, &e1, &t2, &t3, &e2, &e3, &t4 };
for( auto* obj : vector )
{
obj->dispatch( []( auto& obj ) { test(obj); } );
}
}
Output:
1
2
1
1
2
2
1
, , .
, .. .
Podemos aprovechar al máximo las plantillas de funciones.
Sigue siendo posible utilizar la implementación habitual del visitante, basta con hacer su heredero de AbstractVisitors y pasarlo a la función accept .
¿Cuales son las desventajas?
Indirección adicional, ya que Dispatcher contiene un functor.
Enlace al código en el explorador del compilador.
Fuente completa:
#include <type_traits>
#include <iostream>
#include <vector>
template< class ... T >
struct TypeList
{
};
template< class T >
struct AbstractVisitor
{
virtual ~AbstractVisitor() = default;
virtual void visit( T& ) = 0;
};
template< class ...T >
struct AbstractVisitors;
template< class ... T >
struct AbstractVisitors< TypeList< T... > > : virtual AbstractVisitor< T >...
{
};
template< class Dispatcher, class T >
struct Resolver : virtual AbstractVisitor< T >
{
void visit( T& obj ) override
{
static_cast< Dispatcher* >( this )->functor( obj );
};
};
template< class Functor, class ... T >
struct Dispatcher;
template< class Functor, class ... T >
struct Dispatcher< Functor, TypeList< T... > > : AbstractVisitors< TypeList< T... > >, Resolver< Dispatcher< Functor, TypeList< T... > >, T >...
{
Dispatcher( Functor functor ) : functor( functor ) {}
Functor functor;
};
template< class TypeList >
struct Dispatchable
{
virtual ~Dispatchable() = default;
virtual void accept( AbstractVisitors< TypeList >& ) = 0;
template< class Functor >
void dispatch( Functor functor )
{
static Dispatcher< decltype(functor), TypeList > dispatcher( functor );
accept( dispatcher );
};
};
#define DISPATCHED( TYPE, TYPE_LIST ) \
void accept( AbstractVisitors< TYPE_LIST >& visitor ) override \
{ \
static_cast< AbstractVisitor< TYPE >& >( visitor ).visit( *this ); \
}
struct Object1;
struct Object2;
using ObjectList = TypeList< Object1, Object2 >;
struct AbstractObject : Dispatchable< ObjectList >
{
};
struct Object1 : AbstractObject
{
DISPATCHED( Object1, ObjectList )
};
struct Object2 : AbstractObject
{
DISPATCHED( Object2, ObjectList )
};
void test( Object1& obj )
{
std::cout << "1" << std::endl;
}
template< class T >
void test( T& obj )
{
std::cout << "2" << std::endl;
}
int main()
{
Object1 t1,t2,t3,t4;
Object2 e1,e2,e3;
std::vector< AbstractObject* > vector = { &t1, &e1, &t2, &t3, &e2, &e3, &t4 };
for( auto* obj : vector )
{
obj->dispatch( []( auto& obj ) { test(obj); } );
}
}