C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Aide pour realiser une classe de manipualtion de donnees
Goto page 1, 2, 3, 4  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French)
View previous topic :: View next topic  
Author Message
Aurélien REGAT-BARREL
Guest





PostPosted: Wed Jun 16, 2004 12:19 pm    Post subject: Aide pour realiser une classe de manipualtion de donnees Reply with quote



Bonjour,
Ca parrait long, mais c'est vite lu :-)

J'ai un certain nombre de classes ABC, DEF, GHI, ... qui ont la
particularité d'avoir une certain nombre (presque tout le temps 3) de
"valeurs" toutes de type double. En fait, la seule chose qui différencies
ces classes est le nom de ces valeurs :

class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }
private:
double a;
double b;
double c;
};

class DEF
{
public:
double D() const { return this->d; }
double E() const { return this->e; }
double F() const { return this->f; }
private:
double d;
double e;
double f;
};

etc...
C'est utile de les différencier, car ces valeurs n'ont pas la même
signification, et le typage est nécessaire.
Jusque là tout va bien, tout est calculé correctement.

Probleme :
Je veux dessiner ces valeurs dans un graphe, 1 valeur au choix sur chaque
axe.
Par exemple, dessiner A() et B() sur mon graphe.
Je voudrais donc faire un truc générique, et pas XX fonctions pour chaque
type.
J'ai voulu faire hériter toutes ces classes d'une même classe de base :
Triplet.

class Triplet
{
public:
virtual double Value( int Index ) const = 0;
};

Ainsi je file mon ABC ou DEF ou XYZ à mon graphe et je lui dit "dessine moi
la valeur 1 et 2 et il fait Value( 1 ) et Value ( 2 ) pour les récupérer.

class ABC : public Triplet
{
// idem que plus haut

public:
double Value( int Index )
{
switch ( Index )
{
case 0 : return this->A();
case 1 : return this->B();
case 2 : return this->C();
}
// exception
}
};

Mais Triplet est abstraite, et je ne peux pas contruire un
std::vector<Triplet> à passer pour dessiner.
Je compte du coup re-écrire toutes les classes dans ce style :

class Triplet
{
public:
virtual double Value( int Index ) const { return this->values.at(
Index ); }
private:
std::vector<double> values;
};

class ABC : public Triplet
{
public:
double A() const { return this->values[ 0 ]; }
double B() const { return this->values[ 1 ]; }
double C() const { return this->values[ 2 ]; }
};

Y'a-t-il une meilleure solution ?
Par exemple :

class ABC : public std::vector<double>
{
public:
double A() const { return this->at( 0 ); }
double B() const { return this->at( 1 ); }
double C() const { return this->at( 2 ); }
};

Merci de m'avoir lu.

--
Aurélien REGAT-BARREL


Back to top
Nicolas Repiquet
Guest





PostPosted: Wed Jun 16, 2004 5:10 pm    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote




"Aurélien REGAT-BARREL" <nospam-aregatba (AT) yahoo (DOT) fr.invalid> wrote

Quote:
Bonjour,

Bonjour.

Quote:
Mais Triplet est abstraite, et je ne peux pas contruire un
std::vector<Triplet> à passer pour dessiner.
Je compte du coup re-écrire toutes les classes dans ce style :

class Triplet
{
public:
virtual double Value( int Index ) const { return this->values.at(
Index ); }
private:
std::vector<double> values;
};

Pourquoi ne pas garder ta première solution et utiliser un vector<Triplet*>
au lieu de vector<Triplet> ?

-- Nicolas Repiquet



Back to top
Loïc Joly
Guest





PostPosted: Wed Jun 16, 2004 6:13 pm    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote



Aurélien REGAT-BARREL wrote:

Quote:
Bonjour,
Ca parrait long, mais c'est vite lu :-)

J'ai un certain nombre de classes ABC, DEF, GHI, ... qui ont la
particularité d'avoir une certain nombre (presque tout le temps 3) de
"valeurs" toutes de type double. En fait, la seule chose qui différencies
ces classes est le nom de ces valeurs :

Je propose une autre solution, qui évite de faire de l'héritage :

Quote:

class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }

Ici, tu ajoutes :
double getVal(int i) const;

Quote:
private:
double a;
double b;
double c;
};

Et ensuite, tu fait :

template<class Triplet> dessine(Triplet const &t)
{
cout << t.getVal(1);
}

ABC abc;
dessine(abc);

--
Loïc

Back to top
Aurélien Regat-Barrel
Guest





PostPosted: Wed Jun 16, 2004 11:49 pm    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Quote:
Pourquoi ne pas garder ta première solution et utiliser un
vector<Triplet*
au lieu de vector

Merci pour ton aide.
C'est le delete qui m'embête... C'est assez contraignant de devoir appeler
delete sur chaque élément d'un vector reçu en paramètre :
L'idéal serait :

// graphique
void Graph::AddCurve( const std::vector<Triplet> & Values )
{
this->curves.add( Values );
// avec Triplet*, il faudrait parcourir Values et faire des delete...
Sad Sad Sad
}

// autre part
void DrawSelectedTriplets()
{
// liste des triplets à dessiner
std::vector<Triplet> values;

// pour chaque triplet <t>
// si sélectionné
values.push_back( t );
// avec Triplet *, il faudrait allouer un nouveau Triplet copie
de <t> Sad Sad :-(

// dessiner
this->graph->AddCurve( values );
this->graph->DrawCurves();
}

J'ai oublié de préciser que mes Triplets sont souvents temporaires, c.a.d
créés juste avant de les dessiner (dans DrawSelectedTriplets).
Disons que j'ai bien les triplets ABC qui sont persistents, mais on peut
vouloir dessiner CDE, FGH ou XXX qui sont générés à partir de ABC...


--
Aurélien REGAT-BARREL



Back to top
Aurélien Regat-Barrel
Guest





PostPosted: Wed Jun 16, 2004 11:55 pm    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Quote:
Je propose une autre solution, qui évite de faire de l'héritage :


class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }

Ici, tu ajoutes :
double getVal(int i) const;

private:
double a;
double b;
double c;
};

Et ensuite, tu fait :

template<class Triplet> dessine(Triplet const &t)
{
cout << t.getVal(1);
}

ABC abc;
dessine(abc);

Bonjour,
Merci pour ron aide.
Je vois à peu près l'idée, mais :
- je voudrais créer un std::vector de Triplet
- la fonction dessine (ou plutot AddTriplets en vue de les dessiner) est une
fonction membre d'une autre classe "Graph". Comment insérer cette fonction
template dans cette classe qui n'est pas template (et ne peut pas l'être car
dérivant d'une classe d'une lib GUI) ?

--
Aurélien REGAT-BARREL



Back to top
Loïc Joly
Guest





PostPosted: Thu Jun 17, 2004 5:09 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Aurélien Regat-Barrel wrote:

Quote:
Pourquoi ne pas garder ta première solution et utiliser un

vector<Triplet*

au lieu de vector

Merci pour ton aide.
C'est le delete qui m'embête... C'est assez contraignant de devoir appeler
delete sur chaque élément d'un vector reçu en paramètre :

Dans ce cas, soit ton vecteur ne possède pas les triplets, et pas besoin
de faire les delete, soit il les possède (soit de façon partagée ou
suite à une copie en profondeur), et un vecteur de pointeurs
intelligents (par exemple vector<boost::shared_ptr pour de la
propriété partagée, à condition qu'il y ait des
boost::shared_ptr<Triplet> partout dans ton code) peut convenir.

--
Loïc

Back to top
Aurélien REGAT-BARREL
Guest





PostPosted: Thu Jun 17, 2004 8:32 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Quote:
Dans ce cas, soit ton vecteur ne possède pas les triplets, et pas besoin
de faire les delete, soit il les possède (soit de façon partagée ou
suite à une copie en profondeur), et un vecteur de pointeurs
intelligents (par exemple vector<boost::shared_ptr pour de la
propriété partagée, à condition qu'il y ait des
boost::shared_ptr<Triplet> partout dans ton code) peut convenir.

J'y ai pensé (à std::auto_ptr).
Mais les pointeurs pour ça ça me gêne beaucoup.
Je viens de penser à aure chose. J'aime bien cette idée d'interface via la
classe abstraite. Probleme : on ne peut pas faire de std::vector de classes
abstraites. Alternative :

class Triplet
{
public:
virtual std::vector<double> GetArray() const = 0;
}:

class ABC : public Triplet
{
// ...
public:
std::vector<double> GetArray() const
{
std::vector<double> array;
array.push_back( this->A() );
array.push_back( this->B() );
array.push_back( this->C() );
return array;
}
};

le code de dessin devient :

void Graph::AddCurve( const std::vector<double> & Values );

void DrawSelectedTriplets()
{
// liste des triplets à dessiner
std::vector<double> values; // nouveauté double à la place de
Triplet

// pour chaque triplet <t>
// si sélectionné
values.push_back( t.GetArray() ); // nouveauté :
t.GetArray()

// dessiner
this->graph->AddCurve( values );
this->graph->DrawCurves();
}

Qu'en pensez-vous ?
Ca me parrait meilleur que ma dernière idée de classe de base Triplet qui
embarque un std::vector que ses dérivées (ABC, ...) utilisent pour stocker
leur A, B et C...

--
Aurélien REGAT-BARREL



Back to top
Loïc Joly
Guest





PostPosted: Thu Jun 17, 2004 6:14 pm    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Aurélien REGAT-BARREL wrote:
Quote:
Dans ce cas, soit ton vecteur ne possède pas les triplets, et pas besoin
de faire les delete, soit il les possède (soit de façon partagée ou
suite à une copie en profondeur), et un vecteur de pointeurs
intelligents (par exemple vector<boost::shared_ptr pour de la
propriété partagée, à condition qu'il y ait des
boost::shared_ptr<Triplet> partout dans ton code) peut convenir.


J'y ai pensé (à std::auto_ptr).

Tu ne peux pas faire de vector<auto_ptr, de plus la sémentique
d'auto_ptr est tellement étrange que je déconseille son utilisation.


Quote:
Mais les pointeurs pour ça ça me gêne beaucoup.

Si tu n'expliques pas en quoi ça te gêne, on va a voir du mal à trouver
une solution qui te plaise.

--
Loïc


Back to top
Aurélien REGAT-BARREL
Guest





PostPosted: Fri Jun 18, 2004 9:48 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Quote:
Mais les pointeurs pour ça ça me gêne beaucoup.

Si tu n'expliques pas en quoi ça te gêne, on va a voir du mal à trouver
une solution qui te plaise.

2 points :
- J'essaye d'utiliser au minimum les pointeurs. Il n'y en pas pratiquement
pas dans tout mon code. Faut allouer, désallouer, vérifier qu'il est pas
null, checker l'allocation,... Jusque là je me suis très bien débrouillé
sans. Mais j'ai peut être atteind les limites du "non pointeur" et il serait
peut être plus élégant d'utiliser des interfaces (ABC) via des pointeurs
intelligents que d'utiliser des classes de base faussement abstraites via un
constrcuteur protected.
- Utiliser des pointeurs (intelligents ou non) va me faire modifier pas mal
de code.

Mais je garde l'idée sous la main, au moins pour le prochain problème du
genre.
Merci.

--
Aurélien REGAT-BARREL



Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Mon Jun 21, 2004 8:26 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

"Aurélien REGAT-BARREL" <nospam-aregatba (AT) yahoo (DOT) fr.invalid> wrote in
message news:<40d039f6$0$32165$626a14ce (AT) news (DOT) free.fr>...

Quote:
Ca parrait long, mais c'est vite lu :-)

J'ai un certain nombre de classes ABC, DEF, GHI, ... qui ont la
particularité d'avoir une certain nombre (presque tout le temps 3) de
"valeurs" toutes de type double. En fait, la seule chose qui
différencies ces classes est le nom de ces valeurs :

class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }
private:
double a;
double b;
double c;
};

class DEF
{
public:
double D() const { return this->d; }
double E() const { return this->e; }
double F() const { return this->f; }
private:
double d;
double e;
double f;
};

etc...

C'est utile de les différencier, car ces valeurs n'ont pas la même
signification, et le typage est nécessaire. Jusque là tout va bien,
tout est calculé correctement.

Probleme :

Je veux dessiner ces valeurs dans un graphe, 1 valeur au choix sur
chaque axe. Par exemple, dessiner A() et B() sur mon graphe. Je
voudrais donc faire un truc générique, et pas XX fonctions pour chaque
type. J'ai voulu faire hériter toutes ces classes d'une même classe de
base : Triplet.

class Triplet
{
public:
virtual double Value( int Index ) const = 0;
};

Ainsi je file mon ABC ou DEF ou XYZ à mon graphe et je lui dit
"dessine moi la valeur 1 et 2 et il fait Value( 1 ) et Value ( 2 )
pour les récupérer.

class ABC : public Triplet
{
// idem que plus haut

public:
double Value( int Index )
{
switch ( Index )
{
case 0 : return this->A();
case 1 : return this->B();
case 2 : return this->C();
}
// exception
}
};

Mais Triplet est abstraite, et je ne peux pas contruire un
std::vector<Triplet> à passer pour dessiner.
Je compte du coup re-écrire toutes les classes dans ce style :

class Triplet
{
public:
virtual double Value( int Index ) const { return this->values.at(
Index ); }
private:
std::vector<double> values;
};

class ABC : public Triplet
{
public:
double A() const { return this->values[ 0 ]; }
double B() const { return this->values[ 1 ]; }
double C() const { return this->values[ 2 ]; }
};

Y'a-t-il une meilleure solution ?

Ça ne me semble pas mauvais.

Ceci dit, j'ai lu la reste de la discussion, où tu dis de ne pas aimer
les pointeurs. Or, en C++, qu'on les aime ou non, le polymorphisme
implique l'utilisation des pointeurs. Et un certain nombre d'autres
choses. Si tes classes ABC et DEF sont réelement des valeurs, avec une
sémantique de valeur, c'est peut-être que le polymorphisme, même limité,
ne leur convient pas.

En plus de la solution template suggérer par Loïc, tu pourrais penser à
une conversion implicite -- tu définis une class Triplet uniquement pour
l'affichage, puis dans ABC, DEF, etc., tu définis un opérateur de
conversion :

operator Triplet() {
return ...
}

où tu construis un Triplet des valeurs dans la classe dérivée.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Back to top
Aurélien REGAT-BARREL
Guest





PostPosted: Mon Jun 21, 2004 8:50 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Quote:
Ça ne me semble pas mauvais.

Ceci dit, j'ai lu la reste de la discussion, où tu dis de ne pas aimer
les pointeurs. Or, en C++, qu'on les aime ou non, le polymorphisme
implique l'utilisation des pointeurs. Et un certain nombre d'autres
choses.

Peux-tu détailler un petit peu. Pourquoi cela implique-t-il les pointeurs ?

Quote:
Si tes classes ABC et DEF sont réelement des valeurs, avec une
sémantique de valeur, c'est peut-être que le polymorphisme, même limité,
ne leur convient pas.

Eh bien, si l'on considère que le polymorphisme est à utiliser lors d'une
relation "est un", je pense qu'il est adapté ici.
ABC, DEF, ... sont tous des Triplet. La seule chose qui les différencie,
c'est le contexte de leurs valeurs.
ABC va être les coordonnées d'un point en mètres, DEF en yards, GHI en
kilomètres, etc...
Il est donc important de les différencier pour les manipuler.
Mais pour les afficher, j'ai un graphe qui se calibre automatiquement en
fonction du min et du max.
La fonction qui lui donne les Triplets à afficher se charge de mettre la
légende à jour. En fait, mon graphe est un bête graphe générique qui ne fait
que dessiner des Triplet. Passer par Triplet me permet de ne pas devoir le
spécialiser pour chaque nouveau type de Triplet, réduction du couplage, ...

Quote:
En plus de la solution template suggérer par Loïc, tu pourrais penser à
une conversion implicite -- tu définis une class Triplet uniquement pour
l'affichage, puis dans ABC, DEF, etc., tu définis un opérateur de
conversion :

operator Triplet() {
return ...
}

où tu construis un Triplet des valeurs dans la classe dérivée.

Intéressant... Je garde ça sous le coude.
Merci pour ta réponse.

--
Aurélien REGAT-BARREL



Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Tue Jun 22, 2004 9:02 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

"Aurélien REGAT-BARREL" <nospam-aregatba (AT) yahoo (DOT) fr.invalid> wrote in
message news:<40d6a045$0$3992$626a14ce (AT) news (DOT) free.fr>...
Quote:
Ça ne me semble pas mauvais.

Ceci dit, j'ai lu la reste de la discussion, où tu dis de ne pas
aimer les pointeurs. Or, en C++, qu'on les aime ou non, le
polymorphisme implique l'utilisation des pointeurs. Et un certain
nombre d'autres choses.

Peux-tu détailler un petit peu. Pourquoi cela implique-t-il les
pointeurs ?

Parce qu'en C++, un appel n'est en fait polymorphe que lorsqu'il passe
par un pointeur ou par une référence. Le type d'un objet connu du
compilateur est fixé lors de la compilation ; tant que tu n'as pas de
pointeur ni de référence (qui agit un peu comme un pointeur à cet
égard), le type que voit le compilateur, c'est le type réel de l'objet.

C'est la base de ton problème, d'ailleurs. Si tu fais un
std::vector<Triplet>, le type que voit le compilateur, c'est Triplet. Et
puisqu'il n'y a pas de pointeur ni de référence, le type réel dynamique
est aussi Triplet.

Le C++ n'a pas rénouncé ses origines de C au point que tout soit en fait
un pointeur. Il supporte aussi très bien des types « valeur », qu'on
copie, affecte, etc. Sans pointeur ni référence. Mais dans la pratique,
il faut choisir : ou bien, on a un objet polymorphique, qui est la
plupart du temps alloué dynamiquement, qu'on manipule à travers des
pointeurs et des références, et qu'on ne copie ni affecte pas ; ou bien,
on a un objet à sémantique de valeur, qu'on alloue la plupart du temps
statiquement ou sur la pile, qu'on copie et qu'on affecte à droit et à
gauche, mais qui n'est pas polymorphique.

Et évidemment, ce que je viens de dire, c'est très schématique, il y a
bien d'exceptions, et dans la pratique, il y a aussi des exceptions.
Mais c'est un bon point du départ.

Quote:
Si tes classes ABC et DEF sont réelement des valeurs, avec une
sémantique de valeur, c'est peut-être que le polymorphisme, même
limité, ne leur convient pas.

Eh bien, si l'on considère que le polymorphisme est à utiliser lors
d'une relation "est un", je pense qu'il est adapté ici.

De point de vue de la conception, je crois bien aussi. Mais il faut voir
d'après ta conception générale si ça passe, et quel en sont les
implications. Est-ce que ABC et DEF sont conceptuellement des Triplets,
ou est-ce que c'est une caractèristique « accidentelle » ? Ou plus
pragmatique, est-ce qu'elles sont deux variations du même type
fondamental, et qu'en général, tu ne t'intéresses pas à leur type
précise ?

Parce que le « est un », ce n'est qu'une face de la médaille. Pour
revenir à ce que j'ai dit avant, on peut (très schématiquement) diviser
les objets en deux catégories : les types à sémantique de valeur, et les
types « entité ».

Les types à sémantique de valeur se comporte à peu près comme des types
de base, tu les copies, les affectes, etc. ; surtout, une copie en vaut
une autre. En C++, il est habituel de déclarer ces types comme variable
locale (sans pointeur ni référence) ; dans le Java bien écrit, ils sont
immutable et final (comme java.langString) ; en C++, personnellement, et
avec un certain nombre d'exceptions, j'ai une tendance aussi à n'avoir
que les opérateurs d'affectation (y compris du genre +=, *=, etc., le
cas échéant) comme fonctions non const. Du point de vu de la conception,
ce sont des objets sans comportement (on les manipule classiquement avec
des fonctions globales, voire même des opérateurs comme +, -), mais dont
toute la signifiance est l'état.

Les types « entité », ce sont des types de modèlisation ; ils ont un
comportement et une identité. On ne les copie pas (avec quelques
exceptions, par exemple pour supporter une fonctionalité de rollback ou
de undo), et on ne les affecte pas, parce qu'une copie n'est pas la même
chose. Du coup, on les manipule surtout à travers des pointeurs ou des
références.

L'exemple type de la différence : Compte et Montant. Qu'on vire 100
Euros sur ton compte en banque, ça t'est parfaitement égal combien de
fois ils ont copié l'objet qui représente les 100 Euros, du moment que
le montant (sa valeur) soit le même. En revanche, lors du virement, tu
préfèrerais sans doute que ça se fait sur ton compte, et non sur une
copie temporaire qui cessera d'exister lorsqu'on quitte la fonction. (Et
j'insiste, tout ça, c'est très schématique. C'est juste pour donner une
idée, et non pour servir de règle absolue.)

Vue que le polymorphisme joue surtout sur le comportement, c'est un peu
logique que ce soit des objets « entité », avec identité, qui soit
polymorphique. Et dans les faits, en C++, du fait qu'on a de vraies
valeurs, avec un typage statique, la polymorphisme se combine très mal
avec les types de valeur. Si je déclare un objet localement Triplet,
e.g.
Triplet t ;
Il a type Triplet. Toujours et sans exception. L'affection ou
l'initialisation à partir d'un autre objet se fait par copie. Et si cet
autre objet n'a pas le type Triplet, même si c'est un type dérivé, on le
convertit en Triplet. Le type d'un objet ne peut jamais changer. Jamais.

Même avec des pointeurs ou des références, le type d'un objet ne peut
jamais changer. Mettons que j'ai un pointeur à un Triplet, qui pointe en
fait un ABC. Je peux changer la valeur du pointeur, pour qu'il pointe à
un DEF. Mais l'objet qui était un ABC reste un ABC. Considérons le
suivant:
Triplet* a = new ABC ;
Triplet* b = new DEF ;
*a = *b ;
Qu'est-ce que cette dernière ligne doit signifier ? Il va bien appeler
la fonction Triplet::operator=( Triplet const& ). Fonction qui peut être
virtuelle. Mais qu'est-ce que cette fonction doit faire ? Quoiqu'elle
fasse, l'objet ne peut pas changer de type. Il reste un ABC.

C'est pour ça que je dis que le polymorphisme se combine mal avec les
objets de valeur.

Il existe des solutions : Coplien en développe plusieurs, dont l'idiome
lettre/envellope que j'utilise personnellement chaque fois que j'ai
réelement besoin que un objet soit à la fois polymorphique et qu'il
supporte l'affectation.

Quote:
ABC, DEF, ... sont tous des Triplet. La seule chose qui les différencie,
c'est le contexte de leurs valeurs.

Toutes les opérations sur eux sont identique ?

Quote:
ABC va être les coordonnées d'un point en mètres, DEF en yards, GHI en
kilomètres, etc...

D'accord. J'ai compris.

Est-ce que les valeurs dans le Triplet sont toujours dans la même unité
? C-à-d : quand je crée une Cordonnée en yards, est-ce que constructeur
convertit les yards en mètres avant de les stocker, ou est-ce qu'il
stocke réelement des yards ?

Je pose la question, parce que je me démande ce qui passera is j'essaie
d'utiliser un tableau avec des types différents. Si les coordonnées sont
tous stockées dans la même unité, il ne doit pas y avoir de problème.
Sinon, si par exemple j'ai une Coordonnée mètres, une millimètres, et
une troisième en milles nautiques, il va falloir que je sache lequel est
lequel quand je vais me servir du tableau. Au moins que la classe
Triplet a une fonction virtuelle spéciale, getIndexInMeters, ou quelque
chose du genre. Mais alors, on tombe dans le problème valeur/
polymorphisme. Il existe des solutions, mais c'est beaucoup d'effort
pour éventuellement pas grand chose.

Si en revanche j'ai toujours tout en, disons, mètres, et que ce n'est
qu'au niveau de l'interface que ça change, tout va bien. La conversion
de CoordonnéeEnYards en CoordonnéeDeBase est triviale -- c'est ce qu'on
appelle du slicing, mais dans ce cas-ci, le slicing nous donne
exactement ce qu'on veut. En fait, la « valeur », c'est la
CoordonnéeDeBase. Les classes dérivées peuvent être considérer
simplement comme des interfaces supplémentaires de commodité ;
éventuellement, tout ce qu'elles offriront de plus, c'est des
constructeurs. Mais même si elles en offrent plus, la conversion, c'est
toujours assez simple. Même la copie et l'affectation peuvent marcher
sans problèmes, à condition que dans chaque classe, tu fournis non
seulement l'opérateur d'affectation classique (dont la version générée
par le compilateur convient), mais aussi un opérateur d'affectation :

CoordonnéeEnYards::operator=( CoordonnéeDeBase const& ) ;

(La même chose pour les constructeurs, évidemment.)

C'est un cas particulier où la polymorphisme peut se combiner avec une
sémantique de valeur, à condition de bien comprendre que l'affectation
ne change pas le type ; si tu affectes une CoordonnéeEnKilomètres à une
CoordonnéeEnYards, le CoordonnéeEnYards reste en yards. Et que si tu
fais getIndex( 0 ) sur le CoordonnéeEnKilomètres originale, elle va
renvoyer une valeur bien différente que si tu fais getIndex( 0 ) sur le
CoordonnéeEnYards qui a été affecter. Si
CoordonnéeEnKilomètres::getIndex(0) renvoie 1.0,
CoordonnéeEnYards::getIndex(0) doit renvoyer 1093.611111, pour la même
valeur dans CoordonnéeDeBase (probablement 1000.).

Quote:
Il est donc important de les différencier pour les manipuler.

Mais pour les afficher, j'ai un graphe qui se calibre automatiquement
en fonction du min et du max.

La question reste : qu'est-ce qui se passe si les types sont mélangés ?
Parce que si tu utilises le polymorphisme classique, on peut les
mélanger, et même s'ils ne sont jamais mélangés aujourd'hui, ils le
seront un jour ou l'autre.

Quote:
La fonction qui lui donne les Triplets à afficher se charge de mettre
la légende à jour. En fait, mon graphe est un bête graphe générique
qui ne fait que dessiner des Triplet. Passer par Triplet me permet de
ne pas devoir le spécialiser pour chaque nouveau type de Triplet,
réduction du couplage, ...

Tout à fait d'accord. Mais je lui donnerai toujours des triplets dans
une unité préspécifiée.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Back to top
Aurélien REGAT-BARREL
Guest





PostPosted: Tue Jun 22, 2004 4:04 pm    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Quote:
Peux-tu détailler un petit peu. Pourquoi cela implique-t-il les
pointeurs ?
[snip]


Je vais méditer sur tes brillantes explications. Merci pour ces précisions.

Quote:
ABC, DEF, ... sont tous des Triplet. La seule chose qui les différencie,
c'est le contexte de leurs valeurs.

Toutes les opérations sur eux sont identique ?

La seule opération faite sur Triplet est l'affichage.

Quote:
ABC va être les coordonnées d'un point en mètres, DEF en yards, GHI en
kilomètres, etc...

D'accord. J'ai compris.

Est-ce que les valeurs dans le Triplet sont toujours dans la même unité
? C-à-d : quand je crée une Cordonnée en yards, est-ce que constructeur
convertit les yards en mètres avant de les stocker, ou est-ce qu'il
stocke réelement des yards ?

Non. Pire, mon exemple est mauvais. Il faut plutot voir des mètres, puis des
degrés (angles), etc...
Le point commun est que ces triplets sont le reflet de certaines propriétés,
mais dans des unités qui n'ont rien à voir.
Les Triplet ne sont absolument pas comparables entre eux, les unités ne sont
pas convertibles (distances, angles, pourcentages, ...).

Quote:
La question reste : qu'est-ce qui se passe si les types sont mélangés ?
Parce que si tu utilises le polymorphisme classique, on peut les
mélanger, et même s'ils ne sont jamais mélangés aujourd'hui, ils le
seront un jour ou l'autre.

C'est incohérent, ça n'a pas de sens, c'est à éviter. Je réalise que la
relation "est un" est juste en théorie, mais fausse en pratique.
Mes Triplets sont en fait des coordonnées 3D dans différents repères. Donc
ABC, DEF, ... sont bien des Triplets dans le sens ou ils ont 3 valeurs. Mais
c'est juste ça le point commun : ils ont 3 valeurs de même type (double).
Ils ont 3 valeurs dont on ne sait rien à ce niveau. On n'a pas besoin de
plus d'info vu qu'on veut juste les afficher.
Je veux juste une interface de manipulation commune. Le mot interface est
lâché...

Quote:
La fonction qui lui donne les Triplets à afficher se charge de mettre
la légende à jour. En fait, mon graphe est un bête graphe générique
qui ne fait que dessiner des Triplet. Passer par Triplet me permet de
ne pas devoir le spécialiser pour chaque nouveau type de Triplet,
réduction du couplage, ...

Tout à fait d'accord. Mais je lui donnerai toujours des triplets dans
une unité préspécifiée.

Suite à ces très riches explications (merci beaucoup!), je ressors ton idée
d'un opérateur de convertion que je reyclerais volontier en interface. J'ai
un peu avancé du côté du graphique, et j'ai maintenant un objet Courbe.
Cette Courbe contient la liste des Triplets à dessiner, plus leur nom,
etc...
Le truc c'est donc de pourvoir créer une courbe Curve à partir d'une liste
de ABC ou une liste de DEF, etc...
Que penses-tu d'une interface implémentées par ABC, DEF, ... de ce style :

class TripletBase
{
public:
virtual ~TripletBase();
virtual std::vector<double> GetValues() const = 0;
};

Et la classe Curve :

class Curve
{
public:
void AddPoint( const std::vector<double> & V )
{
this->Points.push_back( V );
}
private:
std::vector< std::vector Points;
};

Le graphe est encore plus générique, il n'a plus aucun lien avec les
Triplets.
Pour chaque point <p> ABC ou DEF ou ... à voir, on l'ajoute à <c> via
c.Addpoint( p.GetValues() );

L'interface ne sert pas à grand chose, si ce n'est à faire penser qu'il faut
créer une fonction GetValues pour chaque type de Triplet... (est-ce justifié
?)
Merci beaucoup.

--
Aurélien REGAT-BARREL



Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Wed Jun 23, 2004 8:21 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

"Aurélien REGAT-BARREL" <nospam-aregatba (AT) yahoo (DOT) fr.invalid> wrote in
message news:<40d857a7$0$10168$636a15ce (AT) news (DOT) free.fr>...

Quote:
ABC, DEF, ... sont tous des Triplet. La seule chose qui les
différencie, c'est le contexte de leurs valeurs.

Toutes les opérations sur eux sont identique ?

La seule opération faite sur Triplet est l'affichage.

Alors, *conceptuellement*, je verais une interface (c-à-d une classe
abstraite sans état) Triplet, dont les autres classes héritent.

Aussi, si la fonctionnalité est réelement limitée à l'affichage, je
l'indiquerais dans le nom : TripletAffichable, ou quelque chose du
genre. Voire même plus -- ce qui caractèrise cette classe de base, c'est
bien l'« Affichable » ; si j'ai bien compris, elle n'a aucune autre
fonctionnalité d'un Triplet. (Mais un bon nom qui dit ça ne me vient pas
à l'esprit.) Éventuellement, on pourrait même imaginer une interface
plus abstraite : une SuiteAffichable, avec une fonction pour déterminer
le nombre d'éléments, et une autre pour accéder à chaque élément.

Ça, c'est conceptuellement, et ne tient pas compte des contraites de
l'implémentation, comme par exemple des problèmes éventuels liés à un
mélange entre le polymorphisme et une sémantique de valeur. Encore que
c'est fort possible que tu tombes dans un cas particulier où le problème
ne se pose pas. Si j'ai bien compris, cette interface ne servira que
pour l'affichage -- partout ailleurs dans le programme, il ne s'agit que
des ABC ou des DEF, jamais des Triplet. Or, dans la mésure que 1)
l'affichage n'influe en rien à la durée de vie des objets, et 2) on ne
manipule pas les objets par l'interface Triplet ailleurs que dans
l'affichage, on pourrait bien hériter d'une interface Triplet, et passer
un std::vector< Triplet* > à l'affichage. Ce genre de mélange ne me
plaît pas en général, mais il y a des cas particuliers où il se
justifie, où il est plus propre que les alternatifs.

(N'empêche que je considèrerais de très près l'opérateur de conversion.
En fin de compte, un ABC n'est pas un Triplet -- il a un Triplet qui
sert à l'affichage. Et ce qu'on affiche, c'est un Triplet. Point à la
ligne. Ce n'est pas une implémentation quelconque d'un Triplet. Enfin,
c'est un point de vue. Le problème, c'est qu'on peut régarder prèsque
tout de beaucoup de façons différentes. AMHA, les deux solutions
marchent, et avec le peu d'information que j'ai, l'une ou l'autre est
« assez bien ».)

Quote:
ABC va être les coordonnées d'un point en mètres, DEF en yards,
GHI en kilomètres, etc...

D'accord. J'ai compris.

Est-ce que les valeurs dans le Triplet sont toujours dans la même
unité ? C-à-d : quand je crée une Cordonnée en yards, est-ce que
constructeur convertit les yards en mètres avant de les stocker, ou
est-ce qu'il stocke réelement des yards ?

Non. Pire, mon exemple est mauvais. Il faut plutot voir des mètres,
puis des degrés (angles), etc...

D'accord.

Je m'en doutais un peu. Je me posais cependant la question si on aurait
pu les unifier. Parce que quand on peut, ça peut simplifier la vie.

Mais vue de cet angle, est-ce que l'héritage a un sens ? Un Triplet,
c'est trois valeurs abstraites. Or, si j'ai bien compris, un ABC ne se
compose pas de trois valeurs abstraites, mais de trois distances, tandis
qu'un DEF se consiste en trois angles.

À mon avis, ça argue pour la solution avec ou bien un opérateur de
conversion, ou bien une fonction explicite : Triplet ABC::getValues(),
ou quelque chose du genre.

Quote:
Le point commun est que ces triplets sont le reflet de certaines
propriétés, mais dans des unités qui n'ont rien à voir. Les Triplet ne
sont absolument pas comparables entre eux, les unités ne sont pas
convertibles (distances, angles, pourcentages, ...).

D'accord. Donc, les ABC et les DEF n'ont rien en commun, à part la
caractèristique prèsqu'accidentelle qu'ils ont trois quelque chose qui
se représente par une valeur numérique.

Quote:
La question reste : qu'est-ce qui se passe si les types sont
mélangés ? Parce que si tu utilises le polymorphisme classique, on
peut les mélanger, et même s'ils ne sont jamais mélangés
aujourd'hui, ils le seront un jour ou l'autre.

C'est incohérent, ça n'a pas de sens, c'est à éviter. Je réalise que
la relation "est un" est juste en théorie, mais fausse en pratique.
Mes Triplets sont en fait des coordonnées 3D dans différents repères.
Donc ABC, DEF, ... sont bien des Triplets dans le sens ou ils ont 3
valeurs. Mais c'est juste ça le point commun : ils ont 3 valeurs de
même type (double). Ils ont 3 valeurs dont on ne sait rien à ce
niveau. On n'a pas besoin de plus d'info vu qu'on veut juste les
afficher. Je veux juste une interface de manipulation commune. Le mot
interface est lâché...

Dans ce cas, je pencherais pour une fonction explicite qui retourne un
Triplet. Avec éventuellement une fonction templatée pour construire le
tableau des Triplet, quelque chose du genre :

template< typename FwdIter >
std::vector< Triplet >
prepareLAffichage( FwdIter begin, FwdIter end )
{
std::vector< Triplet > result ;
while ( begin != end ) {
result.push_back( begin->getTriplet() ) ;
++ begin ;
}
return result ;
}

Note bien l'absence de toute classe de base des ABC, DEF, etc. C'est à
mon avis important, parce qu'on ne peut pas substituer un ABC pour un
DEF, même si on peut faire les même choses avec les deux. Or, le
template, c'est idéal pour enforcer les prédicats de type (c'est une
idée que je dois à Matt Austern) -- ici, l'utilisation du template
s'assure que tous les Triplets dans le tableau provient du même type.

Quote:
La fonction qui lui donne les Triplets à afficher se charge de
mettre la légende à jour. En fait, mon graphe est un bête graphe
générique qui ne fait que dessiner des Triplet. Passer par Triplet
me permet de ne pas devoir le spécialiser pour chaque nouveau type
de Triplet, réduction du couplage, ...

Tout à fait d'accord. Mais je lui donnerai toujours des triplets
dans une unité préspécifiée.

Suite à ces très riches explications (merci beaucoup!), je ressors ton
idée d'un opérateur de convertion que je reyclerais volontier en
interface. J'ai un peu avancé du côté du graphique, et j'ai maintenant
un objet Courbe. Cette Courbe contient la liste des Triplets à
dessiner, plus leur nom, etc...

Le truc c'est donc de pourvoir créer une courbe Curve à partir d'une
liste de ABC ou une liste de DEF, etc... Que penses-tu d'une interface
implémentées par ABC, DEF, ... de ce style :

class TripletBase
{
public:
virtual ~TripletBase();
virtual std::vector<double> GetValues() const = 0;
};

Et la classe Curve :

class Curve
{
public:
void AddPoint( const std::vector<double> & V )
{
this->Points.push_back( V );
}
private:
std::vector< std::vector Points;
};

Le graphe est encore plus générique, il n'a plus aucun lien avec les
Triplets.
Pour chaque point <p> ABC ou DEF ou ... à voir, on l'ajoute à <c> via
c.Addpoint( p.GetValues() );

L'interface ne sert pas à grand chose, si ce n'est à faire penser
qu'il faut créer une fonction GetValues pour chaque type de Triplet...
(est-ce justifié ?)

Comme j'ai dit ci-dessus, d'après tes descriptions, j'ai l'impression
ici que c'est plutôt un rôle pour les templates. On ne peut pas
substituer un Triplet pour un autre librement. On a un prédicat sur le
typage, que tous les éléments soient en fait le même type. Enforcer les
prédicats du typage, c'est un rôle où les templates brillent. Or que le
but de l'héritage, en général, c'est justement de se libérer des
contraites du typage.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Back to top
Aurélien REGAT-BARREL
Guest





PostPosted: Wed Jun 23, 2004 9:22 am    Post subject: Re: Aide pour realiser une classe de manipualtion de donnees Reply with quote

Quote:
La seule opération faite sur Triplet est l'affichage.

Alors, *conceptuellement*, je verais une interface (c-à-d une classe
abstraite sans état) Triplet, dont les autres classes héritent.

Aussi, si la fonctionnalité est réelement limitée à l'affichage, je
l'indiquerais dans le nom : TripletAffichable, ou quelque chose du
genre. Voire même plus -- ce qui caractèrise cette classe de base, c'est
bien l'« Affichable » ; si j'ai bien compris, elle n'a aucune autre
fonctionnalité d'un Triplet. (Mais un bon nom qui dit ça ne me vient pas
à l'esprit.) Éventuellement, on pourrait même imaginer une interface
plus abstraite : une SuiteAffichable, avec une fonction pour déterminer
le nombre d'éléments, et une autre pour accéder à chaque élément.

C'est ce que je voulais faire, une interface IPlottable ou... Moi aussi je
n'ai pas trouvé de nom :-)

Quote:
Ça, c'est conceptuellement, et ne tient pas compte des contraites de
l'implémentation, comme par exemple des problèmes éventuels liés à un
mélange entre le polymorphisme et une sémantique de valeur. Encore que
c'est fort possible que tu tombes dans un cas particulier où le problème
ne se pose pas. Si j'ai bien compris, cette interface ne servira que
pour l'affichage -- partout ailleurs dans le programme, il ne s'agit que
des ABC ou des DEF, jamais des Triplet. Or, dans la mésure que 1)
l'affichage n'influe en rien à la durée de vie des objets, et 2) on ne
manipule pas les objets par l'interface Triplet ailleurs que dans
l'affichage, on pourrait bien hériter d'une interface Triplet, et passer
un std::vector< Triplet* > à l'affichage. Ce genre de mélange ne me
plaît pas en général, mais il y a des cas particuliers où il se
justifie, où il est plus propre que les alternatifs.

C'était mon souhait initial, jusqu'à ce que tu interviennes.

Quote:
(N'empêche que je considèrerais de très près l'opérateur de conversion.
En fin de compte, un ABC n'est pas un Triplet -- il a un Triplet qui
sert à l'affichage. Et ce qu'on affiche, c'est un Triplet. Point à la
ligne. Ce n'est pas une implémentation quelconque d'un Triplet. Enfin,
c'est un point de vue. Le problème, c'est qu'on peut régarder prèsque
tout de beaucoup de façons différentes. AMHA, les deux solutions
marchent, et avec le peu d'information que j'ai, l'une ou l'autre est
« assez bien ».)

L'opérateur de conversion me semble maintenant meilleur. Un seul bémol :
comment faire penser à un programmeur futur qui reprend le projet qu'il doit
coder cet opérateur. D'où mon idée de conserver une interface héritée
commune, pour souligner cette info.

Quote:
Est-ce que les valeurs dans le Triplet sont toujours dans la même
unité ? C-à-d : quand je crée une Cordonnée en yards, est-ce que
constructeur convertit les yards en mètres avant de les stocker, ou
est-ce qu'il stocke réelement des yards ?

Non. Pire, mon exemple est mauvais. Il faut plutot voir des mètres,
puis des degrés (angles), etc...

D'accord.

Je m'en doutais un peu. Je me posais cependant la question si on aurait
pu les unifier. Parce que quand on peut, ça peut simplifier la vie.

Mais vue de cet angle, est-ce que l'héritage a un sens ? Un Triplet,
c'est trois valeurs abstraites. Or, si j'ai bien compris, un ABC ne se
compose pas de trois valeurs abstraites, mais de trois distances, tandis
qu'un DEF se consiste en trois angles.

Le seul point commun c'est qu'il y a un "Triplet" initial : ABC, et que les
suivants sont construits à partir de celui-ci.
Mais on parle bien de ABC, et pas de "Triplet".
Le but de "Triplet" est simplement de pouvoir écrire une seule fonction
Affiche( const Triplet & T );
à laquelle je peux passer un ABC, un DEF, ou un autre...

Quote:
À mon avis, ça argue pour la solution avec ou bien un opérateur de
conversion, ou bien une fonction explicite : Triplet ABC::getValues(),
ou quelque chose du genre.

Je suis aussi d'accord. Je pense maintenant à
Affiche( const std::vector<double> & V );
et soit un opérateur de conversion, soit une fonction GetValues(). Mais le
truc c'est que je veux une seule solution utilisée partout.
Pas de ABC::GetTriplet(), DEF::GetValues() et d'opérateur de conversion pour
HIJ. D'ou cette idée d'interface de base commune.

Quote:
Dans ce cas, je pencherais pour une fonction explicite qui retourne un
Triplet. Avec éventuellement une fonction templatée pour construire le
tableau des Triplet, quelque chose du genre :

template< typename FwdIter
std::vector< Triplet
prepareLAffichage( FwdIter begin, FwdIter end )
{
std::vector< Triplet > result ;
while ( begin != end ) {
result.push_back( begin->getTriplet() ) ;
++ begin ;
}
return result ;
}

Note bien l'absence de toute classe de base des ABC, DEF, etc. C'est à
mon avis important, parce qu'on ne peut pas substituer un ABC pour un
DEF, même si on peut faire les même choses avec les deux. Or, le
template, c'est idéal pour enforcer les prédicats de type (c'est une
idée que je dois à Matt Austern) -- ici, l'utilisation du template
s'assure que tous les Triplets dans le tableau provient du même type.

Je m'apprêtais à dire dans une précédente réponse que l'idéal serait
d'interdire le mixage dans un même tableau de ABC et de DEF. Mais ça ne me
parraissait pas possible, car même avec une classe de base Triplet, on
pouvait pas interdire le gars de pusher un ABD.GetValues() puis un
DEF.GetValues().
Cette solution de template est terrible. Je suis peu habitué aux templates,
je n'ai pas le réflexe de les utiliser. Pourtant j'adore la généricité et la
STL. L'idéal aurait été une fonction générique Affiche() dans mon graphe,
juste cette fonction. Mais ce n'est pas possible. Je n'avais en fait pas
pensé à créer uen fonction template que j'utilise à l'intérieur de
Affiche().
Là il est toujours possible de mixer des ABC et DEF (en construisant 2
vector et les mélangeant), mais faut vraiment le vouloir. Je ne pensais pas
pouvoir arriver à un tel niveau de typage. C'est très bien.

Quote:
L'interface ne sert pas à grand chose, si ce n'est à faire penser
qu'il faut créer une fonction GetValues pour chaque type de Triplet...
(est-ce justifié ?)

Comme j'ai dit ci-dessus, d'après tes descriptions, j'ai l'impression
ici que c'est plutôt un rôle pour les templates. On ne peut pas
substituer un Triplet pour un autre librement. On a un prédicat sur le
typage, que tous les éléments soient en fait le même type. Enforcer les
prédicats du typage, c'est un rôle où les templates brillent. Or que le
but de l'héritage, en général, c'est justement de se libérer des
contraites du typage.

Belle conclusion.
Un bémol demeure cependant, et je crois que c'est un défaut des templates
C++.
Comment indiquer qu'il faut coder une fonction getTriplet() ?

Je te remercie beaucoup pour tout ce temps consacré à ce problème. Je crois
qu'une solution élégante a été mise au point, bien plus élégante que mon
code initial en tous cas.
J'aimerais pouvoir te dire que je m'apprête à la coder, mais comme je dois
intégrer mon code dans du code existant et non plus l'inverse (code à base
de char * et de macros de 150 lignes chacune soit plus de 1000 lignes de
macros dans le but de fabriquer un std::string et un std::vector maisons) je
ne sais pas si je vais pouvoir débarquer avec mes classes à moi et mes
templates. C'est frustrant mais je ne suis que stagiaire (on me l'a bien
fait comprendre) et c'est pas moi qui décide... Snif.

--
Aurélien REGAT-BARREL



Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French) All times are GMT
Goto page 1, 2, 3, 4  Next
Page 1 of 4

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.