Bonjour,
J'ai des pointeurs vers des instances de différentes classes A, B, C, D.
Toutes ces classes ont une methode getId() renvoyant une std::string.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 A * a; std::unique_ptr< B > b; std::shared_ptr< C > c; std::weak_ptr < D > d;
Je souhaite créer une classe template Controller< T > de ce type:
Et je voudrais pouvoir l'utiliser avec tous les types de pointeurs cités plus haut:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 template< T > class Controller { T target; public: Controller( const T & target ) : target{ target } {} void print(){ cout << target->getId(); } };
Dans le cas d'un share_ptr j'aimerais pouvoir choisir si le Controller partage la responsabilité de l'objet pointé en le référençant sous forme de shared_ptr, ou pas en le référençant comme weak_ptr.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 Controller< A > aCtrl( a ); aCtrl.print(); Controller< B > bCtrl( b ); bCtrl.print(); Controller< C > cCtrl( c ); cCtrl.print(); Controller< D > dCtrl( d ); dCtrl.print();
Sachant que j'ai encore peu d'expérience avec les templates, j'ai écris une solution avant de venir poser cette question. J'imagine que je suis loin d'être le premier à me confronter à ce problème, mais il est difficile de trouver une réponse dans l'abondance de sujets traitant des templates et des pointeurs.
Mes questions sont:
- est-ce que je m'y prends bien ?
- y a-t-il plus simple ?
- suis-je en train de réinventer la roue pour rien ?
Voici ce que j'ai commis. L'idée est d'ajouter un second paramètre template qui permet l'adaptation aux différents types de pointeurs (il est possible que ce soit ce qu'on appelle une classe de trait, je ne suis pas sûr, je découvre encore le sujet).
Une classe Adapteur est de la forme:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 template < class Target, template < class > class Adapter > class Controller
La classe controller est donc:
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 template < class T > class AnAdapter { public: // Le type de paramètre à passer au constructeur de Controller using Source = ??? ; // au choix T *, unique_ptr<T>, shared_ptr< T >, weak_ptr<T>, et éventuellement autre chose // Le type de Controller::target using Storage = ??? ; // // au choix T *, shared_ptr< T > ou weak_ptr<T> // Fabrique un objet Storage à partir d'un paramètre Source static Storage makeStorage( const Source & t ); // Renvoie un pointeur qui permet d'accéder à l'objet controllé // avec la même syntaxe, ici cout << ptr( target )->getId(); static T * ptr( Storage & t ); } ;
Merci pour votre lecture, je vous laisse avec un exemple fonctionnel complet comprenant plusieurs adaptateurs (et une seule classe A, pas la peine d'alourdir avec B, C et D je pense):
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 template < class Target, template < class > class Adapter > class Controller { private: using Adpt = Adapter< Target > ; using Source = typename Adpt::Source ; using Storage = typename Adpt::Storage ; Storage target ; public: Controller( const Source & target ) : target{ Adpt::makeStorage( target ) } { } void setTarget( const Source & target ) { this->target = Adpt::makeStorage( target ) ; } void printTargetId() { auto ptr = Adpt::ptr( target ) ; if( ptr ) cout << "Target is " << ptr->getId() << endl ; else cout << "No target" << endl ; } } ;
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 #include <iostream> #include <memory> #include <string> using namespace std ; // Store and use a raw pointer template < class T > class RawPointerAdapter { public: using Source = T * ; using Storage = T * ; static Storage makeStorage( const Source & t ){ return t ; } static T * ptr( Storage & t ){ return t ; } } ; // Store and use a raw pointer obtained from an unique_ptr template < class T > class RawPointerFromUniqueAdapter { public: using Source = unique_ptr< T > ; using Storage = T * ; static Storage makeStorage( const Source & t ){ return t.get() ; } static T * ptr( Storage & t ){ return t ; } } ; // Store and use a shared_ptr template < class T > class SharedPointerAdapter { public: using Source = shared_ptr< T > ; using Storage = shared_ptr< T > ; static Storage makeStorage( const Source & t ){ return t ; } static shared_ptr< T > & ptr( Storage & t ){ return t ; } } ; // Store and use a weak_ptr, obtained by either a weak_ptr or a shared_ptr template < class T > class WeakPointerAdapter { public: using Source = weak_ptr< T > ; using Storage = weak_ptr< T > ; static Storage makeStorage( const Source & t ){ return t ; } static shared_ptr< T > ptr( Storage & t ){ return t.lock() ; } } ; template < class Target, template < class > class Adapter > class Controller { private: using Adpt = Adapter< Target > ; using Source = typename Adpt::Source ; using Storage = typename Adpt::Storage ; Storage target ; public: Controller( const Source & target ) : target{ Adpt::makeStorage( target ) } { } void setTarget( const Source & target ) { this->target = Adpt::makeStorage( target ) ; } void printTargetId() { auto ptr = Adpt::ptr( target ) ; if( ptr ) cout << "Target is " << ptr->getId() << endl ; else cout << "No target" << endl ; } } ; class A { string id ; public: A( string id ) : id{ id }{ } const string & getId(){ return id ; } } ; int main() { cout << endl << "RawPointerAdapter ------------------------------" << endl ; A a( "Target 1-1" ) ; A * aPtr = new A( "Target 1-2" ) ; Controller< A, RawPointerAdapter > ctrlRaw( &a ) ; ctrlRaw.printTargetId() ; ctrlRaw.setTarget( aPtr ) ; ctrlRaw.printTargetId() ; cout << endl << "RawPointerFromUniqueAdapter --------------------" << endl ; auto unik1 = make_unique< A >( "Target 2-1" ) ; auto unik2 = make_unique< A >( "Target 2-2" ) ; Controller< A, RawPointerFromUniqueAdapter > ctrlUnik( unik1 ) ; ctrlUnik.printTargetId() ; ctrlUnik.setTarget( unik2 ) ; ctrlUnik.printTargetId() ; unik2.reset() ; // Don't do this because the stored pointer is invalid ! // ctrl2.printTargetId() ; cout << endl << "SharedPointerAdapter ---------------------------" << endl ; auto sh1 = make_shared< A >( "Target 3-1" ) ; auto sh2 = make_shared< A >( "Target 3-2" ) ; auto sh3 = sh2 ; Controller< A, SharedPointerAdapter > ctrlShared( sh1 ) ; ctrlShared.printTargetId() ; ctrlShared.setTarget( sh2 ) ; ctrlShared.printTargetId() ; ctrlShared.setTarget( sh3 ) ; ctrlShared.printTargetId() ; sh3.reset() ; ctrlShared.printTargetId() ; sh2.reset() ; ctrlShared.printTargetId() ; // Still work because ctrl3 store the shared_ptr cout << endl << "WeakPointerAdapter -----------------------------" << endl ; weak_ptr< A > wk1 = sh1 ; Controller< A, WeakPointerAdapter > ctrlWeak( wk1 ) ; ctrlWeak.printTargetId() ; ctrlWeak.setTarget( sh1 ) ; ctrlWeak.printTargetId() ; sh1.reset() ; ctrlWeak.printTargetId() ; // Print "No target" return 0 ; }
Partager