Bonjour,
Le besoin:
Mon appli est constituée de plusieurs éléments X. J'ai une configuration pour ces X par défaut. Cette configuration contient un ensemble d'attributs de type différent contenant la valeur par défaut (cette valeur est modifiable).
Chacun des éléments X peut utiliser soit la valeur par défaut, soit utiliser une valeur particulière. Cela doit être transparent pour X.
Je ne souhaite pas associer chaque instance de X avec une instance de configuration pour: 1/ Eviter de créer un objet configuration si un seul paramètre change, 2/ Bénéficier d'une synchro pour les paramètres non spécialisés si la config par défaut change.

La solution sur laquelle je risque de partir:
Une classe CConfigurationDefaut avec une seule instance pour contenir la configuration par défaut. Cette classe contient en outre les attributs de configuration et une méthode pour y accéder.
Une classe CConfig contenant les éléments spécialisés d'une configuration particulière. Elle contient une méthode pour accéder à un attribut, une méthode pour en changer la valeur (et alors créée la spécialisation), et une méthode de classe pour modifier la valeur par défaut. J'ai utilisé une map avec boost::any pour maintenir la liste des attributs spécialisés.
Je souhaite n'accéder dans X qu'à l'interface de CConfig pour retrouver/positionner les valeurs des attributs sans me soucier s'il s'agit de la valeur par défaut ou d'une valeur spécialisée.

Passons aux choses sérieuses:
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
// Enumeration des attributs:
enum E_Attributs
{
   E_1=1,
   E_2=2,
   E_3=3
};
 
// class de description des attributs:
template<enum E_Attributs> struct CTraitTypeAttribut{};
// spécialisation partielle pour chaque attribut:
template<> struct CTraitTypeAttribut<E_1>{typedef int TypeAttribut;};
template<> struct CTraitTypeAttribut<E_2>{typedef char TypeAttribut;};
template<> struct CTraitTypeAttribut<E_3>{typedef double TypeAttribut;};
 
// class de configuration par défaut:
class CConfigurationDefaut
{
public:
   // récupération de l'instance:
   static CConfigurationDefaut&Defaut() {return m_Instance;}
 
   // récupération d'un attribut:
   template<enum E_Attributs P_Attr> typename CTraitTypeAttribut<P_Attr>::TypeAttribut& GetAttribut();
 
private:
   CTraitTypeAttribut<E_1>::TypeAttribut P1;
   CTraitTypeAttribut<E_2>::TypeAttribut P2;
   CTraitTypeAttribut<E_3>::TypeAttribut P3;
private:
   CConfigurationDefaut(){
      P1=12;
      P2='t';
      P3=4.56;
   }
   CConfigurationDefaut(const CConfigurationDefaut&){}
   ~CConfigurationDefaut(){}
   CConfigurationDefaut&operator=(const CConfigurationDefaut&){return *this;}
 
   static CConfigurationDefaut m_Instance;
};
// spécialisation partielle de l'accesseur aux attributs:
template<> CTraitTypeAttribut<E_1>::TypeAttribut& CConfigurationDefaut::GetAttribut<E_1>(){return P1;}
template<> CTraitTypeAttribut<E_2>::TypeAttribut& CConfigurationDefaut::GetAttribut<E_2>(){return P2;}
template<> CTraitTypeAttribut<E_3>::TypeAttribut& CConfigurationDefaut::GetAttribut<E_3>(){return P3;}
 
CConfigurationDefaut CConfigurationDefaut::m_Instance;
 
// class de configuration partielle:
class CConfig
{
public:
 
 
   template<enum E_Attributs P_Attribut>
   typename CTraitTypeAttribut<P_Attribut>::TypeAttribut getAttribut() const
   {
      std::map<enum E_Attributs,boost::any>::const_iterator L_itElement;
      L_itElement = m_mapAttributs.find(P_Attribut);
      if(L_itElement!=m_mapAttributs.end()){
         return boost::any_cast<typename CTraitTypeAttribut<P_Attribut>::TypeAttribut>(L_itElement->second);
      }
      return CConfigurationDefaut::Defaut().GetAttribut<P_Attribut>();
   }
 
   template<enum E_Attributs P_Attribut>
   void setAttribut(const typename CTraitTypeAttribut<P_Attribut>::TypeAttribut&P_oNouvelleValeur)
   {
      std::map<enum E_Attributs,boost::any>::iterator L_itElement;
      L_itElement = m_mapAttributs.find(P_Attribut);
      if(L_itElement==m_mapAttributs.end()){
         std::pair<std::map<enum E_Attributs,boost::any>::iterator,bool> L_eRetour;
         L_eRetour = m_mapAttributs.insert(std::pair<enum E_Attributs,boost::any>(P_Attribut,P_oNouvelleValeur));
         L_itElement = L_eRetour.first;
      }
      L_itElement->second = P_oNouvelleValeur;
   }
 
   template<enum E_Attributs P_Attribut>
   static void setAttributDefaut(const typename CTraitTypeAttribut<P_Attribut>::TypeAttribut&P_oNouvelleValeur)
   {
      CConfigurationDefaut::Defaut().GetAttribut<P_Attribut>() = P_oNouvelleValeur;
   }
 
private:
   std::map<enum E_Attributs,boost::any> m_mapAttributs;
};
Un exemple d'utilisation:
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
 
#include <iostream>
#include <string>
#include <boost/any.hpp>
#include <map>
#include "config.hxx"
 
 
int main(int argc, char* argv[])
{
   CConfig L_oConfig1;
   CConfig L_oConfig2;
   std::cout<<"Config 1/E_1: "<<L_oConfig1.getAttribut<E_1>()<<std::endl;
   std::cout<<"Config 2/E_1: "<<L_oConfig2.getAttribut<E_1>()<<std::endl;
   L_oConfig2.setAttribut<E_1>(3);
   std::cout<<"Config 1/E_1: "<<L_oConfig1.getAttribut<E_1>()<<std::endl;
   std::cout<<"Config 2/E_1: "<<L_oConfig2.getAttribut<E_1>()<<std::endl;
   CConfig::setAttributDefaut<E_1>(-1);
   std::cout<<"Config 1/E_1: "<<L_oConfig1.getAttribut<E_1>()<<std::endl;
   std::cout<<"Config 2/E_1: "<<L_oConfig2.getAttribut<E_1>()<<std::endl;
 
   std::getc(stdin);
	return 0;
}
La question:
Chaque ajout d'attribut est un peu laborieux: ajout d'un enum, ajout de l'attribut, initialisation de l'attribut, spécialisation d'une classe trait, spécialisation d'une méthode d'accès.
Quelqu'un n'aurait pas une idée plus simple?
Ou quelqu'un aurait-il une astuce pour faciliter l'ajout d'attribut?
Merci.