Definition d'une interface (a la Java) a l'aide de templates
62 réponses
Aurélien REGAT-BARREL
Bonjour à tous,
Je voudrais traduire une interface au sens pur, vraiment logique (ensemble
de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites
de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc
rabatu sur les templates.
Le but est de traduire un shéma UML où divers objets implémentent une
interface ITriplet qui stipule que :
- l'objet est construit à partir de 3 valeurs A, B et C (=> un triplet)
- l'objet possède une méthode GetValue(int) permettant d'accéder à ces 3
valeurs
- l'objet possède une méthode statique GetTripletName() qui retourne le nom
du triplet
Le truc étant que la plupart des objets font des modifications sur A, B et C
avant de les stocker. Les divers objets qui implémentent ITriplet n'ont rien
en commun et ne doivent pas être comparables, pas mélangeables, ... d'où pas
de polymorphisme.
Voilà les solutions auxquelles j'ai pensé, quel est votre opinion :
Pour l'exemple, un seul objet ABC inmplémente ITriplet.
1- on laisse le programmeur faire tout ce qu'il faut (on le guide au
maximum)
INTERFACE( ITriplet
{
public:
ITriplet( double A, double B, double C );
double GetValue( int Index ) const;
static std::string GetTripletName();
};
)
// triplet ABC
class ABC IMPLEMENTS( ITriplet )
{
public:
ABC( double A, double B, double C )
{
data.reserve( 3 );
data.push_back( A );
data.push_back( B );
data.push_back( C );
}
double GetValue( int Index ) const
{
return this->data.at( Index );
}
static std::string GetTripletName()
{
return "A.B.C";
}
private:
std::vector<double> data;
};
template <typename T>
WHERE( T : ITriplet )
T TripletBuilder()
{
return T( 0, 1, 2 );
}
int main()
{
ABC abc = TripletBuilder<ABC>();
}
2- on a bien une interface ITriplet sous forme de template. C'est plus
l'aspect macro qui est exploité ici
// functor pour construire le triplet
typedef std::vector<double> (TripletCtor)( double A, double B, double C );
// functor pour récupérer son nom
typedef std::string (TripletGetName)();
// interface ITriplet
template<TripletCtor Func, TripletGetName Name>
class ITriplet
{
public:
ITriplet( double A, double B, double C ) :
data( Func( A, B, C ) )
{
}
double GetValue( int Index ) const
{
return this->data.at( Index );
}
static std::string GetTripletName()
{
return Name();
}
private:
std::vector<double> data;
};
// triplet ABC
std::vector<double> ComputeABC( double A, double B, double C )
{
std::vector<double> result;
result.reserve( 3 );
result.push_back( A );
result.push_back( B );
result.push_back( C );
return result;
}
std::string GetNameABC()
{
return "A.B.C";
}
typedef ITriplet<ComputeABC, GetNameABC> ABC;
3 - fournir un template de vérification des méthodes attendues. Au lieu de
donner une interface au programmeur comme en 1, on donne un test à faire
passer à son objet.
template<typename T>
class ITripletImplementationChecker
{
public:
ITripletImplementationChecker()
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
T t( a, b, c );
t.GetValue( 0 );
t.GetValue( 1 );
t.GetValue( 2 );
T::GetTripletName();
}
};
ITripletImplementationChecker<ABC> check_abc;
Comme j'ai l'impression de faire n'importe quoi, j'aimerais avoir votre
avis.
Merci de votre aide.
// Intanciation explicite template class ITriplet< Essai >;
Mon incompréhension venait de là en fait je crois. Je pensais qu'à ce stade, le compilo instanciait le template et générait toutes ses fonctions membres. Mais si j'ai bien compris, il n'instancie (à ce stade) que la classe ITriplet<Essai> au sens conteneur de données (struct C), et instancie ses fonctions membres que si elles sont appelées, et au moment du premier appel rencontré.
-- Aurélien REGAT-BARREL
Tou ce passe comme si tu avais :
template< typename > ITriplet {};
class Essai;
// Intanciation explicite
template class ITriplet< Essai >;
Mon incompréhension venait de là en fait je crois. Je pensais qu'à ce stade,
le compilo instanciait le template et générait toutes ses fonctions membres.
Mais si j'ai bien compris, il n'instancie (à ce stade) que la classe
ITriplet<Essai> au sens conteneur de données (struct C), et instancie ses
fonctions membres que si elles sont appelées, et au moment du premier appel
rencontré.
// Intanciation explicite template class ITriplet< Essai >;
Mon incompréhension venait de là en fait je crois. Je pensais qu'à ce stade, le compilo instanciait le template et générait toutes ses fonctions membres. Mais si j'ai bien compris, il n'instancie (à ce stade) que la classe ITriplet<Essai> au sens conteneur de données (struct C), et instancie ses fonctions membres que si elles sont appelées, et au moment du premier appel rencontré.
-- Aurélien REGAT-BARREL
Aurélien REGAT-BARREL
Je n'ai pas vraiment le temps de relire les articles en rapport avec tes triplets. Mais si je comprend bien, tu voudrais simplement faire des types différents, au point même de ne pas pouvoir les utiliser via un type de base, alors que les traitements et la structure sont identiques (mais pas la sémantique, donc).
Voilà. C'est un peu comme les algorithmes de la stl. On peut chercher le max d'un vector<int> et d'un vector<double> sans qu'il y ait de lien de parenté. Moi je veux faire mon vector, sauf que n'est pas le type qui est paramétré mais certaines fonctions membres.
C'était sur quoi j'étais resté avant de découvrir TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence fondamentale entre les 2. Ca doit être cette histoire d'héritage de soit même qui me perturbe. Mais j'ai le sentiment que la 2° solution est meilleure, ou plus élégante du moins. Peut être parce que j'ai vraiment un objet TypeSpécifique1 et non un alias + une deuxième classe TypeSpécifique1Tag. Je pense que c'est moins évident à écrire la classe TypeSpécifique1Tag + l'alias. Avec l'héritage, je pense qu'on comprend mieux que TypeSpécifique1 spécialise TypeFactorisé.
Tu peux toujours, le cas échéant, traiter les quelques exceptions par des spécialisations sur les différents tags.
Elle est peut être là la différence fondamentale. Si je veux ajouter une méthode que seul TypeSpécifique2 possède, je ne peux pas avec ton approche (qui était aussi le mienne ;-). Alors qu'avec l'héritage, je peux la rajouter sans problème :
Je pense que cela traduit mieux le fait d'implémenter l'interface TypeFactorisé, alors que la solution précédente on paramétrise cette interface = on n'a rien de plus que celle-ci. Si j'avais une deuxième interface à implémenter, je pourrais parfaitement le faire avec la 2° version :
Je n'ai pas vraiment le temps de relire les articles en rapport avec
tes triplets. Mais si je comprend bien, tu voudrais simplement faire
des types différents, au point même de ne pas pouvoir les utiliser via
un type de base, alors que les traitements et la structure sont
identiques (mais pas la sémantique, donc).
Voilà. C'est un peu comme les algorithmes de la stl. On peut chercher le max
d'un vector<int> et d'un vector<double> sans qu'il y ait de lien de parenté.
Moi je veux faire mon vector, sauf que n'est pas le type qui est paramétré
mais certaines fonctions membres.
C'était sur quoi j'étais resté avant de découvrir
TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence
fondamentale entre les 2. Ca doit être cette histoire d'héritage de soit
même qui me perturbe.
Mais j'ai le sentiment que la 2° solution est meilleure, ou plus élégante du
moins. Peut être parce que j'ai vraiment un objet TypeSpécifique1 et non un
alias + une deuxième classe TypeSpécifique1Tag. Je pense que c'est moins
évident à écrire la classe TypeSpécifique1Tag + l'alias. Avec l'héritage, je
pense qu'on comprend mieux que TypeSpécifique1 spécialise TypeFactorisé.
Tu peux toujours, le cas échéant, traiter les quelques exceptions
par des spécialisations sur les différents tags.
Elle est peut être là la différence fondamentale. Si je veux ajouter une
méthode que seul TypeSpécifique2 possède, je ne peux pas avec ton approche
(qui était aussi le mienne ;-). Alors qu'avec l'héritage, je peux la
rajouter sans problème :
Je pense que cela traduit mieux le fait d'implémenter l'interface
TypeFactorisé, alors que la solution précédente on paramétrise cette
interface = on n'a rien de plus que celle-ci.
Si j'avais une deuxième interface à implémenter, je pourrais parfaitement le
faire avec la 2° version :
Je n'ai pas vraiment le temps de relire les articles en rapport avec tes triplets. Mais si je comprend bien, tu voudrais simplement faire des types différents, au point même de ne pas pouvoir les utiliser via un type de base, alors que les traitements et la structure sont identiques (mais pas la sémantique, donc).
Voilà. C'est un peu comme les algorithmes de la stl. On peut chercher le max d'un vector<int> et d'un vector<double> sans qu'il y ait de lien de parenté. Moi je veux faire mon vector, sauf que n'est pas le type qui est paramétré mais certaines fonctions membres.
C'était sur quoi j'étais resté avant de découvrir TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence fondamentale entre les 2. Ca doit être cette histoire d'héritage de soit même qui me perturbe. Mais j'ai le sentiment que la 2° solution est meilleure, ou plus élégante du moins. Peut être parce que j'ai vraiment un objet TypeSpécifique1 et non un alias + une deuxième classe TypeSpécifique1Tag. Je pense que c'est moins évident à écrire la classe TypeSpécifique1Tag + l'alias. Avec l'héritage, je pense qu'on comprend mieux que TypeSpécifique1 spécialise TypeFactorisé.
Tu peux toujours, le cas échéant, traiter les quelques exceptions par des spécialisations sur les différents tags.
Elle est peut être là la différence fondamentale. Si je veux ajouter une méthode que seul TypeSpécifique2 possède, je ne peux pas avec ton approche (qui était aussi le mienne ;-). Alors qu'avec l'héritage, je peux la rajouter sans problème :
Je pense que cela traduit mieux le fait d'implémenter l'interface TypeFactorisé, alors que la solution précédente on paramétrise cette interface = on n'a rien de plus que celle-ci. Si j'avais une deuxième interface à implémenter, je pourrais parfaitement le faire avec la 2° version :
Je n'ai pas vraiment le temps de relire les articles en rapport avec tes triplets. Mais si je comprend bien, tu voudrais simplement faire des types différents, au point même de ne pas pouvoir les utiliser via un type de base, alors que les traitements et la structure sont identiques (mais pas la sémantique, donc).
Voilà. C'est un peu comme les algorithmes de la stl. On peut chercher le max d'un vector<int> et d'un vector<double> sans qu'il y ait de lien de parenté. Moi je veux faire mon vector, sauf que n'est pas le type qui est paramétré mais certaines fonctions membres.
C'était sur quoi j'étais resté avant de découvrir TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence fondamentale entre les 2.
Dans le premier cas, tu utilises des instanciations différentes de TypeFactorisé<>. Dans le second, tu définis des nouveaux types qui héritent d'instantiations différentes de TypeFactorisé<>.
Ca doit être cette histoire d'héritage de soit même qui me perturbe.
Non. On hérite ici d'un type différent, qui se trouve être l'instantiation d'un modèle avec pour paramètre effectif le type que l'on définit.
Mais j'ai le sentiment que la 2° solution est meilleure, ou plus élégante du moins. Peut être parce que j'ai vraiment un objet TypeSpécifique1 et non un alias + une deuxième classe TypeSpécifique1Tag. Je pense que c'est moins évident à écrire la classe TypeSpécifique1Tag + l'alias. Avec l'héritage, je pense qu'on comprend mieux que TypeSpécifique1 spécialise TypeFactorisé.
Tu peux toujours, le cas échéant, traiter les quelques exceptions par des spécialisations sur les différents tags.
Elle est peut être là la différence fondamentale. Si je veux ajouter une méthode que seul TypeSpécifique2 possède, je ne peux pas avec ton approche (qui était aussi le mienne ;-). Alors qu'avec l'héritage, je peux la rajouter sans problème :
Je n'ai pas vraiment le temps de relire les articles en rapport avec
tes triplets. Mais si je comprend bien, tu voudrais simplement faire
des types différents, au point même de ne pas pouvoir les utiliser via
un type de base, alors que les traitements et la structure sont
identiques (mais pas la sémantique, donc).
Voilà. C'est un peu comme les algorithmes de la stl. On peut chercher le max
d'un vector<int> et d'un vector<double> sans qu'il y ait de lien de parenté.
Moi je veux faire mon vector, sauf que n'est pas le type qui est paramétré
mais certaines fonctions membres.
C'était sur quoi j'étais resté avant de découvrir
TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence
fondamentale entre les 2.
Dans le premier cas, tu utilises des instanciations différentes de
TypeFactorisé<>. Dans le second, tu définis des nouveaux types qui
héritent d'instantiations différentes de TypeFactorisé<>.
Ca doit être cette histoire d'héritage de soit
même qui me perturbe.
Non. On hérite ici d'un type différent, qui se trouve être
l'instantiation d'un modèle avec pour paramètre effectif le type que
l'on définit.
Mais j'ai le sentiment que la 2° solution est meilleure, ou plus élégante du
moins. Peut être parce que j'ai vraiment un objet TypeSpécifique1 et non un
alias + une deuxième classe TypeSpécifique1Tag. Je pense que c'est moins
évident à écrire la classe TypeSpécifique1Tag + l'alias. Avec l'héritage, je
pense qu'on comprend mieux que TypeSpécifique1 spécialise TypeFactorisé.
Tu peux toujours, le cas échéant, traiter les quelques exceptions
par des spécialisations sur les différents tags.
Elle est peut être là la différence fondamentale. Si je veux ajouter une
méthode que seul TypeSpécifique2 possède, je ne peux pas avec ton approche
(qui était aussi le mienne ;-). Alors qu'avec l'héritage, je peux la
rajouter sans problème :
Je n'ai pas vraiment le temps de relire les articles en rapport avec tes triplets. Mais si je comprend bien, tu voudrais simplement faire des types différents, au point même de ne pas pouvoir les utiliser via un type de base, alors que les traitements et la structure sont identiques (mais pas la sémantique, donc).
Voilà. C'est un peu comme les algorithmes de la stl. On peut chercher le max d'un vector<int> et d'un vector<double> sans qu'il y ait de lien de parenté. Moi je veux faire mon vector, sauf que n'est pas le type qui est paramétré mais certaines fonctions membres.
C'était sur quoi j'étais resté avant de découvrir TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence fondamentale entre les 2.
Dans le premier cas, tu utilises des instanciations différentes de TypeFactorisé<>. Dans le second, tu définis des nouveaux types qui héritent d'instantiations différentes de TypeFactorisé<>.
Ca doit être cette histoire d'héritage de soit même qui me perturbe.
Non. On hérite ici d'un type différent, qui se trouve être l'instantiation d'un modèle avec pour paramètre effectif le type que l'on définit.
Mais j'ai le sentiment que la 2° solution est meilleure, ou plus élégante du moins. Peut être parce que j'ai vraiment un objet TypeSpécifique1 et non un alias + une deuxième classe TypeSpécifique1Tag. Je pense que c'est moins évident à écrire la classe TypeSpécifique1Tag + l'alias. Avec l'héritage, je pense qu'on comprend mieux que TypeSpécifique1 spécialise TypeFactorisé.
Tu peux toujours, le cas échéant, traiter les quelques exceptions par des spécialisations sur les différents tags.
Elle est peut être là la différence fondamentale. Si je veux ajouter une méthode que seul TypeSpécifique2 possède, je ne peux pas avec ton approche (qui était aussi le mienne ;-). Alors qu'avec l'héritage, je peux la rajouter sans problème :
Dans le premier cas, tu utilises des instanciations différentes de TypeFactorisé<>. Dans le second, tu définis des nouveaux types qui héritent d'instantiations différentes de TypeFactorisé<>.
En fait, je me demandais quelle était la réelle différence entre les 2 cas. Quand on sait rien de cela, qu'on manipule seulement le type qui en résulte, y'a-t-il de subtiles différences, ou ne peut-on pas faire la distinction (comportement vis à vis des autres types de triplets notamment...) ?
-- Aurélien REGAT-BARREL
Dans le premier cas, tu utilises des instanciations différentes de
TypeFactorisé<>. Dans le second, tu définis des nouveaux types qui
héritent d'instantiations différentes de TypeFactorisé<>.
En fait, je me demandais quelle était la réelle différence entre les 2 cas.
Quand on sait rien de cela, qu'on manipule seulement le type qui en résulte,
y'a-t-il de subtiles différences, ou ne peut-on pas faire la distinction
(comportement vis à vis des autres types de triplets notamment...) ?
Dans le premier cas, tu utilises des instanciations différentes de TypeFactorisé<>. Dans le second, tu définis des nouveaux types qui héritent d'instantiations différentes de TypeFactorisé<>.
En fait, je me demandais quelle était la réelle différence entre les 2 cas. Quand on sait rien de cela, qu'on manipule seulement le type qui en résulte, y'a-t-il de subtiles différences, ou ne peut-on pas faire la distinction (comportement vis à vis des autres types de triplets notamment...) ?
-- Aurélien REGAT-BARREL
kanze
"Aurélien REGAT-BARREL" wrote in message news:<40f2592d$0$25747$...
Je voudrais traduire une interface au sens pur, vraiment logique (ensemble de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc rabatu sur les templates.
C'est le problème d'avant, n'est-ce pas ?
Je pars en vacances demain ; je n'ai donc pas beaucoup de temps à y consacrer, et surtout, je ne serais pas disponible pour répondre aux questions sur ma réponse, mais compte tenu de que ce que tu dis ici et dans ta première réponse à Fabien, j'ai l'impression que ce qu'il te faut, c'est encore un template, cette fois-ci sur une classe « traits ».
Pour résumer, tu as un ensemble de classes dont toutes contiennent un vecteur de trois éléments, et dont le comportement ne diffère que par les transformations sur ces éléments dans le constructeur, et un libellé qui est rendu par une fonction statique. Alors, la partie commune du comportement doit se trouver dans un template, et la partie « customisable » dans une classe de traits qui sert de paramètre au template. À peu près :
template< typename Traits > class ConcreteTriplet { public: ConcreteTriplet( double A, double B, double C ) { Traits::initialize( myValues, A, B, C ) ; assert( myValues.size() == 3 ) ; }
double getValue( int i ) const { assert( i >= 0 && i < myValues.size() ) ; return myValues[ i ] ; }
L'implémentation trivial de ABCTraits::initialize serait :
void ABCTraits::initialize( std::vector< double >& dest, double A, double B double C ) { dest.push_back( A ) ; dest.push_back( B ) ; dest.push_back( C ) ; }
Mais évidemment, elle pourrait faire tout genre de transformation voulue, du moment qu'elle met bien trois valeurs dans dest.
Note que c'est une solution très ouverte. Tu n'exiges pas qu'on se sert de ConcreteTriplet pour l'implémentation d'un triplet ; seulement, s'il ne le fait pas, il faut qu'il sache ce qu'il fait.
Le but est de traduire un shéma UML où divers objets implémentent une interface ITriplet qui stipule que :
Je ne sais pas quel outil tu utilises pour les schéma UML, mais au moins avec Rose, il y a un support correct pour les templates, en tant que tels. Dans mon dernier projet, j'ai bien défini des trucs comme ci-dessus (un template sur traits) sans problèmes.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
"Aurélien REGAT-BARREL" <nospam-aregatba@yahoo.fr.invalid> wrote in
message news:<40f2592d$0$25747$626a14ce@news.free.fr>...
Je voudrais traduire une interface au sens pur, vraiment logique
(ensemble de méthodes). Je ne veux pas passer par l'héritage et les
classes abstraites de bases, car notamment je ne veux pas de
polymorphisme. Je me suis donc rabatu sur les templates.
C'est le problème d'avant, n'est-ce pas ?
Je pars en vacances demain ; je n'ai donc pas beaucoup de temps à y
consacrer, et surtout, je ne serais pas disponible pour répondre aux
questions sur ma réponse, mais compte tenu de que ce que tu dis ici et
dans ta première réponse à Fabien, j'ai l'impression que ce qu'il te
faut, c'est encore un template, cette fois-ci sur une classe « traits ».
Pour résumer, tu as un ensemble de classes dont toutes contiennent un
vecteur de trois éléments, et dont le comportement ne diffère que par
les transformations sur ces éléments dans le constructeur, et un libellé
qui est rendu par une fonction statique. Alors, la partie commune du
comportement doit se trouver dans un template, et la partie
« customisable » dans une classe de traits qui sert de paramètre au
template. À peu près :
template< typename Traits >
class ConcreteTriplet
{
public:
ConcreteTriplet( double A, double B, double C )
{
Traits::initialize( myValues, A, B, C ) ;
assert( myValues.size() == 3 ) ;
}
double getValue( int i ) const
{
assert( i >= 0 && i < myValues.size() ) ;
return myValues[ i ] ;
}
L'implémentation trivial de ABCTraits::initialize serait :
void
ABCTraits::initialize(
std::vector< double >& dest,
double A,
double B
double C )
{
dest.push_back( A ) ;
dest.push_back( B ) ;
dest.push_back( C ) ;
}
Mais évidemment, elle pourrait faire tout genre de transformation
voulue, du moment qu'elle met bien trois valeurs dans dest.
Note que c'est une solution très ouverte. Tu n'exiges pas qu'on se sert
de ConcreteTriplet pour l'implémentation d'un triplet ; seulement, s'il
ne le fait pas, il faut qu'il sache ce qu'il fait.
Le but est de traduire un shéma UML où divers objets implémentent une
interface ITriplet qui stipule que :
Je ne sais pas quel outil tu utilises pour les schéma UML, mais au moins
avec Rose, il y a un support correct pour les templates, en tant que
tels. Dans mon dernier projet, j'ai bien défini des trucs comme
ci-dessus (un template sur traits) sans problèmes.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
"Aurélien REGAT-BARREL" wrote in message news:<40f2592d$0$25747$...
Je voudrais traduire une interface au sens pur, vraiment logique (ensemble de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc rabatu sur les templates.
C'est le problème d'avant, n'est-ce pas ?
Je pars en vacances demain ; je n'ai donc pas beaucoup de temps à y consacrer, et surtout, je ne serais pas disponible pour répondre aux questions sur ma réponse, mais compte tenu de que ce que tu dis ici et dans ta première réponse à Fabien, j'ai l'impression que ce qu'il te faut, c'est encore un template, cette fois-ci sur une classe « traits ».
Pour résumer, tu as un ensemble de classes dont toutes contiennent un vecteur de trois éléments, et dont le comportement ne diffère que par les transformations sur ces éléments dans le constructeur, et un libellé qui est rendu par une fonction statique. Alors, la partie commune du comportement doit se trouver dans un template, et la partie « customisable » dans une classe de traits qui sert de paramètre au template. À peu près :
template< typename Traits > class ConcreteTriplet { public: ConcreteTriplet( double A, double B, double C ) { Traits::initialize( myValues, A, B, C ) ; assert( myValues.size() == 3 ) ; }
double getValue( int i ) const { assert( i >= 0 && i < myValues.size() ) ; return myValues[ i ] ; }
L'implémentation trivial de ABCTraits::initialize serait :
void ABCTraits::initialize( std::vector< double >& dest, double A, double B double C ) { dest.push_back( A ) ; dest.push_back( B ) ; dest.push_back( C ) ; }
Mais évidemment, elle pourrait faire tout genre de transformation voulue, du moment qu'elle met bien trois valeurs dans dest.
Note que c'est une solution très ouverte. Tu n'exiges pas qu'on se sert de ConcreteTriplet pour l'implémentation d'un triplet ; seulement, s'il ne le fait pas, il faut qu'il sache ce qu'il fait.
Le but est de traduire un shéma UML où divers objets implémentent une interface ITriplet qui stipule que :
Je ne sais pas quel outil tu utilises pour les schéma UML, mais au moins avec Rose, il y a un support correct pour les templates, en tant que tels. Dans mon dernier projet, j'ai bien défini des trucs comme ci-dessus (un template sur traits) sans problèmes.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
kanze
"Isammoc" wrote in message news:<ccusv2$64f$...
Je voudrais traduire une interface au sens pur, vraiment logique (ensemble de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc rabatu sur les templates.
Pourtant c'est la classe abstraite qui est le mode défini pour faire une interface en C++ si je ne m'abuse.
Pour une certaine définition d'interface. En fait, je crois que ce qu'il veut s'appelle plutôt un concepte. C'est une interface logique, mais sans substituabilité.
C'est + restrictif que le java, menfin...
Les classes abstraites de C++, plus restrictives que les interfaces en Java ? Alors là, tu m'étonnes.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
"Isammoc" <Isammoc@wanadoo.fr> wrote in message
news:<ccusv2$64f$1@news-reader5.wanadoo.fr>...
Je voudrais traduire une interface au sens pur, vraiment logique
(ensemble de méthodes). Je ne veux pas passer par l'héritage et les
classes abstraites de bases, car notamment je ne veux pas de
polymorphisme. Je me suis donc rabatu sur les templates.
Pourtant c'est la classe abstraite qui est le mode défini pour faire
une interface en C++ si je ne m'abuse.
Pour une certaine définition d'interface. En fait, je crois que ce qu'il
veut s'appelle plutôt un concepte. C'est une interface logique, mais
sans substituabilité.
C'est + restrictif que le java, menfin...
Les classes abstraites de C++, plus restrictives que les interfaces en
Java ? Alors là, tu m'étonnes.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Je voudrais traduire une interface au sens pur, vraiment logique (ensemble de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc rabatu sur les templates.
Pourtant c'est la classe abstraite qui est le mode défini pour faire une interface en C++ si je ne m'abuse.
Pour une certaine définition d'interface. En fait, je crois que ce qu'il veut s'appelle plutôt un concepte. C'est une interface logique, mais sans substituabilité.
C'est + restrictif que le java, menfin...
Les classes abstraites de C++, plus restrictives que les interfaces en Java ? Alors là, tu m'étonnes.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Fabien LE LEZ
On Tue, 13 Jul 2004 11:44:14 +0200, "Aurélien REGAT-BARREL" :
C'était sur quoi j'étais resté avant de découvrir TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence fondamentale entre les 2.
J'avais pensé d'emblée à la première, mais la seconde est plus facile à mettre en oeuvre, parce qu'on n'a pas à créer des identifiants uniques pour instancier le template -- le nom de la classe suffit.
On Tue, 13 Jul 2004 11:44:14 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba@yahoo.fr.invalid>:
C'était sur quoi j'étais resté avant de découvrir
TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence
fondamentale entre les 2.
J'avais pensé d'emblée à la première, mais la seconde est plus facile
à mettre en oeuvre, parce qu'on n'a pas à créer des identifiants
uniques pour instancier le template -- le nom de la classe suffit.
C'était sur quoi j'étais resté avant de découvrir TypeSpécifique1 : TypeFactorisé<TypeSpécifique1>
C'est assez proche, je ne saurais d'ailleurs pas dire la différence fondamentale entre les 2.
J'avais pensé d'emblée à la première, mais la seconde est plus facile à mettre en oeuvre, parce qu'on n'a pas à créer des identifiants uniques pour instancier le template -- le nom de la classe suffit.
Aurélien REGAT-BARREL
C'est le problème d'avant, n'est-ce pas ?
Oui.
Je pars en vacances demain ; je n'ai donc pas beaucoup de temps à y consacrer, et surtout, je ne serais pas disponible pour répondre aux questions sur ma réponse, mais compte tenu de que ce que tu dis ici et dans ta première réponse à Fabien, j'ai l'impression que ce qu'il te faut, c'est encore un template, cette fois-ci sur une classe « traits ».
Pour résumer, tu as un ensemble de classes dont toutes contiennent un vecteur de trois éléments, et dont le comportement ne diffère que par les transformations sur ces éléments dans le constructeur, et un libellé qui est rendu par une fonction statique. Alors, la partie commune du comportement doit se trouver dans un template, et la partie « customisable » dans une classe de traits qui sert de paramètre au template. À peu près :
C'est ce que je pensais faire, et ce que dkrm m'a proposé aussi. Mais j'aurais une exigence supplémentaire, pouvoir éventuellement ajouter des méthodes. Je ne suis pas encore sûr de devoir le faire, mais j'aimerais conserver cette option. Exemple bête justement : Tous les triplets doivent se construire à partir de A, B et C, y compris ABC (qui n'a du coup rien à faire). Je réfléchi à l'utilité d'un constructeur supplémentaire pour certains autres triplets qui utiliserait ABC. Je trouvais le coup du class ABC : public ConcreteTriplet<ABC> plus ouvert que typedef ConcreteTriplet< ABCTraits > ABC ; dans ce sens, et plus proche de la philosophie "implémente une interface" => je peux ainsi implémenter 2 interfaces, ce qui me parrait délicat avec l'approche "traits". Ce qui me gêne en revanche c'est le redondance :
dans le 2° cas, on a donc ABC::tripletName() et ABC::getTripletName()
Le but est de traduire un shéma UML où divers objets implémentent une interface ITriplet qui stipule que :
Je ne sais pas quel outil tu utilises pour les schéma UML, mais au moins avec Rose, il y a un support correct pour les templates, en tant que tels. Dans mon dernier projet, j'ai bien défini des trucs comme ci-dessus (un template sur traits) sans problèmes.
Lol. Je suis dans un toute petite boite avec très peu de moyens, j'utilise... Dia.
Bonnes vacances.
-- Aurélien REGAT-BARREL
C'est le problème d'avant, n'est-ce pas ?
Oui.
Je pars en vacances demain ; je n'ai donc pas beaucoup de temps à y
consacrer, et surtout, je ne serais pas disponible pour répondre aux
questions sur ma réponse, mais compte tenu de que ce que tu dis ici et
dans ta première réponse à Fabien, j'ai l'impression que ce qu'il te
faut, c'est encore un template, cette fois-ci sur une classe « traits ».
Pour résumer, tu as un ensemble de classes dont toutes contiennent un
vecteur de trois éléments, et dont le comportement ne diffère que par
les transformations sur ces éléments dans le constructeur, et un libellé
qui est rendu par une fonction statique. Alors, la partie commune du
comportement doit se trouver dans un template, et la partie
« customisable » dans une classe de traits qui sert de paramètre au
template. À peu près :
C'est ce que je pensais faire, et ce que dkrm m'a proposé aussi.
Mais j'aurais une exigence supplémentaire, pouvoir éventuellement ajouter
des méthodes. Je ne suis pas encore sûr de devoir le faire, mais j'aimerais
conserver cette option. Exemple bête justement :
Tous les triplets doivent se construire à partir de A, B et C, y compris ABC
(qui n'a du coup rien à faire).
Je réfléchi à l'utilité d'un constructeur supplémentaire pour certains
autres triplets qui utiliserait ABC.
Je trouvais le coup du
class ABC : public ConcreteTriplet<ABC>
plus ouvert que
typedef ConcreteTriplet< ABCTraits > ABC ;
dans ce sens, et plus proche de la philosophie "implémente une interface" =>
je peux ainsi implémenter 2 interfaces, ce qui me parrait délicat avec
l'approche "traits".
Ce qui me gêne en revanche c'est le redondance :
dans le 2° cas, on a donc
ABC::tripletName()
et
ABC::getTripletName()
Le but est de traduire un shéma UML où divers objets implémentent une
interface ITriplet qui stipule que :
Je ne sais pas quel outil tu utilises pour les schéma UML, mais au moins
avec Rose, il y a un support correct pour les templates, en tant que
tels. Dans mon dernier projet, j'ai bien défini des trucs comme
ci-dessus (un template sur traits) sans problèmes.
Lol. Je suis dans un toute petite boite avec très peu de moyens,
j'utilise... Dia.
Je pars en vacances demain ; je n'ai donc pas beaucoup de temps à y consacrer, et surtout, je ne serais pas disponible pour répondre aux questions sur ma réponse, mais compte tenu de que ce que tu dis ici et dans ta première réponse à Fabien, j'ai l'impression que ce qu'il te faut, c'est encore un template, cette fois-ci sur une classe « traits ».
Pour résumer, tu as un ensemble de classes dont toutes contiennent un vecteur de trois éléments, et dont le comportement ne diffère que par les transformations sur ces éléments dans le constructeur, et un libellé qui est rendu par une fonction statique. Alors, la partie commune du comportement doit se trouver dans un template, et la partie « customisable » dans une classe de traits qui sert de paramètre au template. À peu près :
C'est ce que je pensais faire, et ce que dkrm m'a proposé aussi. Mais j'aurais une exigence supplémentaire, pouvoir éventuellement ajouter des méthodes. Je ne suis pas encore sûr de devoir le faire, mais j'aimerais conserver cette option. Exemple bête justement : Tous les triplets doivent se construire à partir de A, B et C, y compris ABC (qui n'a du coup rien à faire). Je réfléchi à l'utilité d'un constructeur supplémentaire pour certains autres triplets qui utiliserait ABC. Je trouvais le coup du class ABC : public ConcreteTriplet<ABC> plus ouvert que typedef ConcreteTriplet< ABCTraits > ABC ; dans ce sens, et plus proche de la philosophie "implémente une interface" => je peux ainsi implémenter 2 interfaces, ce qui me parrait délicat avec l'approche "traits". Ce qui me gêne en revanche c'est le redondance :
dans le 2° cas, on a donc ABC::tripletName() et ABC::getTripletName()
Le but est de traduire un shéma UML où divers objets implémentent une interface ITriplet qui stipule que :
Je ne sais pas quel outil tu utilises pour les schéma UML, mais au moins avec Rose, il y a un support correct pour les templates, en tant que tels. Dans mon dernier projet, j'ai bien défini des trucs comme ci-dessus (un template sur traits) sans problèmes.
Lol. Je suis dans un toute petite boite avec très peu de moyens, j'utilise... Dia.
Bonnes vacances.
-- Aurélien REGAT-BARREL
Aurélien REGAT-BARREL
J'avais pensé d'emblée à la première, mais la seconde est plus facile à mettre en oeuvre, parce qu'on n'a pas à créer des identifiants uniques pour instancier le template -- le nom de la classe suffit.
Je suis d'accord. Par contre une chose me gêne : la redondance.
class ABC : public ITriplet<ABC> { public: static const char TripletName[]; }; const char ABC::TripletName[] = "A.B.C";
J'ai donc ABC::TripletName et ABC::GetTripletName()... :-/
-- Aurélien REGAT-BARREL
J'avais pensé d'emblée à la première, mais la seconde est plus facile
à mettre en oeuvre, parce qu'on n'a pas à créer des identifiants
uniques pour instancier le template -- le nom de la classe suffit.
Je suis d'accord. Par contre une chose me gêne : la redondance.
J'avais pensé d'emblée à la première, mais la seconde est plus facile à mettre en oeuvre, parce qu'on n'a pas à créer des identifiants uniques pour instancier le template -- le nom de la classe suffit.
Je suis d'accord. Par contre une chose me gêne : la redondance.