Bonjour,

J'ai des pointeurs vers des instances de différentes classes A, B, C, D.
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;
Toutes ces classes ont une methode getId() renvoyant une std::string.
Je souhaite créer une classe template Controller< T > de ce type:
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(); }
};
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
Controller< A > aCtrl( a ); aCtrl.print();
Controller< B > bCtrl( b ); bCtrl.print();
Controller< C > cCtrl( c ); cCtrl.print();
Controller< D > dCtrl( d ); dCtrl.print();
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.

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).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
template <
	class Target,
	template < class > class Adapter
>
class Controller
Une classe Adapteur est de la forme:
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 );
} ;
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
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 ;
		}
} ;
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
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 ;
}