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.
On Mon, 12 Jul 2004 11:30:44 +0200, "Aurélien REGAT-BARREL" :
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.
Houlà... Donc, tu ne veux ni héritage, ni fonctions virtuelles, juste t'assurer 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
C'est bien ça ?
Je pense que le mieux est de ne pas créer de code, mais juste mettre dans la doc que la classe créée doit avoir ces fonctions.
Eventuellement, avec certaines fonctions de Boost (cherche du côté de <http://www.boost.org/libs/libraries.htm#Correctness> ou <http://www.boost.org/libs/libraries.htm#Generic>), tu devrais pouvoir créer une macro "VERIFIER_APPARTENANCE_A_ITriplet(nom_classe)" qui refuse la compilation si une des fonctions obligatoires est absente.
On Mon, 12 Jul 2004 11:30:44 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba@yahoo.fr.invalid>:
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.
Houlà... Donc, tu ne veux ni héritage, ni fonctions virtuelles, juste
t'assurer 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
C'est bien ça ?
Je pense que le mieux est de ne pas créer de code, mais juste mettre
dans la doc que la classe créée doit avoir ces fonctions.
Eventuellement, avec certaines fonctions de Boost (cherche du côté de
<http://www.boost.org/libs/libraries.htm#Correctness> ou
<http://www.boost.org/libs/libraries.htm#Generic>), tu devrais pouvoir
créer une macro "VERIFIER_APPARTENANCE_A_ITriplet(nom_classe)" qui
refuse la compilation si une des fonctions obligatoires est absente.
On Mon, 12 Jul 2004 11:30:44 +0200, "Aurélien REGAT-BARREL" :
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.
Houlà... Donc, tu ne veux ni héritage, ni fonctions virtuelles, juste t'assurer 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
C'est bien ça ?
Je pense que le mieux est de ne pas créer de code, mais juste mettre dans la doc que la classe créée doit avoir ces fonctions.
Eventuellement, avec certaines fonctions de Boost (cherche du côté de <http://www.boost.org/libs/libraries.htm#Correctness> ou <http://www.boost.org/libs/libraries.htm#Generic>), tu devrais pouvoir créer une macro "VERIFIER_APPARTENANCE_A_ITriplet(nom_classe)" qui refuse la compilation si une des fonctions obligatoires est absente.
Aurélien REGAT-BARREL
Houlà... Donc, tu ne veux ni héritage, ni fonctions virtuelles, juste t'assurer 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
C'est bien ça ?
C'est tout à fait ça.
Je pense que le mieux est de ne pas créer de code, mais juste mettre dans la doc que la classe créée doit avoir ces fonctions.
La doc se présente entre autre sous forme d'un diagramme des classes UML qui définit cette interface. Il y aura bien un bout de texte qui va en parler, mais je voudrais rendre mon code le plus parlant possible.
Eventuellement, avec certaines fonctions de Boost (cherche du côté de <http://www.boost.org/libs/libraries.htm#Correctness> ou <http://www.boost.org/libs/libraries.htm#Generic>), tu devrais pouvoir créer une macro "VERIFIER_APPARTENANCE_A_ITriplet(nom_classe)" qui refuse la compilation si une des fonctions obligatoires est absente.
C'était mon idée n° 3, fournir une fonction templétée de vérification. Mais en fait y'a juste une chose qui me dérange : la moitié du code entre ces classes est identique : accéder aux valeurs des triplets, accéder au nom, accéder au nom de chaque triplet,... d'où mon idée n°2. ITriplet n'est rien d'autre qu'un conteneur, seul le contenu varie entre les différents triplets. C'est un peu comme si je voulais définir une classe vector avec une fonction GetName() et un constructeur personnalisé... Car en fin de compte seul le code du constructeur diffère ainsi que la valeur des chaines renvoyées. Je vais regarder boost. Merci.
-- Aurélien REGAT-BARREL
Houlà... Donc, tu ne veux ni héritage, ni fonctions virtuelles, juste
t'assurer 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
C'est bien ça ?
C'est tout à fait ça.
Je pense que le mieux est de ne pas créer de code, mais juste mettre
dans la doc que la classe créée doit avoir ces fonctions.
La doc se présente entre autre sous forme d'un diagramme des classes UML qui
définit cette interface. Il y aura bien un bout de texte qui va en parler,
mais je voudrais rendre mon code le plus parlant possible.
Eventuellement, avec certaines fonctions de Boost (cherche du côté de
<http://www.boost.org/libs/libraries.htm#Correctness> ou
<http://www.boost.org/libs/libraries.htm#Generic>), tu devrais pouvoir
créer une macro "VERIFIER_APPARTENANCE_A_ITriplet(nom_classe)" qui
refuse la compilation si une des fonctions obligatoires est absente.
C'était mon idée n° 3, fournir une fonction templétée de vérification.
Mais en fait y'a juste une chose qui me dérange : la moitié du code entre
ces classes est identique : accéder aux valeurs des triplets, accéder au
nom, accéder au nom de chaque triplet,... d'où mon idée n°2.
ITriplet n'est rien d'autre qu'un conteneur, seul le contenu varie entre les
différents triplets. C'est un peu comme si je voulais définir une classe
vector avec une fonction GetName() et un constructeur personnalisé...
Car en fin de compte seul le code du constructeur diffère ainsi que la
valeur des chaines renvoyées.
Je vais regarder boost. Merci.
Houlà... Donc, tu ne veux ni héritage, ni fonctions virtuelles, juste t'assurer 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
C'est bien ça ?
C'est tout à fait ça.
Je pense que le mieux est de ne pas créer de code, mais juste mettre dans la doc que la classe créée doit avoir ces fonctions.
La doc se présente entre autre sous forme d'un diagramme des classes UML qui définit cette interface. Il y aura bien un bout de texte qui va en parler, mais je voudrais rendre mon code le plus parlant possible.
Eventuellement, avec certaines fonctions de Boost (cherche du côté de <http://www.boost.org/libs/libraries.htm#Correctness> ou <http://www.boost.org/libs/libraries.htm#Generic>), tu devrais pouvoir créer une macro "VERIFIER_APPARTENANCE_A_ITriplet(nom_classe)" qui refuse la compilation si une des fonctions obligatoires est absente.
C'était mon idée n° 3, fournir une fonction templétée de vérification. Mais en fait y'a juste une chose qui me dérange : la moitié du code entre ces classes est identique : accéder aux valeurs des triplets, accéder au nom, accéder au nom de chaque triplet,... d'où mon idée n°2. ITriplet n'est rien d'autre qu'un conteneur, seul le contenu varie entre les différents triplets. C'est un peu comme si je voulais définir une classe vector avec une fonction GetName() et un constructeur personnalisé... Car en fin de compte seul le code du constructeur diffère ainsi que la valeur des chaines renvoyées. Je vais regarder boost. Merci.
-- Aurélien REGAT-BARREL
Fabien LE LEZ
On Mon, 12 Jul 2004 12:13:48 +0200, "Aurélien REGAT-BARREL" :
Mais en fait y'a juste une chose qui me dérange : la moitié du code entre ces classes est identique : accéder aux valeurs des triplets, accéder au nom, accéder au nom de chaque triplet,... d'où mon idée n°2. [...] C'est un peu comme si je voulais définir une classe vector avec une fonction GetName() et un constructeur personnalisé...
Ben oui, en C++ y'a un outil tout fait pour ça : l'héritage ;-)
Note que si tu peux utiliser l'héritage, mais sans qu'une classe soit "indirectement convertible" en une autre, y'a un moyen :
template <char const* nom_classe> /* Pas sûr de moi, sur ce coup-là, mais on doit pouvoir s'en sortir d'une façon ou d'une autre... */ class ITriplet { public: ITriplet (double a, double b, double c) { data[0]= a; data[1]= b; data[2]= c; } static std::string GetTripletName() { return nom_classe; } double GetValue (int index) const { if (index>=0 && index<NB_VALEURS) return data[i]; throw "Erreur ITriplet" + GetTripletName() + "::GetValue"; } protected: enum { NB_VALEURS= 3 }; double data[NB_VALEURS]; /* Pas besoin d'un vector<> si on connaît la taille à la compilation ;-) */ };
class MontripletMachin: public ITriplet <"MontripletMachin"> { //... };
On Mon, 12 Jul 2004 12:13:48 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba@yahoo.fr.invalid>:
Mais en fait y'a juste une chose qui me dérange : la moitié du code entre
ces classes est identique : accéder aux valeurs des triplets, accéder au
nom, accéder au nom de chaque triplet,... d'où mon idée n°2.
[...]
C'est un peu comme si je voulais définir une classe
vector avec une fonction GetName() et un constructeur personnalisé...
Ben oui, en C++ y'a un outil tout fait pour ça : l'héritage ;-)
Note que si tu peux utiliser l'héritage, mais sans qu'une classe soit
"indirectement convertible" en une autre, y'a un moyen :
template <char const* nom_classe> /* Pas sûr de moi, sur ce coup-là,
mais on doit pouvoir s'en sortir d'une façon ou d'une autre... */
class ITriplet
{
public:
ITriplet (double a, double b, double c)
{ data[0]= a; data[1]= b; data[2]= c; }
static std::string GetTripletName() { return nom_classe; }
double GetValue (int index) const
{
if (index>=0 && index<NB_VALEURS) return data[i];
throw "Erreur ITriplet" + GetTripletName() + "::GetValue";
}
protected:
enum { NB_VALEURS= 3 };
double data[NB_VALEURS]; /* Pas besoin d'un vector<> si on connaît
la taille à la compilation ;-) */
};
class MontripletMachin: public ITriplet <"MontripletMachin">
{
//...
};
On Mon, 12 Jul 2004 12:13:48 +0200, "Aurélien REGAT-BARREL" :
Mais en fait y'a juste une chose qui me dérange : la moitié du code entre ces classes est identique : accéder aux valeurs des triplets, accéder au nom, accéder au nom de chaque triplet,... d'où mon idée n°2. [...] C'est un peu comme si je voulais définir une classe vector avec une fonction GetName() et un constructeur personnalisé...
Ben oui, en C++ y'a un outil tout fait pour ça : l'héritage ;-)
Note que si tu peux utiliser l'héritage, mais sans qu'une classe soit "indirectement convertible" en une autre, y'a un moyen :
template <char const* nom_classe> /* Pas sûr de moi, sur ce coup-là, mais on doit pouvoir s'en sortir d'une façon ou d'une autre... */ class ITriplet { public: ITriplet (double a, double b, double c) { data[0]= a; data[1]= b; data[2]= c; } static std::string GetTripletName() { return nom_classe; } double GetValue (int index) const { if (index>=0 && index<NB_VALEURS) return data[i]; throw "Erreur ITriplet" + GetTripletName() + "::GetValue"; } protected: enum { NB_VALEURS= 3 }; double data[NB_VALEURS]; /* Pas besoin d'un vector<> si on connaît la taille à la compilation ;-) */ };
class MontripletMachin: public ITriplet <"MontripletMachin"> { //... };
Aurélien REGAT-BARREL
Ben oui, en C++ y'a un outil tout fait pour ça : l'héritage ;-)
Note que si tu peux utiliser l'héritage, mais sans qu'une classe soit "indirectement convertible" en une autre, y'a un moyen :
C'est surtout le polymorphisme qui me gêne.
template <char const* nom_classe> /* Pas sûr de moi, sur ce coup-là, mais on doit pouvoir s'en sortir d'une façon ou d'une autre... */ class ITriplet { public: ITriplet (double a, double b, double c) { data[0]= a; data[1]= b; data[2]= c; } static std::string GetTripletName() { return nom_classe; } double GetValue (int index) const { if (index>=0 && index<NB_VALEURS) return data[i]; throw "Erreur ITriplet" + GetTripletName() + "::GetValue"; } protected: enum { NB_VALEURS= 3 }; double data[NB_VALEURS]; /* Pas besoin d'un vector<> si on connaît la taille à la compilation ;-) */ };
class MontripletMachin: public ITriplet <"MontripletMachin"> { //... };
char * directement c'est pas possible, d'où le recours à un functor GetTripletName() dans mon exemple 2. Donc ta solution n'est pas possible. Sans compter qu'il faut aussi customiser le constructeur, et encore d'autres trucs que j'ai pas mis (GetValueName, ...). Actuellement je penche pour une solution comme ceci :
// interface ITriplet template<typename T> class ITriplet { public: ITriplet( double A, double B, double C ) : data( T::Compute( A, B, C ) ) { } double GetValue( int Index ) const { return this->data.at( Index ); } static std::string GetTripletName() { return T::GetName(); } private: std::vector<double> data; };
// partie spécifique du triplet ABC struct ABCImpl { static std::vector<double> Compute( 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; } static std::string GetName() { return "A.B.C"; } };
// triplet ABC typedef ITriplet<ABCImpl> ABC;
D'un côte le template qui définit l'interface (donc on est assez proche du diagramme UML) + tout ce qui est redondant (stockage des triplets, accès aux valeurs, ...) et de l'autre la partie spécifique à chaque triplet sous forme d'une struct passée en paramètre du template ITriplet... C'est un peu le principe du pattern façade, le template ITriplet jouant le rôle de... l'interface.
-- Aurélien REGAT-BARREL
Ben oui, en C++ y'a un outil tout fait pour ça : l'héritage ;-)
Note que si tu peux utiliser l'héritage, mais sans qu'une classe soit
"indirectement convertible" en une autre, y'a un moyen :
C'est surtout le polymorphisme qui me gêne.
template <char const* nom_classe> /* Pas sûr de moi, sur ce coup-là,
mais on doit pouvoir s'en sortir d'une façon ou d'une autre... */
class ITriplet
{
public:
ITriplet (double a, double b, double c)
{ data[0]= a; data[1]= b; data[2]= c; }
static std::string GetTripletName() { return nom_classe; }
double GetValue (int index) const
{
if (index>=0 && index<NB_VALEURS) return data[i];
throw "Erreur ITriplet" + GetTripletName() + "::GetValue";
}
protected:
enum { NB_VALEURS= 3 };
double data[NB_VALEURS]; /* Pas besoin d'un vector<> si on connaît
la taille à la compilation ;-) */
};
class MontripletMachin: public ITriplet <"MontripletMachin">
{
//...
};
char * directement c'est pas possible, d'où le recours à un functor
GetTripletName() dans mon exemple 2.
Donc ta solution n'est pas possible. Sans compter qu'il faut aussi
customiser le constructeur, et encore d'autres trucs que j'ai pas mis
(GetValueName, ...).
Actuellement je penche pour une solution comme ceci :
// interface ITriplet
template<typename T>
class ITriplet
{
public:
ITriplet( double A, double B, double C ) :
data( T::Compute( A, B, C ) )
{
}
double GetValue( int Index ) const
{
return this->data.at( Index );
}
static std::string GetTripletName()
{
return T::GetName();
}
private:
std::vector<double> data;
};
// partie spécifique du triplet ABC
struct ABCImpl
{
static std::vector<double> Compute( 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;
}
static std::string GetName()
{
return "A.B.C";
}
};
// triplet ABC
typedef ITriplet<ABCImpl> ABC;
D'un côte le template qui définit l'interface (donc on est assez proche du
diagramme UML) + tout ce qui est redondant (stockage des triplets, accès aux
valeurs, ...) et de l'autre la partie spécifique à chaque triplet sous forme
d'une struct passée en paramètre du template ITriplet... C'est un peu le
principe du pattern façade, le template ITriplet jouant le rôle de...
l'interface.
Ben oui, en C++ y'a un outil tout fait pour ça : l'héritage ;-)
Note que si tu peux utiliser l'héritage, mais sans qu'une classe soit "indirectement convertible" en une autre, y'a un moyen :
C'est surtout le polymorphisme qui me gêne.
template <char const* nom_classe> /* Pas sûr de moi, sur ce coup-là, mais on doit pouvoir s'en sortir d'une façon ou d'une autre... */ class ITriplet { public: ITriplet (double a, double b, double c) { data[0]= a; data[1]= b; data[2]= c; } static std::string GetTripletName() { return nom_classe; } double GetValue (int index) const { if (index>=0 && index<NB_VALEURS) return data[i]; throw "Erreur ITriplet" + GetTripletName() + "::GetValue"; } protected: enum { NB_VALEURS= 3 }; double data[NB_VALEURS]; /* Pas besoin d'un vector<> si on connaît la taille à la compilation ;-) */ };
class MontripletMachin: public ITriplet <"MontripletMachin"> { //... };
char * directement c'est pas possible, d'où le recours à un functor GetTripletName() dans mon exemple 2. Donc ta solution n'est pas possible. Sans compter qu'il faut aussi customiser le constructeur, et encore d'autres trucs que j'ai pas mis (GetValueName, ...). Actuellement je penche pour une solution comme ceci :
// interface ITriplet template<typename T> class ITriplet { public: ITriplet( double A, double B, double C ) : data( T::Compute( A, B, C ) ) { } double GetValue( int Index ) const { return this->data.at( Index ); } static std::string GetTripletName() { return T::GetName(); } private: std::vector<double> data; };
// partie spécifique du triplet ABC struct ABCImpl { static std::vector<double> Compute( 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; } static std::string GetName() { return "A.B.C"; } };
// triplet ABC typedef ITriplet<ABCImpl> ABC;
D'un côte le template qui définit l'interface (donc on est assez proche du diagramme UML) + tout ce qui est redondant (stockage des triplets, accès aux valeurs, ...) et de l'autre la partie spécifique à chaque triplet sous forme d'une struct passée en paramètre du template ITriplet... C'est un peu le principe du pattern façade, le template ITriplet jouant le rôle de... l'interface.
On Mon, 12 Jul 2004 14:12:54 +0200, "Aurélien REGAT-BARREL" :
static std::vector<double> Compute( 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;
Pas bon, puisque tu dois retaper ce code à chaque fois. Au moins fais-en une fonction libre !
Aurélien REGAT-BARREL
static std::vector<double> Compute( 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;
Pas bon, puisque tu dois retaper ce code à chaque fois. Au moins fais-en une fonction libre !
Je n'ai peut être pas été clair sur ce point. Chaque triplet se calcule à partir de A, B et C. ABC est à part car il n'a rien à calculer. Exemple avec le triplet DEF :
// A, B, C en pourcentages static std::vector<double> Compute( double A, double B, double C ) { std::vector<double> result; double total = A + B + C; result.reserve( 3 ); result.push_back( A / total ); result.push_back( B / total ); result.push_back( C / total ); return result; }
On comprend mieux pourquoi il faut pas confondre / mélanger les triplets...
-- Aurélien REGAT-BARREL
static std::vector<double> Compute( 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;
Pas bon, puisque tu dois retaper ce code à chaque fois.
Au moins fais-en une fonction libre !
Je n'ai peut être pas été clair sur ce point. Chaque triplet se calcule à
partir de A, B et C. ABC est à part car il n'a rien à calculer.
Exemple avec le triplet DEF :
// A, B, C en pourcentages
static std::vector<double> Compute( double A, double B, double C )
{
std::vector<double> result;
double total = A + B + C;
result.reserve( 3 );
result.push_back( A / total );
result.push_back( B / total );
result.push_back( C / total );
return result;
}
On comprend mieux pourquoi il faut pas confondre / mélanger les triplets...
static std::vector<double> Compute( 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;
Pas bon, puisque tu dois retaper ce code à chaque fois. Au moins fais-en une fonction libre !
Je n'ai peut être pas été clair sur ce point. Chaque triplet se calcule à partir de A, B et C. ABC est à part car il n'a rien à calculer. Exemple avec le triplet DEF :
// A, B, C en pourcentages static std::vector<double> Compute( double A, double B, double C ) { std::vector<double> result; double total = A + B + C; result.reserve( 3 ); result.push_back( A / total ); result.push_back( B / total ); result.push_back( C / total ); return result; }
On comprend mieux pourquoi il faut pas confondre / mélanger les triplets...
class Essai: public ITriplet<Essai> { public: Essai (double a, double b, double c) : ITriplet<Essai> (a,b,c) {} static char const * GetName() { return "Essai"; } };
Ohhhhhh... On peut faire ça en C++... J'étais pas loin, j'avais pensé à :
// 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 class ABC : public ITriplet<ABC::Compute, ABC::GetName> { protected: static std::vector<double> Compute( 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; } static std::string GetName() { return "A.B.C"; } };
Mais c'est le serpent qui se mord la queue, j'utilise ABC pour définir ABC. Du coup je pige pas pourquoi ton exemple fonctionne, mais je crois que c'est pile ce qu'il me fallait. Manquerait plus qu'à comprendre comment je peux instancier un template à partir d'un type qui n'est pas encore défini, afin d'hériter de ce type instancié et donc finalement hériter de soit même... |-o Comment ça marche ?
class Essai: public ITriplet<Essai>
{
public:
Essai (double a, double b, double c)
: ITriplet<Essai> (a,b,c) {}
static char const * GetName() { return "Essai"; }
};
Ohhhhhh...
On peut faire ça en C++...
J'étais pas loin, j'avais pensé à :
// 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
class ABC : public ITriplet<ABC::Compute, ABC::GetName>
{
protected:
static std::vector<double> Compute( 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;
}
static std::string GetName()
{
return "A.B.C";
}
};
Mais c'est le serpent qui se mord la queue, j'utilise ABC pour définir ABC.
Du coup je pige pas pourquoi ton exemple fonctionne, mais je crois que c'est
pile ce qu'il me fallait. Manquerait plus qu'à comprendre comment je peux
instancier un template à partir d'un type qui n'est pas encore défini, afin
d'hériter de ce type instancié et donc finalement hériter de soit même...
|-o
Comment ça marche ?
class Essai: public ITriplet<Essai> { public: Essai (double a, double b, double c) : ITriplet<Essai> (a,b,c) {} static char const * GetName() { return "Essai"; } };
Ohhhhhh... On peut faire ça en C++... J'étais pas loin, j'avais pensé à :
// 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 class ABC : public ITriplet<ABC::Compute, ABC::GetName> { protected: static std::vector<double> Compute( 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; } static std::string GetName() { return "A.B.C"; } };
Mais c'est le serpent qui se mord la queue, j'utilise ABC pour définir ABC. Du coup je pige pas pourquoi ton exemple fonctionne, mais je crois que c'est pile ce qu'il me fallait. Manquerait plus qu'à comprendre comment je peux instancier un template à partir d'un type qui n'est pas encore défini, afin d'hériter de ce type instancié et donc finalement hériter de soit même... |-o Comment ça marche ?
-- Aurélien REGAT-BARREL
Fabien LE LEZ
On Mon, 12 Jul 2004 15:16:20 +0200, "Aurélien REGAT-BARREL" :
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au début de la ligne).
Au début j'avais écrit
class ABC; class ABC: public ITriplet<ABC> ...
mais finalement la forward-declaration semble inutile.
On Mon, 12 Jul 2004 15:16:20 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba@yahoo.fr.invalid>:
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au
début de la ligne).
Au début j'avais écrit
class ABC;
class ABC: public ITriplet<ABC> ...
mais finalement la forward-declaration semble inutile.
On Mon, 12 Jul 2004 15:16:20 +0200, "Aurélien REGAT-BARREL" :
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au début de la ligne).
Au début j'avais écrit
class ABC; class ABC: public ITriplet<ABC> ...
mais finalement la forward-declaration semble inutile.
Aurélien REGAT-BARREL
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au début de la ligne).
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être instancié avec ABC, or ABC n'est pas encore implémenté... C'est comme faire (pour ce que j'ai compris) :
class A; class B : public A { }; class A { };
sauf que là ça marche... pour moi les templates ça marche un peu comme les macros ( ~ génération de code), mais apparement c'est pas le cas. J'ai toujours pas compris :-/
-- Aurélien REGAT-BARREL
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au
début de la ligne).
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être
instancié avec ABC, or ABC n'est pas encore implémenté...
C'est comme faire (pour ce que j'ai compris) :
class A;
class B : public A
{
};
class A
{
};
sauf que là ça marche... pour moi les templates ça marche un peu comme les
macros ( ~ génération de code), mais apparement c'est pas le cas.
J'ai toujours pas compris :-/
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au début de la ligne).
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être instancié avec ABC, or ABC n'est pas encore implémenté... C'est comme faire (pour ce que j'ai compris) :
class A; class B : public A { }; class A { };
sauf que là ça marche... pour moi les templates ça marche un peu comme les macros ( ~ génération de code), mais apparement c'est pas le cas. J'ai toujours pas compris :-/