Ç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 ?
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, ...
Ç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 ?
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, ...
Ç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 ?
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, ...
Peux-tu détailler un petit peu. Pourquoi cela implique-t-il les
pointeurs ?
[snip]
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 ?
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 ?
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.
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.
Peux-tu détailler un petit peu. Pourquoi cela implique-t-il les
pointeurs ?
[snip]
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 ?
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 ?
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.
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.
Peux-tu détailler un petit peu. Pourquoi cela implique-t-il les
pointeurs ?
[snip]
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 ?
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 ?
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.
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.
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.
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, ...).
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é...
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<double> > 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é ?)
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.
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, ...).
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é...
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<double> > 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é ?)
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.
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, ...).
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é...
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<double> > 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é ?)
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 ».)
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.
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.
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.
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 ».)
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.
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.
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.
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 ».)
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.
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.
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.
Ç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.
(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.
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...
À 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.
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.
Ç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.
(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.
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...
À 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.
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.
Ç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.
(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.
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...
À 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.
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.
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...
Ce qui argue très fort pour un type à part. Est-ce qu'on peut exclure
d'office qu'il n'y aurait jamais de type qui s'affiche de deux façons :
ou comme un Triplet, ou d'une autre façon ?
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.
Bien venu au monde professionnel:-). En trente ans d'expérience, j'ai pu
implémenter un programme nouveau de rien deux fois. Sinon, c'était
toujours adapter quelque chose d'existant. Dans la réalité, au moins
dans l'industrie (je ne sais pas comment ça se passe dans les écoles),
on est toujours confronté des contraints contradictoires : il faut faire
beau, avec un existant, dans un delai pas toujours très réaliste. Il
faut toujours faire un choix : qu'est-ce qu'il faut faire, qu'est-ce que
je peux faire de plus du minimum, et qu'est-ce qu'il faut laisser du
côté, au moins pour l'instant -- la perfection n'est pas de ce monde.
Savoir faire ce choix, c'est souvent plus important que bien connaître
des templates ou la conception OO.
Mais ne laisse pas ce fait te dégouter de l'informatique, ni du monde
industriel ou commerciel. Il faut partir du principe que le programme
que tu laisses ne serait jamais parfait. Mais avec un peu d'effort, tu
peux faire en sort qu'il serait toujours mieux que quand tu l'as pris en
main. Ce qui est aussi une satisfaction. (Personnellement, je tire aussi
une très grande satisfaction du fait que certains programmes sur
lesquels j'ai travaillé sont réelement utilisés, de façon intense. Faire
un programme parfait, mais qui ne sert pas, ne me donnerait pas autant
de satisfaction.)
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...
Ce qui argue très fort pour un type à part. Est-ce qu'on peut exclure
d'office qu'il n'y aurait jamais de type qui s'affiche de deux façons :
ou comme un Triplet, ou d'une autre façon ?
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.
Bien venu au monde professionnel:-). En trente ans d'expérience, j'ai pu
implémenter un programme nouveau de rien deux fois. Sinon, c'était
toujours adapter quelque chose d'existant. Dans la réalité, au moins
dans l'industrie (je ne sais pas comment ça se passe dans les écoles),
on est toujours confronté des contraints contradictoires : il faut faire
beau, avec un existant, dans un delai pas toujours très réaliste. Il
faut toujours faire un choix : qu'est-ce qu'il faut faire, qu'est-ce que
je peux faire de plus du minimum, et qu'est-ce qu'il faut laisser du
côté, au moins pour l'instant -- la perfection n'est pas de ce monde.
Savoir faire ce choix, c'est souvent plus important que bien connaître
des templates ou la conception OO.
Mais ne laisse pas ce fait te dégouter de l'informatique, ni du monde
industriel ou commerciel. Il faut partir du principe que le programme
que tu laisses ne serait jamais parfait. Mais avec un peu d'effort, tu
peux faire en sort qu'il serait toujours mieux que quand tu l'as pris en
main. Ce qui est aussi une satisfaction. (Personnellement, je tire aussi
une très grande satisfaction du fait que certains programmes sur
lesquels j'ai travaillé sont réelement utilisés, de façon intense. Faire
un programme parfait, mais qui ne sert pas, ne me donnerait pas autant
de satisfaction.)
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...
Ce qui argue très fort pour un type à part. Est-ce qu'on peut exclure
d'office qu'il n'y aurait jamais de type qui s'affiche de deux façons :
ou comme un Triplet, ou d'une autre façon ?
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.
Bien venu au monde professionnel:-). En trente ans d'expérience, j'ai pu
implémenter un programme nouveau de rien deux fois. Sinon, c'était
toujours adapter quelque chose d'existant. Dans la réalité, au moins
dans l'industrie (je ne sais pas comment ça se passe dans les écoles),
on est toujours confronté des contraints contradictoires : il faut faire
beau, avec un existant, dans un delai pas toujours très réaliste. Il
faut toujours faire un choix : qu'est-ce qu'il faut faire, qu'est-ce que
je peux faire de plus du minimum, et qu'est-ce qu'il faut laisser du
côté, au moins pour l'instant -- la perfection n'est pas de ce monde.
Savoir faire ce choix, c'est souvent plus important que bien connaître
des templates ou la conception OO.
Mais ne laisse pas ce fait te dégouter de l'informatique, ni du monde
industriel ou commerciel. Il faut partir du principe que le programme
que tu laisses ne serait jamais parfait. Mais avec un peu d'effort, tu
peux faire en sort qu'il serait toujours mieux que quand tu l'as pris en
main. Ce qui est aussi une satisfaction. (Personnellement, je tire aussi
une très grande satisfaction du fait que certains programmes sur
lesquels j'ai travaillé sont réelement utilisés, de façon intense. Faire
un programme parfait, mais qui ne sert pas, ne me donnerait pas autant
de satisfaction.)
J'ai pensé à plusieurs solutions (36 constructeurs dans Curve, ton template
utilisant une fonction template GetTripletType spécialisée 36 fois,...).
Voici celle que j'ai retenu car me paraissant le plus propre (couplage le
plus faible, code le plus restreint ) :
class ABC
{
public:
Triplet getTriplet() const;
static const std::string TypeName;
};
const std::string ABC::TypeName = "ABC";
template< typename T >
Curve
BuildCurve( const std::vector<T> & Values )
{
Curve curve;
for ( size_t i = 0, size = Values.size(); i < size; ++i )
{
curve.Triplets.push_back( Values[ i ].getTriplet() ) ;
}
curve.Type = T::TypeName;
return curve ;
}
void test()
{
std::vector<ABC> values;
Curve c = BuildCurve<ABC>( values );
}
J'ai pensé à plusieurs solutions (36 constructeurs dans Curve, ton template
utilisant une fonction template GetTripletType spécialisée 36 fois,...).
Voici celle que j'ai retenu car me paraissant le plus propre (couplage le
plus faible, code le plus restreint ) :
class ABC
{
public:
Triplet getTriplet() const;
static const std::string TypeName;
};
const std::string ABC::TypeName = "ABC";
template< typename T >
Curve
BuildCurve( const std::vector<T> & Values )
{
Curve curve;
for ( size_t i = 0, size = Values.size(); i < size; ++i )
{
curve.Triplets.push_back( Values[ i ].getTriplet() ) ;
}
curve.Type = T::TypeName;
return curve ;
}
void test()
{
std::vector<ABC> values;
Curve c = BuildCurve<ABC>( values );
}
J'ai pensé à plusieurs solutions (36 constructeurs dans Curve, ton template
utilisant une fonction template GetTripletType spécialisée 36 fois,...).
Voici celle que j'ai retenu car me paraissant le plus propre (couplage le
plus faible, code le plus restreint ) :
class ABC
{
public:
Triplet getTriplet() const;
static const std::string TypeName;
};
const std::string ABC::TypeName = "ABC";
template< typename T >
Curve
BuildCurve( const std::vector<T> & Values )
{
Curve curve;
for ( size_t i = 0, size = Values.size(); i < size; ++i )
{
curve.Triplets.push_back( Values[ i ].getTriplet() ) ;
}
curve.Type = T::TypeName;
return curve ;
}
void test()
{
std::vector<ABC> values;
Curve c = BuildCurve<ABC>( values );
}
Tu peux faire encore un peu plus générique comme ça :
template< typename InputIterator >
Curve
BuildCurve( InputIterator first, InputIterator last )
{
Curve curve;
for( InputIterator i = first; i != last; ++i )
{
curve.Triplets.push_back( i->getTriplet() );
}
curve.Type = iterator_traits<InputIterator>::value_type::TypeName;
return curve;
}
L'avantage est que tu peux construire un object Curve à partir de
n'importe quel type de conteneur.
Cela dit si tu peux modifier Curve (j'ai pas lu l'intégralité du fil
dont j'ai un doute) tu peux faire la même chose dans un constructeur
template :
class Curve {
public:
template< typename InputIterator >
Curve(InputIterator first, InputIterator last );
}
Ce qui donne, pour l'instanciation :
Curve c( values.begin(), values.end() );
Tu peux faire encore un peu plus générique comme ça :
template< typename InputIterator >
Curve
BuildCurve( InputIterator first, InputIterator last )
{
Curve curve;
for( InputIterator i = first; i != last; ++i )
{
curve.Triplets.push_back( i->getTriplet() );
}
curve.Type = iterator_traits<InputIterator>::value_type::TypeName;
return curve;
}
L'avantage est que tu peux construire un object Curve à partir de
n'importe quel type de conteneur.
Cela dit si tu peux modifier Curve (j'ai pas lu l'intégralité du fil
dont j'ai un doute) tu peux faire la même chose dans un constructeur
template :
class Curve {
public:
template< typename InputIterator >
Curve(InputIterator first, InputIterator last );
}
Ce qui donne, pour l'instanciation :
Curve c( values.begin(), values.end() );
Tu peux faire encore un peu plus générique comme ça :
template< typename InputIterator >
Curve
BuildCurve( InputIterator first, InputIterator last )
{
Curve curve;
for( InputIterator i = first; i != last; ++i )
{
curve.Triplets.push_back( i->getTriplet() );
}
curve.Type = iterator_traits<InputIterator>::value_type::TypeName;
return curve;
}
L'avantage est que tu peux construire un object Curve à partir de
n'importe quel type de conteneur.
Cela dit si tu peux modifier Curve (j'ai pas lu l'intégralité du fil
dont j'ai un doute) tu peux faire la même chose dans un constructeur
template :
class Curve {
public:
template< typename InputIterator >
Curve(InputIterator first, InputIterator last );
}
Ce qui donne, pour l'instanciation :
Curve c( values.begin(), values.end() );
Tu peux faire encore un peu plus générique comme ça :
template< typename InputIterator >
Curve
BuildCurve( InputIterator first, InputIterator last )
{
Curve curve;
for( InputIterator i = first; i != last; ++i )
{
curve.Triplets.push_back( i->getTriplet() );
}
curve.Type = iterator_traits<InputIterator>::value_type::TypeName;
return curve;
}
Je connaissais pas, je pensais que c'était possible avec le RTTI, mais pas
avec la STL. Mais je ne peux pas utiliser ta solution car le nom de mes
classes n'est pas toujours parfaitement égal à celui du Triplet (Par exemple
le triplet TempDeg s'appelle "Temp°", ...). Mais merci pour le tuyau.
Ah ben décidémment je pensais pas qu'on pouvait mettre une fonction template
comme membre d'une classe "normale". Je croyais qu'on pouvais seulement
faire des fonctions globales. Comment ça se passe après au niveau de la
compilation ? Il génère autant de fonctions membres que t'instancies cette
fonction template ?
Mais je vais m'en tenir à une fonction séparée car je risque plus tard de
devoir à nouveau convertir mes Triplets, donc avoir besoin du même code que
dans le constructeur et donc le dupliquer. En revanche ajouter un
constructeur template qui utilise la fonction template de conversion, c'est
pile poil ce qu'il me faut. C'est terrible, pouvoir accepter un argument
générique dans une classe non template est vraiment ce que je voulais depuis
le début.
Tu peux faire encore un peu plus générique comme ça :
template< typename InputIterator >
Curve
BuildCurve( InputIterator first, InputIterator last )
{
Curve curve;
for( InputIterator i = first; i != last; ++i )
{
curve.Triplets.push_back( i->getTriplet() );
}
curve.Type = iterator_traits<InputIterator>::value_type::TypeName;
return curve;
}
Je connaissais pas, je pensais que c'était possible avec le RTTI, mais pas
avec la STL. Mais je ne peux pas utiliser ta solution car le nom de mes
classes n'est pas toujours parfaitement égal à celui du Triplet (Par exemple
le triplet TempDeg s'appelle "Temp°", ...). Mais merci pour le tuyau.
Ah ben décidémment je pensais pas qu'on pouvait mettre une fonction template
comme membre d'une classe "normale". Je croyais qu'on pouvais seulement
faire des fonctions globales. Comment ça se passe après au niveau de la
compilation ? Il génère autant de fonctions membres que t'instancies cette
fonction template ?
Mais je vais m'en tenir à une fonction séparée car je risque plus tard de
devoir à nouveau convertir mes Triplets, donc avoir besoin du même code que
dans le constructeur et donc le dupliquer. En revanche ajouter un
constructeur template qui utilise la fonction template de conversion, c'est
pile poil ce qu'il me faut. C'est terrible, pouvoir accepter un argument
générique dans une classe non template est vraiment ce que je voulais depuis
le début.
Tu peux faire encore un peu plus générique comme ça :
template< typename InputIterator >
Curve
BuildCurve( InputIterator first, InputIterator last )
{
Curve curve;
for( InputIterator i = first; i != last; ++i )
{
curve.Triplets.push_back( i->getTriplet() );
}
curve.Type = iterator_traits<InputIterator>::value_type::TypeName;
return curve;
}
Je connaissais pas, je pensais que c'était possible avec le RTTI, mais pas
avec la STL. Mais je ne peux pas utiliser ta solution car le nom de mes
classes n'est pas toujours parfaitement égal à celui du Triplet (Par exemple
le triplet TempDeg s'appelle "Temp°", ...). Mais merci pour le tuyau.
Ah ben décidémment je pensais pas qu'on pouvait mettre une fonction template
comme membre d'une classe "normale". Je croyais qu'on pouvais seulement
faire des fonctions globales. Comment ça se passe après au niveau de la
compilation ? Il génère autant de fonctions membres que t'instancies cette
fonction template ?
Mais je vais m'en tenir à une fonction séparée car je risque plus tard de
devoir à nouveau convertir mes Triplets, donc avoir besoin du même code que
dans le constructeur et donc le dupliquer. En revanche ajouter un
constructeur template qui utilise la fonction template de conversion, c'est
pile poil ce qu'il me faut. C'est terrible, pouvoir accepter un argument
générique dans une classe non template est vraiment ce que je voulais depuis
le début.
Ah ben décidémment je pensais pas qu'on pouvait mettre une fonction template
comme membre d'une classe "normale". Je croyais qu'on pouvais seulement
faire des fonctions globales. Comment ça se passe après au niveau de la
compilation ? Il génère autant de fonctions membres que t'instancies cette
fonction template ?
Ah ben décidémment je pensais pas qu'on pouvait mettre une fonction template
comme membre d'une classe "normale". Je croyais qu'on pouvais seulement
faire des fonctions globales. Comment ça se passe après au niveau de la
compilation ? Il génère autant de fonctions membres que t'instancies cette
fonction template ?
Ah ben décidémment je pensais pas qu'on pouvait mettre une fonction template
comme membre d'une classe "normale". Je croyais qu'on pouvais seulement
faire des fonctions globales. Comment ça se passe après au niveau de la
compilation ? Il génère autant de fonctions membres que t'instancies cette
fonction template ?