Bonjour à tous,
Dans un gros projet que je développe actuellement, j'utilise en masse le pattern visiteur pour étendre les fonctionnalités de mes hiérarchies classes. Outre le coté laborieux de la chose lorsque je rajoute une classe dans ma hiérarchie (modifier tous les visiteurs dépendant de la classe de base), je dois maintenant implémenter un système de plugin permettant d'ajouter dynamiquement de nouvelles classes. Et donc impossible d'étendre les fonctionnalités de ces classes avec mes visiteurs classiques.

Je cherche donc une solution de remplacement et je suis tombé sur cette article décrivant un visiteur dynamique basé sur le RTTI. Le principe et d'enregistrer dynamiquement les fonctions de visitation (si ca se dit) pour chaque type. J'ai implémenté une première version de ce visiteur et je trouve ca plutôt pratique à utiliser, je peux même l'utiliser pour étendre mes précédents visiteurs (par héritage) sans foutre en l'air tout mon code.
J'aimerais avoir votre avis sur ce visiteur dynamique et ces éventuelles limitations (utilisation des fonctions de RTTI, faible vitesse d'exécution, etc...), voir s'il est totalement à proscrire.

Pour vous donner une idée plus précise, je poste également une première implémentation de ce visiteur dynamique.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <map>
#include <iostream>
#include <typeinfo.h>
 
//conversion du parametre de type BASE_CLASS_TYPE vers CLASS_TYPE
template<class CLASS_TYPE, class BASE_CLASS_TYPE, class VISITOR_TYPE>
void call(void (*f)( VISITOR_TYPE &, CLASS_TYPE & ),  VISITOR_TYPE & visitor, BASE_CLASS_TYPE & obj)
{
        f(visitor, static_cast<CLASS_TYPE&>(obj));
}
 
//modification du prototype de la fonction de visitation de void(VISITOR_TYPE&, BASE_CLASS_TYPE&) vers void(VISITOR_TYPE&, CLASS_TYPE&)
template<class CLASS_TYPE, class BASE_CLASS_TYPE, class VISITOR_TYPE>
boost::function<void(VISITOR_TYPE &, BASE_CLASS_TYPE &)> bind_function(void (*f)(VISITOR_TYPE &, CLASS_TYPE & ))
{
    return boost::function<void(VISITOR_TYPE &, BASE_CLASS_TYPE &)>( boost::bind( call<CLASS_TYPE,BASE_CLASS_TYPE,VISITOR_TYPE>, f, _1 , _2) );
}
 
//map static de const type_info* vers boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)
template< class VISITOR_TYPE, class BASE_CLASS_TYPE>
static std::map<const type_info*,boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)>> & functions()
{
    static std::map<const type_info*,boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)>> static_map;
    return static_map;
}
 
//Le visiteur de base
template<class VISITOR_TYPE, class BASE_CLASS_TYPE>
class BaseVisitor
{
public:
 
    typedef boost::function<void(VISITOR_TYPE &,BASE_CLASS_TYPE &)> function_type;
 
    //enregistrement d'une fonction de visitation
    template< class CLASS_TYPE >
    static void Register(void (*fn)(VISITOR_TYPE & ,CLASS_TYPE & ) )
    {
        functions<VISITOR_TYPE,BASE_CLASS_TYPE>()[&typeid(CLASS_TYPE)] = bind_function<CLASS_TYPE,BASE_CLASS_TYPE,VISITOR_TYPE>(fn);
    }
 
    //renvoie la fonction de visitation, pas indispensable
    template< class CLASS_TYPE >
    static function_type GetFunction()
    {
        std::map<const type_info*,function_type>::iterator it = functions<VISITOR_TYPE,BASE_CLASS_TYPE>().find(&typeid(CLASS_TYPE));
        if(it != functions<VISITOR_TYPE,BASE_CLASS_TYPE>().end())
            return it->second;
        else
            return function_type();
    }
 
    //Visite un objet
    virtual void Visit(BASE_CLASS_TYPE & obj)
    {
        std::map<const type_info*,function_type>::iterator it = functions<VISITOR_TYPE,BASE_CLASS_TYPE>().find(&typeid(obj));
        if(it != functions<VISITOR_TYPE,BASE_CLASS_TYPE>().end())
            it->second(static_cast<VISITOR_TYPE&>(*this),obj);
        else
            DefaultBehavior(obj);
    }
 
    //Comportement par default pour les classes qui ne sont pas enregistrées
    virtual void DefaultBehavior(BASE_CLASS_TYPE & )
    {}
};
 
 
 
//Hiérarchie de classe bidon, avec fonction virtuelle obligatoire pour l'utilisation du RTTI
struct A{
    int virtual get(){return 0;}
};
 
struct B : public A{
 
};
 
struct C : public A{};
 
 
//Un visiteur quelconque
class visitor : public BaseVisitor<visitor,A>
{};
 
//Les fonctions de visitation
void testA(visitor & v, A & a)
{
    std::cout<<"A"<<std::endl;
}
 
void testB(visitor & v, B & b)
{
    std::cout<<"B"<<std::endl;
}
 
void testC(visitor & v, C & b)
{
    std::cout<<"C"<<std::endl;
}
 
 
int main()
{
    A *a=new A;
    A *b=new B;
    C *c=new C;
 
    //enregistrement des fonctions
    visitor::Register(&testA);
    visitor::Register(&testB);
 
    visitor v;
    v.Visit(*a);
    v.Visit(*b);
    v.Visit(*c);
 
    std::system("pause");
}