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 ?
En fait j'ai plusieurs graphes. Pour l'instant 2 sont limités à un
seul type de Triplet, et un autre est capable de presque tous les
représenter, à 1 exception près, pour lequel ça n'a pas de sens. Donc
je compte créer une classe Curve pouvant être construite à partir de
n'importe quelle liste de Triplet, et qui contient le nom du type de
Triplet utilisé pour la construire. Chaque graphe possède une méthode
draw(const Curve &) et libre à lui d'accepter ou non de dessiner cette
courbe en fonction de ton type. Ces graphes dérivent de GraphBase qui
accepte sans broncher une courbe Curve à dessiner.
Le graphe qui dessine n'importe quel Triplet sauf ABC ressemblera donc
à (c'est un exemple minimal):
struct Curve
{
public:
std::vector<Triplet> Triplets; // les valeurs à dessiner
std::string Type; // type de Triplet : utilisé pour les legendes du
graph & les vérifications
};
class Graph : public GraphBase
{
void Graph::Draw( const Curve & C )
{
if ( C.Type == "ABC" ) throw InvalidCurveType;
GraphBase::DrawCurve( C.Triplets );
GraphBase::SetLegend( C.Type );
}
};
Ca évite 36 fonctions de dessin spécialisées pour chaque type de
Triplet, sans compter qu'il faut créer autant de type Curve qu'il y a
de Triplet. Reste à construire une Curve à partir d'une liste de
Triplets pour les 36 types des Triplets... 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 );
}
C'est directement inspiré de ta solution, sauf que au lieu d'une
fonction getTriplet() il faut aussi le nom TypeName;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),
Je suis en entreprise.
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.
C'est pour gagner du temps que je reprend ce code qui ne me plaît
guère.
Je me force un peu car j'ai entendu parler du syndrome NIH (Not
Implemented Here) qui est une des causes principales des retards et
surcoûts de projets (c'est pas moi qui l'ait fait donc c'est nul donc
je refait). Ce n'est pas reprendre du code qui me gêne, c'est
reprendre du code bourré de mégas macros difficiles à lires et très
dures à débogguer.
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'essaye de travailler dans ce sens : améliorer les choses. Mais
certains pensent que c'est déjà parfait.
Mais je suis optimiste, je devrait pouvoir utiliser cette approche en
créant une petite couche de liaison entre le code bourré de macros et
un code "Thanks to James Kanze for this great tip". Intégrer du code
bourré de macros ne me gêne pas trop s'il marche. Ce qui me
désespérais c'est de devoir y intégrer mon code et donc d'utiliser
cette programmation par macros...Je pense arriver à y échapper. :-)
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 ?
En fait j'ai plusieurs graphes. Pour l'instant 2 sont limités à un
seul type de Triplet, et un autre est capable de presque tous les
représenter, à 1 exception près, pour lequel ça n'a pas de sens. Donc
je compte créer une classe Curve pouvant être construite à partir de
n'importe quelle liste de Triplet, et qui contient le nom du type de
Triplet utilisé pour la construire. Chaque graphe possède une méthode
draw(const Curve &) et libre à lui d'accepter ou non de dessiner cette
courbe en fonction de ton type. Ces graphes dérivent de GraphBase qui
accepte sans broncher une courbe Curve à dessiner.
Le graphe qui dessine n'importe quel Triplet sauf ABC ressemblera donc
à (c'est un exemple minimal):
struct Curve
{
public:
std::vector<Triplet> Triplets; // les valeurs à dessiner
std::string Type; // type de Triplet : utilisé pour les legendes du
graph & les vérifications
};
class Graph : public GraphBase
{
void Graph::Draw( const Curve & C )
{
if ( C.Type == "ABC" ) throw InvalidCurveType;
GraphBase::DrawCurve( C.Triplets );
GraphBase::SetLegend( C.Type );
}
};
Ca évite 36 fonctions de dessin spécialisées pour chaque type de
Triplet, sans compter qu'il faut créer autant de type Curve qu'il y a
de Triplet. Reste à construire une Curve à partir d'une liste de
Triplets pour les 36 types des Triplets... 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 );
}
C'est directement inspiré de ta solution, sauf que au lieu d'une
fonction getTriplet() il faut aussi le nom TypeName;
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),
Je suis en entreprise.
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.
C'est pour gagner du temps que je reprend ce code qui ne me plaît
guère.
Je me force un peu car j'ai entendu parler du syndrome NIH (Not
Implemented Here) qui est une des causes principales des retards et
surcoûts de projets (c'est pas moi qui l'ait fait donc c'est nul donc
je refait). Ce n'est pas reprendre du code qui me gêne, c'est
reprendre du code bourré de mégas macros difficiles à lires et très
dures à débogguer.
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'essaye de travailler dans ce sens : améliorer les choses. Mais
certains pensent que c'est déjà parfait.
Mais je suis optimiste, je devrait pouvoir utiliser cette approche en
créant une petite couche de liaison entre le code bourré de macros et
un code "Thanks to James Kanze for this great tip". Intégrer du code
bourré de macros ne me gêne pas trop s'il marche. Ce qui me
désespérais c'est de devoir y intégrer mon code et donc d'utiliser
cette programmation par macros...Je pense arriver à y échapper. :-)
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 ?
En fait j'ai plusieurs graphes. Pour l'instant 2 sont limités à un
seul type de Triplet, et un autre est capable de presque tous les
représenter, à 1 exception près, pour lequel ça n'a pas de sens. Donc
je compte créer une classe Curve pouvant être construite à partir de
n'importe quelle liste de Triplet, et qui contient le nom du type de
Triplet utilisé pour la construire. Chaque graphe possède une méthode
draw(const Curve &) et libre à lui d'accepter ou non de dessiner cette
courbe en fonction de ton type. Ces graphes dérivent de GraphBase qui
accepte sans broncher une courbe Curve à dessiner.
Le graphe qui dessine n'importe quel Triplet sauf ABC ressemblera donc
à (c'est un exemple minimal):
struct Curve
{
public:
std::vector<Triplet> Triplets; // les valeurs à dessiner
std::string Type; // type de Triplet : utilisé pour les legendes du
graph & les vérifications
};
class Graph : public GraphBase
{
void Graph::Draw( const Curve & C )
{
if ( C.Type == "ABC" ) throw InvalidCurveType;
GraphBase::DrawCurve( C.Triplets );
GraphBase::SetLegend( C.Type );
}
};
Ca évite 36 fonctions de dessin spécialisées pour chaque type de
Triplet, sans compter qu'il faut créer autant de type Curve qu'il y a
de Triplet. Reste à construire une Curve à partir d'une liste de
Triplets pour les 36 types des Triplets... 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 );
}
C'est directement inspiré de ta solution, sauf que au lieu d'une
fonction getTriplet() il faut aussi le nom TypeName;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),
Je suis en entreprise.
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.
C'est pour gagner du temps que je reprend ce code qui ne me plaît
guère.
Je me force un peu car j'ai entendu parler du syndrome NIH (Not
Implemented Here) qui est une des causes principales des retards et
surcoûts de projets (c'est pas moi qui l'ait fait donc c'est nul donc
je refait). Ce n'est pas reprendre du code qui me gêne, c'est
reprendre du code bourré de mégas macros difficiles à lires et très
dures à débogguer.
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'essaye de travailler dans ce sens : améliorer les choses. Mais
certains pensent que c'est déjà parfait.
Mais je suis optimiste, je devrait pouvoir utiliser cette approche en
créant une petite couche de liaison entre le code bourré de macros et
un code "Thanks to James Kanze for this great tip". Intégrer du code
bourré de macros ne me gêne pas trop s'il marche. Ce qui me
désespérais c'est de devoir y intégrer mon code et donc d'utiliser
cette programmation par macros...Je pense arriver à y échapper. :-)
Aurélien REGAT-BARREL wrote:
<snip>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 ;
}
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;
}
void test()
{
std::vector<ABC> values;
Curve c = BuildCurve<ABC>( values );
Curve c = BuildCurve( values.begin(), values.end() );}
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() );
Aurélien REGAT-BARREL wrote:
<snip>
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 ;
}
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;
}
void test()
{
std::vector<ABC> values;
Curve c = BuildCurve<ABC>( values );
Curve c = BuildCurve( values.begin(), values.end() );
}
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() );
Aurélien REGAT-BARREL wrote:
<snip>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 ;
}
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;
}
void test()
{
std::vector<ABC> values;
Curve c = BuildCurve<ABC>( values );
Curve c = BuildCurve( values.begin(), values.end() );}
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() );
En ce qui concerne le typage, est-ce que tu connais dynamic_cast et
typeid ? Pour le premier emploi, au moins, on pourrait très bien se
servir de typeid et std::type_info, et libérer l'utilisateur du devoir
de le déclarer lui-même.
Mais ici aussi, j'aurais peut-être une approche un peu différente. Tu as
de différents types de Triplet, et de différents types de Graph. Alors,
on pourrait imaginer que pour chaque type de Triplet, on associe un
std::set< std::type_info const* >, avec les types des graphes auquels le
Triplet peut être associé. Voire...
On définit une classe ProprietesDeTriplet, qui contient comme membres
l'ensemble des graphes supportés, le libellé pour la graphe, etc. Avec
une instance par type de Triplet ; Curve contiendrait alors un pointeur
à cet objet, et chaque type de Graph pourrait commencer par :
if ( c.tripletInfo->supportedGraphTypes.find( &typeid( *this ) )
== c.tripletInfo->supportedGraphTypes.end() ) {
throw InvalidCurveType ;
}
// ...
setLegent( c.tripletInfo.libelle ) ;
Mais ça met l'information sur les types de graphes supportés dans les
classes ABC, DEF, etc. Je ne sais pas assez de l'application pour savoir
si c'est bien ou non. Si l'information doit être dans les Graph, on peut
l'y mettre aussi. Ou on pourrait se servir d'un map indépendant des
deux.
J'aurais tendance à en faire une fonction membre statique. Voire
carrément de mettre le code dans un constructeur templaté. À condition,
évidemment, d'avoir un compilateur à peu près à jour, qui supporte des
membres templatés (mais ça devient de moins en moins un problème).
J'essaye de travailler dans ce sens : améliorer les choses. Mais
certains pensent que c'est déjà parfait.
Évidemment, puisque c'est eux qui l'ont fait:-).
L'importance, c'est de se rendre compte que ce n'est jamais parfait.
Même quand on l'a fait soi-même. Il y a toujours des contraits externes,
de temps ou d'autres choses, qui font qu'on aurait pu faire mieux, si.
L'importance, aussi, c'est de se rendre compte que nous ne connaissons
jamais les conditions et les contraintes qui étaient présentes quand le
code était écrit. On ne peut donc jamais dire qu'on aurait fait mieux.
Tout code est ce qu'il est. Il a une histoire, qu'on ne connaît jamais
parfaitement, mais qui explique pourquoi il est ce qu'il est.
L'importance, ce n'est pas comment il y est arrivé ; c'est où on va de
là. Il faut savoir accepter ce qu'ont fait les autres. Ce n'est pas
« leur code, c'est de la merde », mais « s'ils n'avaient pas eu
tellement de contraites externes, ils l'auraient sûrement écrit
différemment. »
Mais je suis optimiste, je devrait pouvoir utiliser cette approche en
créant une petite couche de liaison entre le code bourré de macros et
un code "Thanks to James Kanze for this great tip". Intégrer du code
bourré de macros ne me gêne pas trop s'il marche. Ce qui me
désespérais c'est de devoir y intégrer mon code et donc d'utiliser
cette programmation par macros...Je pense arriver à y échapper. :-)
C'est une bonne approche. On utilise ce qu'on peut. S'il y a des parties
vraiment sales, on les isole. L'importance, c'est le résultat. Le
résultat total : que ça marche, que c'est maintienable, et que ça n'a
pas coûté plus que nécessaire.
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni mauvais.
Il est, c'est tout.
En ce qui concerne le typage, est-ce que tu connais dynamic_cast et
typeid ? Pour le premier emploi, au moins, on pourrait très bien se
servir de typeid et std::type_info, et libérer l'utilisateur du devoir
de le déclarer lui-même.
Mais ici aussi, j'aurais peut-être une approche un peu différente. Tu as
de différents types de Triplet, et de différents types de Graph. Alors,
on pourrait imaginer que pour chaque type de Triplet, on associe un
std::set< std::type_info const* >, avec les types des graphes auquels le
Triplet peut être associé. Voire...
On définit une classe ProprietesDeTriplet, qui contient comme membres
l'ensemble des graphes supportés, le libellé pour la graphe, etc. Avec
une instance par type de Triplet ; Curve contiendrait alors un pointeur
à cet objet, et chaque type de Graph pourrait commencer par :
if ( c.tripletInfo->supportedGraphTypes.find( &typeid( *this ) )
== c.tripletInfo->supportedGraphTypes.end() ) {
throw InvalidCurveType ;
}
// ...
setLegent( c.tripletInfo.libelle ) ;
Mais ça met l'information sur les types de graphes supportés dans les
classes ABC, DEF, etc. Je ne sais pas assez de l'application pour savoir
si c'est bien ou non. Si l'information doit être dans les Graph, on peut
l'y mettre aussi. Ou on pourrait se servir d'un map indépendant des
deux.
J'aurais tendance à en faire une fonction membre statique. Voire
carrément de mettre le code dans un constructeur templaté. À condition,
évidemment, d'avoir un compilateur à peu près à jour, qui supporte des
membres templatés (mais ça devient de moins en moins un problème).
J'essaye de travailler dans ce sens : améliorer les choses. Mais
certains pensent que c'est déjà parfait.
Évidemment, puisque c'est eux qui l'ont fait:-).
L'importance, c'est de se rendre compte que ce n'est jamais parfait.
Même quand on l'a fait soi-même. Il y a toujours des contraits externes,
de temps ou d'autres choses, qui font qu'on aurait pu faire mieux, si.
L'importance, aussi, c'est de se rendre compte que nous ne connaissons
jamais les conditions et les contraintes qui étaient présentes quand le
code était écrit. On ne peut donc jamais dire qu'on aurait fait mieux.
Tout code est ce qu'il est. Il a une histoire, qu'on ne connaît jamais
parfaitement, mais qui explique pourquoi il est ce qu'il est.
L'importance, ce n'est pas comment il y est arrivé ; c'est où on va de
là. Il faut savoir accepter ce qu'ont fait les autres. Ce n'est pas
« leur code, c'est de la merde », mais « s'ils n'avaient pas eu
tellement de contraites externes, ils l'auraient sûrement écrit
différemment. »
Mais je suis optimiste, je devrait pouvoir utiliser cette approche en
créant une petite couche de liaison entre le code bourré de macros et
un code "Thanks to James Kanze for this great tip". Intégrer du code
bourré de macros ne me gêne pas trop s'il marche. Ce qui me
désespérais c'est de devoir y intégrer mon code et donc d'utiliser
cette programmation par macros...Je pense arriver à y échapper. :-)
C'est une bonne approche. On utilise ce qu'on peut. S'il y a des parties
vraiment sales, on les isole. L'importance, c'est le résultat. Le
résultat total : que ça marche, que c'est maintienable, et que ça n'a
pas coûté plus que nécessaire.
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni mauvais.
Il est, c'est tout.
En ce qui concerne le typage, est-ce que tu connais dynamic_cast et
typeid ? Pour le premier emploi, au moins, on pourrait très bien se
servir de typeid et std::type_info, et libérer l'utilisateur du devoir
de le déclarer lui-même.
Mais ici aussi, j'aurais peut-être une approche un peu différente. Tu as
de différents types de Triplet, et de différents types de Graph. Alors,
on pourrait imaginer que pour chaque type de Triplet, on associe un
std::set< std::type_info const* >, avec les types des graphes auquels le
Triplet peut être associé. Voire...
On définit une classe ProprietesDeTriplet, qui contient comme membres
l'ensemble des graphes supportés, le libellé pour la graphe, etc. Avec
une instance par type de Triplet ; Curve contiendrait alors un pointeur
à cet objet, et chaque type de Graph pourrait commencer par :
if ( c.tripletInfo->supportedGraphTypes.find( &typeid( *this ) )
== c.tripletInfo->supportedGraphTypes.end() ) {
throw InvalidCurveType ;
}
// ...
setLegent( c.tripletInfo.libelle ) ;
Mais ça met l'information sur les types de graphes supportés dans les
classes ABC, DEF, etc. Je ne sais pas assez de l'application pour savoir
si c'est bien ou non. Si l'information doit être dans les Graph, on peut
l'y mettre aussi. Ou on pourrait se servir d'un map indépendant des
deux.
J'aurais tendance à en faire une fonction membre statique. Voire
carrément de mettre le code dans un constructeur templaté. À condition,
évidemment, d'avoir un compilateur à peu près à jour, qui supporte des
membres templatés (mais ça devient de moins en moins un problème).
J'essaye de travailler dans ce sens : améliorer les choses. Mais
certains pensent que c'est déjà parfait.
Évidemment, puisque c'est eux qui l'ont fait:-).
L'importance, c'est de se rendre compte que ce n'est jamais parfait.
Même quand on l'a fait soi-même. Il y a toujours des contraits externes,
de temps ou d'autres choses, qui font qu'on aurait pu faire mieux, si.
L'importance, aussi, c'est de se rendre compte que nous ne connaissons
jamais les conditions et les contraintes qui étaient présentes quand le
code était écrit. On ne peut donc jamais dire qu'on aurait fait mieux.
Tout code est ce qu'il est. Il a une histoire, qu'on ne connaît jamais
parfaitement, mais qui explique pourquoi il est ce qu'il est.
L'importance, ce n'est pas comment il y est arrivé ; c'est où on va de
là. Il faut savoir accepter ce qu'ont fait les autres. Ce n'est pas
« leur code, c'est de la merde », mais « s'ils n'avaient pas eu
tellement de contraites externes, ils l'auraient sûrement écrit
différemment. »
Mais je suis optimiste, je devrait pouvoir utiliser cette approche en
créant une petite couche de liaison entre le code bourré de macros et
un code "Thanks to James Kanze for this great tip". Intégrer du code
bourré de macros ne me gêne pas trop s'il marche. Ce qui me
désespérais c'est de devoir y intégrer mon code et donc d'utiliser
cette programmation par macros...Je pense arriver à y échapper. :-)
C'est une bonne approche. On utilise ce qu'on peut. S'il y a des parties
vraiment sales, on les isole. L'importance, c'est le résultat. Le
résultat total : que ça marche, que c'est maintienable, et que ça n'a
pas coûté plus que nécessaire.
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni mauvais.
Il est, c'est tout.
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.
Heu, pas compris le problème là. Ma solution n'est pas plus
contraignante que la fonction que tu as proposé, elle est juste plus
souple car tu tout conteneur avec des itérateurs fera l'affaire, même un
tableau "à la C" si ça te chante. C'est d'ailleur inspiré de ce qui est
fait pour certaines fonctions des conteneurs de la STL (cf
http://www.sgi.com/tech/stl/Vector.html par exemple).
Il y a encore une option, mais qui te fait un peu changer tes classes,
c'est de définir l'operateur de conversion vers Triplet. Comme ça tu
peux remplir ton std::vector directement, ce qui donne quelque chose comme
:
C'est encore plus concis comme écriture, et donc moins explicite. C'est
à toi de voir ce que tu préfères.
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.
Heu, pas compris le problème là. Ma solution n'est pas plus
contraignante que la fonction que tu as proposé, elle est juste plus
souple car tu tout conteneur avec des itérateurs fera l'affaire, même un
tableau "à la C" si ça te chante. C'est d'ailleur inspiré de ce qui est
fait pour certaines fonctions des conteneurs de la STL (cf
http://www.sgi.com/tech/stl/Vector.html par exemple).
Il y a encore une option, mais qui te fait un peu changer tes classes,
c'est de définir l'operateur de conversion vers Triplet. Comme ça tu
peux remplir ton std::vector directement, ce qui donne quelque chose comme
:
C'est encore plus concis comme écriture, et donc moins explicite. C'est
à toi de voir ce que tu préfères.
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.
Heu, pas compris le problème là. Ma solution n'est pas plus
contraignante que la fonction que tu as proposé, elle est juste plus
souple car tu tout conteneur avec des itérateurs fera l'affaire, même un
tableau "à la C" si ça te chante. C'est d'ailleur inspiré de ce qui est
fait pour certaines fonctions des conteneurs de la STL (cf
http://www.sgi.com/tech/stl/Vector.html par exemple).
Il y a encore une option, mais qui te fait un peu changer tes classes,
c'est de définir l'operateur de conversion vers Triplet. Comme ça tu
peux remplir ton std::vector directement, ce qui donne quelque chose comme
:
C'est encore plus concis comme écriture, et donc moins explicite. C'est
à toi de voir ce que tu préfères.
Mais ici aussi, j'aurais peut-être une approche un peu différente. Tu as
de différents types de Triplet, et de différents types de Graph. Alors,
on pourrait imaginer que pour chaque type de Triplet, on associe un
std::set< std::type_info const* >, avec les types des graphes auquels le
Triplet peut être associé. Voire...
[...]
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni
mauvais.
Mais ici aussi, j'aurais peut-être une approche un peu différente. Tu as
de différents types de Triplet, et de différents types de Graph. Alors,
on pourrait imaginer que pour chaque type de Triplet, on associe un
std::set< std::type_info const* >, avec les types des graphes auquels le
Triplet peut être associé. Voire...
[...]
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni
mauvais.
Mais ici aussi, j'aurais peut-être une approche un peu différente. Tu as
de différents types de Triplet, et de différents types de Graph. Alors,
on pourrait imaginer que pour chaque type de Triplet, on associe un
std::set< std::type_info const* >, avec les types des graphes auquels le
Triplet peut être associé. Voire...
[...]
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni
mauvais.
writes:Mais ici aussi, j'aurais peut-être une approche un peu différente.
Tu as de différents types de Triplet, et de différents types de
Graph. Alors, on pourrait imaginer que pour chaque type de Triplet,
on associe un std::set< std::type_info const* >, avec les types des
graphes auquels le Triplet peut être associé. Voire...
Je n'ai suivi la discussion que de loin, mais il me semble qu'il
s'agit ici d'un problème de typage *à la compilation*. Ne vaudrait-il
pas mieux utiliser quelque chose comme les TypeList d'Alexandrescu ?
[...]
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni
mauvais.
Ben heu, si, quand même un peu ...
kanze@gabi-soft.fr writes:
Mais ici aussi, j'aurais peut-être une approche un peu différente.
Tu as de différents types de Triplet, et de différents types de
Graph. Alors, on pourrait imaginer que pour chaque type de Triplet,
on associe un std::set< std::type_info const* >, avec les types des
graphes auquels le Triplet peut être associé. Voire...
Je n'ai suivi la discussion que de loin, mais il me semble qu'il
s'agit ici d'un problème de typage *à la compilation*. Ne vaudrait-il
pas mieux utiliser quelque chose comme les TypeList d'Alexandrescu ?
[...]
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni
mauvais.
Ben heu, si, quand même un peu ...
writes:Mais ici aussi, j'aurais peut-être une approche un peu différente.
Tu as de différents types de Triplet, et de différents types de
Graph. Alors, on pourrait imaginer que pour chaque type de Triplet,
on associe un std::set< std::type_info const* >, avec les types des
graphes auquels le Triplet peut être associé. Voire...
Je n'ai suivi la discussion que de loin, mais il me semble qu'il
s'agit ici d'un problème de typage *à la compilation*. Ne vaudrait-il
pas mieux utiliser quelque chose comme les TypeList d'Alexandrescu ?
[...]
Autrement dit, une fois qu'il existe, le code n'est ni bon, ni
mauvais.
Ben heu, si, quand même un peu ...
drkm wrote in message
news:...Je n'ai suivi la discussion que de loin, mais il me semble qu'il
s'agit ici d'un problème de typage *à la compilation*. Ne vaudrait-il
pas mieux utiliser quelque chose comme les TypeList d'Alexandrescu ?
Je ne sais pas ; je ne connais pas TypeList. Étant obligé à utiliser des
compilateurs assez anciens, et d'écrire du code que peuvent comprendre
des collègues qui ne veulent pas se mettre à jour, j'ai rarement
l'occasion de pouvoir profiter de ce que fait Andrei.
Est-ce une
implémentation de ce que je propose ?
drkm <usenet.fclcxx@fgeorges.org> wrote in message
news:<wkk6xqcwm7.fsf@fgeorges.org>...
Je n'ai suivi la discussion que de loin, mais il me semble qu'il
s'agit ici d'un problème de typage *à la compilation*. Ne vaudrait-il
pas mieux utiliser quelque chose comme les TypeList d'Alexandrescu ?
Je ne sais pas ; je ne connais pas TypeList. Étant obligé à utiliser des
compilateurs assez anciens, et d'écrire du code que peuvent comprendre
des collègues qui ne veulent pas se mettre à jour, j'ai rarement
l'occasion de pouvoir profiter de ce que fait Andrei.
Est-ce une
implémentation de ce que je propose ?
drkm wrote in message
news:...Je n'ai suivi la discussion que de loin, mais il me semble qu'il
s'agit ici d'un problème de typage *à la compilation*. Ne vaudrait-il
pas mieux utiliser quelque chose comme les TypeList d'Alexandrescu ?
Je ne sais pas ; je ne connais pas TypeList. Étant obligé à utiliser des
compilateurs assez anciens, et d'écrire du code que peuvent comprendre
des collègues qui ne veulent pas se mettre à jour, j'ai rarement
l'occasion de pouvoir profiter de ce que fait Andrei.
Est-ce une
implémentation de ce que je propose ?