IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

[Qu'en pensez-vous?] Propriétés en C++


Sujet :

C++

  1. #1
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Par défaut [Qu'en pensez-vous?] Propriétés en C++
    Bonsoir tout le monde.
    Je commence le développement d'une Lib en C++ et je désire intégrer les propriétés à cette Lib en se basant sur l'article Implementing Properties In C++. Il y aura biensure les fonctions GetXXXX et SetXXXX mais je trouve très intéressant les propriétés.
    Donc, voici le code de la classe qui m'intéresse et qui j'aimerai qu'on me dise si c'est OK, si non, ce qui manque, ce qui est fait de trop, ce qui est mal fait, etc.
    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
     
    template <typename ObjectType, typename ValueType>
    class property
    {
    public :
    	// Type de pointeur de fonction pour Property Get
    	typedef ValueType (ObjectType::*PGETTER)(void);
    	// Type de pointeur de fonction pour Property Set
    	typedef void (ObjectType::*PSETTER)(ValueType value);
     
    private :
    	// Instance de l'objet contenant la propriété à gérer
    	ObjectType *m_pObject;
    	// Pointeur de la fonction Property Set
    	PSETTER Set;
    	// Pointeur de la fonction Property Get
    	PGETTER Get;
    public:
    	// Constructeur par défaut
    	property(void)
    	{
    		m_pObject = NULL;
    		Set = NULL;
    		Get = NULL;
    	}
     
    	// Initialisation de la propriété
    	// object : Instance de l'object contenant la propriété.
    	// pGet   : Pointeur vers la fonction de récupération de la valeur de la propriété.
    	// pSet   : Pointeur vers la fonction d'affectation de la valeur de la propriété.
    	void Setup(ObjectType *object, PGETTER pGet, PSETTER pSet)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		m_pObject = object;
    		this->Set = pSet;
    		this->Get = pGet;
    	}
     
    	// Vérifier l'accès en lecture
    	bool CanRead(void)
    	{
    		return (this->GSet != NULL);
    	}
     
    	// Vérifier l'accès en écriture
    	bool CanWrite(void)
    	{
    		return (this->Set != NULL);
    	}
     
    	// Surcharger le retour de la valeur casté au type de la propriété
    	operator ValueType()
    	{
    		return (m_pObject->*Get)();
    	}
     
    	// Surcharger l'affectation
    	ValueType operator =(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		if(this->Set == NULL) throw "Propriété en lecture seule!";
    		(m_pObject->*Set)(value);
    		return (m_pObject->*Get)();
    	}
     
    	// Comparer les objets et non pas les classe property
    	bool operator==(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		return value == (m_pObject->*Get)();
    	}
     
    	// Surcharger l'accès au membres pour accèder au membres de l'objet de la propriété
    	// et non pas l'instance de la classe property.
    	ValueType *operator->()
    	{
    		return (m_pObject->*Get)();
    	}
    };
    Pour plus d'informations, voici le code source du programme contenant la classe en question, des classes pour le teste et une main pour tester.
    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
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
     
    #include <stdio.h>
     
    ////////////////////////////////////////////////////////////////////////////////
    template <typename ObjectType, typename ValueType>
    class property
    {
    public :
    	// Type de pointeur de fonction pour Property Get
    	typedef ValueType (ObjectType::*PGETTER)(void);
    	// Type de pointeur de fonction pour Property Set
    	typedef void (ObjectType::*PSETTER)(ValueType value);
     
    private :
    	// Instance de l'objet contenant la propriété à gérer
    	ObjectType *m_pObject;
    	// Pointeur de la fonction Property Set
    	PSETTER Set;
    	// Pointeur de la fonction Property Get
    	PGETTER Get;
    public:
    	// Constructeur par défaut
    	property(void)
    	{
    		m_pObject = NULL;
    		Set = NULL;
    		Get = NULL;
    	}
     
    	// Initialisation de la propriété
    	// object : Instance de l'object contenant la propriété.
    	// pGet   : Pointeur vers la fonction de récupération de la valeur de la propriété.
    	// pSet   : Pointeur vers la fonction d'affectation de la valeur de la propriété.
    	void Setup(ObjectType *object, PGETTER pGet, PSETTER pSet)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		m_pObject = object;
    		this->Set = pSet;
    		this->Get = pGet;
    	}
     
    	// Vérifier l'accès en lecture
    	bool CanRead(void)
    	{
    		return (this->GSet != NULL);
    	}
     
    	// Vérifier l'accès en écriture
    	bool CanWrite(void)
    	{
    		return (this->Set != NULL);
    	}
     
    	// Surcharger le retour de la valeur casté au type de la propriété
    	operator ValueType()
    	{
    		return (m_pObject->*Get)();
    	}
     
    	// Surcharger l'affectation
    	ValueType operator =(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		if(this->Set == NULL) throw "Propriété en lecture seule!";
    		(m_pObject->*Set)(value);
    		return (m_pObject->*Get)();
    	}
     
    	// Comparer les objets et non pas les classe property
    	bool operator==(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		return value == (m_pObject->*Get)();
    	}
     
    	// Surcharger l'accès au membres pour accèder au membres de l'objet de la propriété
    	// et non pas l'instance de la classe property.
    	ValueType *operator->()
    	{
    		return (m_pObject->*Get)();
    	}
    };
    ////////////////////////////////////////////////////////////////////////////////
     
    // Juste pour tester Intellisense pour l'affichage de la liste des membres d'une classe
    // avec l'opérateur surchargé "->" de la classe property
    class Redirection
    {
    public :
    	int i;
    	Redirection()
    	{
    		i = (int)this;
    	}
    };
    ////////////////////////////////////////////////////////////////////////////////
     
    // Implémentation des propriétés dans une classe
    class Test
    {
    public :
    	// Constructeur par défaut
    	Test()
    	{
    		// Initialiser la valeur de la propriété Count
    		m_Count = 0;
    		// Préparer les paramètres de fonctionnement de la propriété Count en lecture/écriture
    		Count.Setup(this, &Test::GetCount, &Test::SetCount);
    		// Préparer les paramètres de fonctionnement de la propriété Redirectional en lecture seule
    		Redirectional.Setup(this, &Test::GetRedirection, NULL);
    	}
     
    	Test(int init)
    	{
    		m_Count = init;
    		// Préparer les paramètres de fonctionnement de la propriété Count en lecture/écriture
    		Count.Setup(this, &Test::GetCount, &Test::SetCount);
    		// Préparer les paramètres de fonctionnement de la propriété Redirectional en lecture seule
    		Redirectional.Setup(this, &Test::GetRedirection, NULL);
    	}
     
    	// Property setter de la propriété Count
    	void SetCount(int value)
    	{
    		printf("SetCount called\n");
    		m_Count = value;
    	}
     
    	// Property getter de la propriété Count
    	int GetCount(void)
    	{
    		printf("GetCount called\n");
    		return m_Count;
    	}
     
    	// Property getter de la propriété Redirectional
    	Redirection GetRedirection(void)
    	{
    		return this->m_redir;
    	}
     
    public :
    	// Déclaration de la propriété Count
    	property<Test, int> Count;
    	// Déclaration de la propriété Redirectional
    	property<Test, Redirection> Redirectional;
     
    private :
    	// Variable contenant la valeur de la propriété Count
    	int m_Count;
    	// Object de la propriété Redirectional
    	Redirection m_redir;
    };
    ////////////////////////////////////////////////////////////////////////////////
     
    // Programme principal
    // Quelques testes...
    int main(void)
    {
    	try {
    		Test test;
    		test.Count = 11;
    		int i = test.Count;
    		Test *pTest = new Test();
    		pTest->Count = 2;
    		printf("%d + %d = %d\n", (int)pTest->Count, (int)test.Count, pTest->Count + test.Count);
    		pTest->Count = test.Count;
    		printf("%d + %d = %d\n", (int)pTest->Count, (int)test.Count, pTest->Count + test.Count);
    		Redirection redir;
    		pTest->Redirectional = redir; // Affectation d'une propriété en lecture seule
    		delete pTest;
    	} catch (char *message) {
    		printf("Exception levée!\n\t%s\n", message);
    	} catch (...) {
    		printf("Exception non gérée levée!..");
    	}
    	while(true);
    }
    Je suis ouvert à tout commentaire, critère, conseil ou autre.
    Merci.

  2. #2
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Pas de remarques sur le code pour l'instant, je n'ai pas regardé en détails.

    Par contre, c'est la démarche qui m'intrigue. Pourquoi diable introduire des propriétés en C++ ? Que reproches-tu aux get/set ?

    J'ai l'impression qu'en plus, en introduisant un nouveau niveau d'indirection, tu vas clairement perdre en performance.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    223
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2007
    Messages : 223
    Par défaut
    Qu'est ce qui différencie une propriété d'un get/set finalement ?

  4. #4
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Pas de remarque sur le fond, même s'il y en aurait à faire, par contre sur la forme tu devrais utiliser boost::function pour stocker tes accesseurs. Cela te permettrait d'éviter de passer la classe en paramètre template de property, et d'avoir beaucoup plus de flexibilité sur le prototype des accesseurs.

    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
    template <typename T>
    class property
    {
    public:
     
        typedef boost::function<T ()> GetterType;
        typedef boost::function<void (T)> SetterType;
     
    public:
     
        property()
        {
        }
     
        property(GetterType getter, SetterType setter = SetterType())
        : m_getter(getter)
        , m_setter(setter)
        {
        }
     
        ...
     
    private :
     
        GetterType m_getter;
        SetterType m_setter;
    };
     
    class Test
    {
    public :
     
        Test()
        : Count(boost::bind(&Test::GetCount, this), boost::bind(&Test::SetCount, this))
        , Redirectional(boost::bind(&GetRedirection, this))
        {
        }
     
    public :
     
        property<int> Count;
        property<Redirection> Redirectional;
    };

  5. #5
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 292
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 292
    Par défaut
    J'en pense n1615 -- et il me semblait qu'il y avait eu quelques autres documents sur le sujet. (Je n'ai trop le temps de faire une étude comparative des diverses solutions techniques)
    Après la pertinence ... parfois cela peut l'être. Tant que l'on garde bien en tête que accesseurs ou propriétés on a vite vite de violer Déméter et sa loi.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  6. #6
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Par défaut
    Merci Luc Hermitte et Laurent Gomila pour les éclairsissement que vous m'avez apporté je vais étudier la lib Boost et le pdf que je trouve très intéressent pour y voir plus clair.
    Pour la question concernant les performances, je crois que ça va dépendre des programmeurs utilisant une telle lib. Ceux qui vise les performances vont utiliser les get/set alors que ceux qui se sentent à l'aise utilisant les propriétés surtout ceux qui travaillent ou migre de et vers dotnet vont utiliser les propriétés.

    Merci.

    [EDIT]
    Citation Envoyé par Luc Hermitte Voir le message
    Après la pertinence ... parfois cela peut l'être. Tant que l'on garde bien en tête que accesseurs ou propriétés on a vite vite de violer Déméter et sa loi.
    J'ai lu ce paragraphe plus de dix fois... Je comprends toujours rien

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    En général, les propriétés répondent à deux désirs :
    - Un souhait d'alléger certaines syntaxes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    a.setColor(b.getColor() * 0.5);
    a.color = b.color * 0.5;
    - Un souhait d'obtenir une certaine introspection sur les classes (quelles propriétés sont disponibles)

    Quel est ton but ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    Membre émérite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par défaut
    Moi j'aurais juste une question pas forcément pertinente : avec toute cette tringlerie (pointeurs de membre...), est-ce que le compilateur arrive à inliner correctement les cas triviaux?

  9. #9
    Membre éclairé Avatar de cynique
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    60
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 60
    Par défaut
    Citation Envoyé par emmr.rida Voir le message
    Bonsoir tout le monde.
    Je commence le développement d'une Lib en C++ et je désire intégrer les propriétés à cette Lib en se basant sur l'article Implementing Properties In C++. Il y aura biensure les fonctions GetXXXX et SetXXXX mais je trouve très intéressant les propriétés.
    Donc, voici le code de la classe qui m'intéresse et qui j'aimerai qu'on me dise si c'est OK, si non, ce qui manque, ce qui est fait de trop, ce qui est mal fait, etc.
    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
     
    template <typename ObjectType, typename ValueType>
    class property
    {
    public :
    	// Type de pointeur de fonction pour Property Get
    	typedef ValueType (ObjectType::*PGETTER)(void);
    	// Type de pointeur de fonction pour Property Set
    	typedef void (ObjectType::*PSETTER)(ValueType value);
     
    private :
    	// Instance de l'objet contenant la propriété à gérer
    	ObjectType *m_pObject;
    	// Pointeur de la fonction Property Set
    	PSETTER Set;
    	// Pointeur de la fonction Property Get
    	PGETTER Get;
    public:
    	// Constructeur par défaut
    	property(void)
    	{
    		m_pObject = NULL;
    		Set = NULL;
    		Get = NULL;
    	}
     
    	// Initialisation de la propriété
    	// object : Instance de l'object contenant la propriété.
    	// pGet   : Pointeur vers la fonction de récupération de la valeur de la propriété.
    	// pSet   : Pointeur vers la fonction d'affectation de la valeur de la propriété.
    	void Setup(ObjectType *object, PGETTER pGet, PSETTER pSet)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		m_pObject = object;
    		this->Set = pSet;
    		this->Get = pGet;
    	}
     
    	// Vérifier l'accès en lecture
    	bool CanRead(void)
    	{
    		return (this->GSet != NULL);
    	}
     
    	// Vérifier l'accès en écriture
    	bool CanWrite(void)
    	{
    		return (this->Set != NULL);
    	}
     
    	// Surcharger le retour de la valeur casté au type de la propriété
    	operator ValueType()
    	{
    		return (m_pObject->*Get)();
    	}
     
    	// Surcharger l'affectation
    	ValueType operator =(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		if(this->Set == NULL) throw "Propriété en lecture seule!";
    		(m_pObject->*Set)(value);
    		return (m_pObject->*Get)();
    	}
     
    	// Comparer les objets et non pas les classe property
    	bool operator==(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		return value == (m_pObject->*Get)();
    	}
     
    	// Surcharger l'accès au membres pour accèder au membres de l'objet de la propriété
    	// et non pas l'instance de la classe property.
    	ValueType *operator->()
    	{
    		return (m_pObject->*Get)();
    	}
    };
    Pour plus d'informations, voici le code source du programme contenant la classe en question, des classes pour le teste et une main pour tester.
    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
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
     
    #include <stdio.h>
     
    ////////////////////////////////////////////////////////////////////////////////
    template <typename ObjectType, typename ValueType>
    class property
    {
    public :
    	// Type de pointeur de fonction pour Property Get
    	typedef ValueType (ObjectType::*PGETTER)(void);
    	// Type de pointeur de fonction pour Property Set
    	typedef void (ObjectType::*PSETTER)(ValueType value);
     
    private :
    	// Instance de l'objet contenant la propriété à gérer
    	ObjectType *m_pObject;
    	// Pointeur de la fonction Property Set
    	PSETTER Set;
    	// Pointeur de la fonction Property Get
    	PGETTER Get;
    public:
    	// Constructeur par défaut
    	property(void)
    	{
    		m_pObject = NULL;
    		Set = NULL;
    		Get = NULL;
    	}
     
    	// Initialisation de la propriété
    	// object : Instance de l'object contenant la propriété.
    	// pGet   : Pointeur vers la fonction de récupération de la valeur de la propriété.
    	// pSet   : Pointeur vers la fonction d'affectation de la valeur de la propriété.
    	void Setup(ObjectType *object, PGETTER pGet, PSETTER pSet)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		m_pObject = object;
    		this->Set = pSet;
    		this->Get = pGet;
    	}
     
    	// Vérifier l'accès en lecture
    	bool CanRead(void)
    	{
    		return (this->GSet != NULL);
    	}
     
    	// Vérifier l'accès en écriture
    	bool CanWrite(void)
    	{
    		return (this->Set != NULL);
    	}
     
    	// Surcharger le retour de la valeur casté au type de la propriété
    	operator ValueType()
    	{
    		return (m_pObject->*Get)();
    	}
     
    	// Surcharger l'affectation
    	ValueType operator =(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		if(this->Set == NULL) throw "Propriété en lecture seule!";
    		(m_pObject->*Set)(value);
    		return (m_pObject->*Get)();
    	}
     
    	// Comparer les objets et non pas les classe property
    	bool operator==(const ValueType& value)
    	{
    		// TODO : Vérifier les paramètres et gérer les exceptions
    		return value == (m_pObject->*Get)();
    	}
     
    	// Surcharger l'accès au membres pour accèder au membres de l'objet de la propriété
    	// et non pas l'instance de la classe property.
    	ValueType *operator->()
    	{
    		return (m_pObject->*Get)();
    	}
    };
    ////////////////////////////////////////////////////////////////////////////////
     
    // Juste pour tester Intellisense pour l'affichage de la liste des membres d'une classe
    // avec l'opérateur surchargé "->" de la classe property
    class Redirection
    {
    public :
    	int i;
    	Redirection()
    	{
    		i = (int)this;
    	}
    };
    ////////////////////////////////////////////////////////////////////////////////
     
    // Implémentation des propriétés dans une classe
    class Test
    {
    public :
    	// Constructeur par défaut
    	Test()
    	{
    		// Initialiser la valeur de la propriété Count
    		m_Count = 0;
    		// Préparer les paramètres de fonctionnement de la propriété Count en lecture/écriture
    		Count.Setup(this, &Test::GetCount, &Test::SetCount);
    		// Préparer les paramètres de fonctionnement de la propriété Redirectional en lecture seule
    		Redirectional.Setup(this, &Test::GetRedirection, NULL);
    	}
     
    	Test(int init)
    	{
    		m_Count = init;
    		// Préparer les paramètres de fonctionnement de la propriété Count en lecture/écriture
    		Count.Setup(this, &Test::GetCount, &Test::SetCount);
    		// Préparer les paramètres de fonctionnement de la propriété Redirectional en lecture seule
    		Redirectional.Setup(this, &Test::GetRedirection, NULL);
    	}
     
    	// Property setter de la propriété Count
    	void SetCount(int value)
    	{
    		printf("SetCount called\n");
    		m_Count = value;
    	}
     
    	// Property getter de la propriété Count
    	int GetCount(void)
    	{
    		printf("GetCount called\n");
    		return m_Count;
    	}
     
    	// Property getter de la propriété Redirectional
    	Redirection GetRedirection(void)
    	{
    		return this->m_redir;
    	}
     
    public :
    	// Déclaration de la propriété Count
    	property<Test, int> Count;
    	// Déclaration de la propriété Redirectional
    	property<Test, Redirection> Redirectional;
     
    private :
    	// Variable contenant la valeur de la propriété Count
    	int m_Count;
    	// Object de la propriété Redirectional
    	Redirection m_redir;
    };
    ////////////////////////////////////////////////////////////////////////////////
     
    // Programme principal
    // Quelques testes...
    int main(void)
    {
    	try {
    		Test test;
    		test.Count = 11;
    		int i = test.Count;
    		Test *pTest = new Test();
    		pTest->Count = 2;
    		printf("%d + %d = %d\n", (int)pTest->Count, (int)test.Count, pTest->Count + test.Count);
    		pTest->Count = test.Count;
    		printf("%d + %d = %d\n", (int)pTest->Count, (int)test.Count, pTest->Count + test.Count);
    		Redirection redir;
    		pTest->Redirectional = redir; // Affectation d'une propriété en lecture seule
    		delete pTest;
    	} catch (char *message) {
    		printf("Exception levée!\n\t%s\n", message);
    	} catch (...) {
    		printf("Exception non gérée levée!..");
    	}
    	while(true);
    }
    Je suis ouvert à tout commentaire, critère, conseil ou autre.
    Merci.
    Tu devrais éffacer le constructeur par défaut et la fonction Setup(), et puis écrire un constructeur qui accepte des paramètres de Setup(). Un "property" ne changera jamais son objet ou les fonctions de "get" et "set". En ce moment, tu peux les changer plusieurs fois, ou tu peux éviter l'appel à Setup(), et puis quand tu appeleras "get" ou "set", tu appeleras une fonction NULL sur un objet NULL.

    Et il ya deux constructeurs: (objet, getter) et (objet, getter, setter) - c'est plus clair.

  10. #10
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Par défaut
    @ JolyLoic
    Mon but c'est d'offrir une certaine souplesse aux programmeurs qui vont utiliser la lib en question. Moi aussi je me sens à l'aise quand je travail avec des propriétés vu que j'ai fais mes débuts en VB puis C/C++ puis .NET

    @ cynique
    Oui, t'as raison, mais j'ai pas implémenté des constructeurs comme tu l'as suggérée car la syntaxe de la déclaration des propriétés deviendra un peu plus complexe. Je cherche toujours une solution pour ne plus avoir à inclure le type de la classe dans laquelle la propriété est déclarée et avoir seulement le type de la propriété comme dans l'exemple de Laurent Gomila mais sans avoir à utiliser Boost (pour l'instant!). Concernant les failles que t'as signalé, y aurai surement la gestion des exceptions ainsi que l'interdiction d'un deuxième appel à Setup.

    Concernant les questions sur le fonctionnement interne des compilateurs, j'ai pas encore atteint le niveau requis pour y répondre.

    Merci.

  11. #11
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Par défaut
    Voici une version amélioré de la classe property :
    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
     
    template <class T, typename TValue>
    class property
    {
    public :
    	// Type de pointeur de fonction pour Property Get
    	typedef TValue (T::*PGETTER)(void);
    	// Type de pointeur de fonction pour Property Set
    	typedef TValue (T::*PSETTER)(TValue value);
     
    private :
    	// Instance de l'objet contenant la propriété à gérer
    	T *m_pObject;
    	// Pointeur de la fonction Property Set
    	PSETTER pSetter;
    	// Pointeur de la fonction Property Get
    	PGETTER pGetter;
    	// Une seule initialisation
    	bool canInit;
    public:
    	// Constructeur par défaut
    	property(void)
    	{
    		m_pObject = NULL;
    		pSetter = NULL;
    		pGetter = NULL;
    		canInit = true;
    	}
     
    	// Initialisation de la propriété
    	// pObject   : Instance de l'object contenant la propriété.
    	// pGetter   : Pointeur vers la fonction de récupération de la valeur de la propriété.
    	// pSetter   : Pointeur vers la fonction d'affectation de la valeur de la propriété.
    	void Setup(T *pObject, PGETTER pGetFunction, PSETTER pSetFunction)
    	{
    		if(!canInit) throw "Tentative d'une seconde initialisation!..";
    		if(pObject == NULL) throw "Paramètre NULL : pObject";
    		m_pObject = pObject;
    		this->pSetter = pSetFunction;
    		this->pGetter = pGetFunction;
    		canInit = false;
    	}
     
    	// Vérifier l'accès en lecture
    	bool CanRead(void)
    	{
    		return (this->pGetter != NULL);
    	}
     
    	// Vérifier l'accès en écriture
    	bool CanWrite(void)
    	{
    		return (this->pSetter != NULL);
    	}
     
    	// Surcharger le retour de la valeur casté au type de la propriété
    	operator TValue()
    	{
    		// TODO : Assertion de m_pObject != NULL
    		// Personnellement, je me vois jamais utiliser une propriété en lecture seule!
    		if(this->pGetter == NULL) throw "Propriété en écriture seule!..";
    		return (m_pObject->*pGetter)();
    	}
     
    	// Surcharger l'affectation
    	TValue operator =(const TValue& value)
    	{
    		// TODO : Assertion de m_pObject != NULL
    		if(this->pSetter == NULL) throw "Propriété en lecture seule!";
    		return (m_pObject->*pSetter)(value);
    	}
     
    	// Comparer les objets et non pas les classe property
    	bool operator==(const TValue& value)
    	{
    		// TODO : Assertion de m_pObject != NULL
    		if(this->pGetter == NULL) throw "Propriété en écriture seule!..";
    		return value == (m_pObject->*pGetter)();
    	}
     
    	// Surcharger l'accès au membres pour accèder au membres de l'objet de la propriété
    	// et non pas l'instance de la classe property.
    	TValue *operator->()
    	{
    		// TODO : Assertion de m_pObject != NULL
    		if(this->pGetter == NULL) throw "Propriété en écriture seule!..";
    		return (m_pObject->*pGetter)();
    	}
    };
    Ce qui a changé :
    - Le prototype du setter est modifié pour retourner une valeur au cas où le setter refuse la nouvelle valeur et la corrige par une autre.
    - Une variable membre canInit est ajoutée pour la vérification d'une seconde tentative d'initialisation de la propriété. Elle prend comme valeur initiale true et change à false au premier appel de Setup().

    @ cynique
    J'ai maintenu la structure d'un constructeur par défaut et une fonction membre d'initialisation car on peut pas passer l'adresse de l'objet conteneur de la propriété si on déclare la propriété de façon statique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    property<Test, int> Count(this, &Test::GetCount, &Test::SetCount); // Erreur de syntaxe 'this'
    Et je vois pas l'intéret d'un constructeur avec paramètres plus une initialisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    property<Test, int> Count(&Test::GetCount, &Test::SetCount);
    ...
     
    this->Count(this);

    Qu'en pensez-vous?

    PS : Y a-t-il une astuce pour enlever le nom de la classe et rendre la déclaration : property<int> Propriete;

  12. #12
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Y a-t-il une astuce pour enlever le nom de la classe et rendre la déclaration : property<int> Propriete;
    Il faut ajouter un niveau d'abstraction, un truc de ce genre :
    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
    template <typename TValue>
    class HolderBase
    {
    public :
     
        virtual ~HolderBase() {}
        virtual TValue get() const = 0;
        virtual TValue set(TValue value) = 0;
    };
     
    template <class T, typename TValue>
    class Holder : public HolderBase<TValue>
    {
    public :
     
        typedef TValue (T::*PGETTER)(void);
        typedef TValue (T::*PSETTER)(TValue value);
     
    public :
     
        Holder(PGETTER getter, PSETTER setter, T* object) :
        m_getter(getter), m_setter(setter), m_object(object)
        {
        }
     
        virtual TValue get() const {return (m_object->*m_getter)();}
        virtual TValue set(TValue value) {return (m_object->*m_setter)(value);}
     
    private :
     
        PGETTER m_getter;
        PSETTER m_setter;
        T* m_object;
    };
     
    template <typename TValue>
    class property
    {
    public :
     
        template <typename T>
        void Setup(T *pObject, TValue (T::*pGetterFunction)(), TValue (T::*pSetterFunction)(TValue))
        {
            m_holder = new Holder<T, TValue>(pObject, pGetterFunction, pSetterFunction);
        }
     
        // operator= --> m_holder->set
        // operator TValue --> m_holder->get
     
    private :
     
        HolderBase<TValue>* m_holder;
    }
    Code pondu vite fait pour donner l'exemple.
    Je n'ai pas inclus la gestion mémoire du pointeur m_holder pour alléger le code.

  13. #13
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Par défaut
    Merci Laurent Gomila, ça va beaucoup m'aidé...

  14. #14
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 292
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 292
    Par défaut
    Citation Envoyé par emmr.rida Voir le message
    Luc Hermitte> Après la pertinence ... parfois cela peut l'être. Tant que l'on garde bien en tête que accesseurs ou propriétés on a vite vite de violer Déméter et sa loi.
    J'ai lu ce paragraphe plus de dix fois... Je comprends toujours rien
    Comprends que les accesseurs/mutateurs sont souvent employés à tord. Cherche "Loi de Déméter".
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  15. #15
    Membre émérite Avatar de HanLee
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    738
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2004
    Messages : 738
    Par défaut
    Et ma question, vous avez une idée ?

  16. #16
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Par défaut
    J'ai essayer une autre alternative pour implémenter des propriétés en C++, mais cette fois sans les templates. Les inconvenients de cette nouvelle implémentation c'est que ça va rendre le corps de la classe propriétaire un peu plus complexe si la classe propriété est implémentée inline dans le cas contraire, on peut pas avoir plusieurs classes avec les mêmes noms de propriétés.
    Le principe de cette solution et de créer un amitié réciproque entre la classe propriétaire et la classe propriété ou une contrat de confience entre classes amies. L'essentiel, c'est la sécuriété des données en dehors de la classe propriétaire.
    Voici le code :
    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
     
    #include <iostream>
     
    // Classe propriétaire
    class Test
    {
    protected :
    	int MaxCount;
    public :
    	Test(void) : MaxCount(10)
    	{
    		// Définir le propriétaire de l'objet propriété Count
    		Count.pTest = this;
    	}
     
    public :
    	// Classe propriété
    	class _Count
    	{
    		// Pour l'accés illimité à l'objet propriétaire
    		friend class Test;
    	private :
    		// Valeur encapsulée
    		int m_Value;
    		// Objet parent
    		Test *pTest;
    	public :
    		_Count(void) : pTest(NULL), m_Value(0) {}
     
    		// Getter
    		int get(void) const
    		{
    			if(pTest == NULL) throw "Propriété non initialisée! pTest est NULL";
    			return m_Value;
    		}
     
    		// Setter
    		int set(int value)
    		{
    			if(pTest == NULL) throw "Propriété non initialisée! pTest est NULL";
    			if(pTest->MaxCount > value) {
    				m_Value = pTest->MaxCount;
    				return m_Value;
    			}
    			m_Value = value;
    		}
     
    		operator int() const { return get(); }
     
    		int operator=(int value) { return set(value); }
    	} Count; // Instanciation après définition
    	// Pour un accès illimité pour initialisation
    	friend class _Count;
    };
     
    void main(void)
    {
    	Test test;
    	test.Count = 3;
    	test.Count = test.Count * 7;
    	test.Count = test.Count == 21 ? 6 : 7;
    	test.Count += 24; // Faut implémenter l'opérateur
    	test.Count /= 6; // Faut implémenter l'opérateur
    	test.Count++; // Faut implémenter l'opérateur
    }
    Merci d'avance pour vos suggestions et corrections

  17. #17
    Membre émérite Avatar de 10_GOTO_10
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    890
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2004
    Messages : 890
    Par défaut
    Au cas où ça intéresse quelqu'un, voici comment sont implémentées les propriétés dans ma librairie FreeVCL:

    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
    #define DECLARE_PROPERTY( base, type, var )                                    \
    protected:                                                                     \
    virtual type Get_##var(void);                                                  \
    virtual bool Set_##var(type);                                                  \
    public:                                                                        \
    struct _dp_##Get##var##_##Set##var                                             \
    {                                                                              \
        inline operator type()                                                     \
        {                                                                          \
            int Offset = (size_t) ((char *) &((base *) this)->var - (char *) this);\
            return ((base *) ((char *) this - Offset))->Get_##var();               \
        }                                                                          \
        inline type operator=(type const & value)                                  \
        {                                                                          \
            int Offset = (size_t) ((char *) &((base *) this)->var - (char *) this);\
            ((base *) ((char *) this - Offset))->Set_##var( value );               \
            return value ;                                                         \
        }                                                                          \
        inline bool operator==(type const & value)                                 \
        {                                                                          \
            int Offset = (size_t) ((char *) &((base *) this)->var - (char *) this);\
            return ((base *) ((char *) this - Offset))->Get_##var() == value;      \
        }                                                                          \
        inline bool operator!=(type const & value)                                 \
        {                                                                          \
            int Offset = (size_t) ((char *) &((base *) this)->var - (char *) this);\
            return ((base *) ((char *) this - Offset))->Get_##var() != value;      \
        }                                                                          \
        inline type operator->()                                                   \
        {                                                                          \
            int Offset = (size_t) ((char *) &((base *) this)->var - (char *) this);\
            return ((base *) ((char *) this - Offset))->Get_##var();               \
        }                                                                          \
    };                                                                             \
    friend struct _dp_##Get##var##_##Set##var;                                     \
    _dp_##Get##var##_##Set##var var
    Ensuite, il suffit de déclarer la propriété dans le fichier .h de la classe, par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      DECLARE_PROPERTY(MaClasse, bool, MaPropriete);

  18. #18
    Membre expérimenté
    Inscrit en
    Octobre 2007
    Messages
    236
    Détails du profil
    Informations personnelles :
    Âge : 46

    Informations forums :
    Inscription : Octobre 2007
    Messages : 236
    Par défaut
    Merci 10_GOTO_10, t'es le meilleur
    Bonne continuation et bon courage pour ton projet...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Que pensez-vous des générateurs de doc PHP ?
    Par Nonothehobbit dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 64
    Dernier message: 10/07/2007, 10h17
  2. Que pensez vous du nouveau kernel 2.6 ?
    Par GLDavid dans le forum Administration système
    Réponses: 58
    Dernier message: 02/08/2004, 15h45
  3. Borland prépare un EDI pour C# - qu'en pensez vous ?
    Par Marc Lussac dans le forum Actualités
    Réponses: 24
    Dernier message: 23/07/2003, 10h32
  4. Que pensez vous du mariage ASP Flash?
    Par tyma dans le forum Flash
    Réponses: 4
    Dernier message: 09/07/2003, 15h00
  5. Réponses: 13
    Dernier message: 11/05/2003, 13h25

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo