Zusammenfassen des Besuchermusters (C ++)

Nachteile einer typischen Implementierung

Der Artikel enthält absichtlich kein Beispiel für eine typische Implementierung des Besuchermusters in C ++.





Wenn Sie nicht vertraut mit diesem Muster sind, Sie können sich damit vertraut machen hier .





Kurz gesagt, dieses Muster ist sehr nützlich, wenn Sie eine Sammlung von Zeigern auf eine abstrakte Basisklasse durchlaufen müssen, um abhängig vom Typ, der hinter der Abstraktion verborgen ist, eine Operation auf sie anzuwenden.





Gehen wir daher direkt zu den Mängeln, die wir beseitigen möchten.





  • ( ), . ( - , .)





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





  • , , .





  • , .. .





  • Wir können die Funktionsvorlagen voll ausnutzen.





  • Es bleibt weiterhin möglich, die übliche Implementierung des Besuchers zu verwenden. Es reicht aus, nur Ihren Erben von AbstractVisitors zu machen und ihn an die Accept- Funktion zu übergeben .









Was sind die Nachteile?





  • Zusätzliche Indirektion, da Der Dispatcher enthält einen Funktor.









Link zum Code im Compiler-Explorer.





Vollständige Quelle:
#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