Résumé du modèle de visiteur (C ++)

Inconvénients d'une implémentation typique

L'article ne fournit pas intentionnellement un exemple d'implémentation typique du modèle de visiteur en C ++.





Si vous n'êtes pas familier avec ce modèle, vous pouvez vous le familiariser ici .





En un mot, ce modèle est très utile si vous avez besoin de parcourir une collection de pointeurs vers une classe de base abstraite, en leur appliquant une sorte d'opération en fonction du type qui est caché derrière l'abstraction.





Par conséquent, passons directement aux lacunes que nous souhaitons éliminer.





  • ( ), . ( - , .)





  • , , . - 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.





( CRTP ).





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 .





, AbstractVisitor< T >.( .)





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





  • , , .





  • , .. .





  • Nous pouvons profiter pleinement des modèles de fonctions.





  • Il reste possible d'utiliser l'implémentation habituelle du visiteur, il suffit juste de faire votre héritier de AbstractVisitors et de le passer à la fonction accept .









Quels sont les inconvénients?





  • Indirection supplémentaire, depuis Dispatcher contient un foncteur.









Lien vers le code dans l'explorateur du compilateur.





Source complète:
#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); } );
    }
}
      
      














All Articles