 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Aurélien REGAT-BARREL Guest
|
Posted: Mon Jul 12, 2004 9:30 am Post subject: Definition d'une interface (a la Java) a l'aide de templates |
|
|
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)
#define INTERFACE(x) /**/
#define IMPLEMENTS(x) /**/
#define WHERE(x) /**/
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.
--
Aurélien REGAT-BARREL
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Mon Jul 12, 2004 9:56 am Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
On Mon, 12 Jul 2004 11:30:44 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba (AT) yahoo (DOT) fr.invalid>:
| Quote: | 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 :
| Quote: | - 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.
|
|
| Back to top |
|
 |
Aurélien REGAT-BARREL Guest
|
Posted: Mon Jul 12, 2004 10:13 am Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
| Quote: | 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.
| Quote: | 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.
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
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Mon Jul 12, 2004 11:02 am Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
On Mon, 12 Jul 2004 12:13:48 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba (AT) yahoo (DOT) fr.invalid>:
| Quote: | 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">
{
//...
};
|
|
| Back to top |
|
 |
Aurélien REGAT-BARREL Guest
|
Posted: Mon Jul 12, 2004 12:12 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
| Quote: | 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.
| Quote: | 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
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
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Mon Jul 12, 2004 12:33 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
Deuxième essai :
template <class T> class ITriplet
{
public:
double GetValue (int index) const
{
if (index<0 || index>=NB_VALEURS)
throw "Erreur ITriplet::GetValue";
return data[index];
}
protected:
enum { NB_VALEURS= 3 };
double data[NB_VALEURS];
ITriplet (double a, double b, double c)
{ data[0]= a; data[1]= b; data[2]= c; }
};
class Essai: public ITriplet<Essai>
{
public:
Essai (double a, double b, double c)
: ITriplet<Essai> (a,b,c) {}
static char const * GetName() { return "Essai"; }
};
class Essai2: public ITriplet<Essai>
{
public:
Essai2 (double a, double b, double c)
: ITriplet<Essai> (a,b,c) {}
static char const * GetName() { return "Essai2"; }
};
Essai et Essai2 n'ont aucun parent commun.
De plus, il n'y a pas de polymorphisme et pas de virtualité.
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Mon Jul 12, 2004 12:34 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
On Mon, 12 Jul 2004 14:12:54 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba (AT) yahoo (DOT) fr.invalid>:
| Quote: | 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 !
|
|
| Back to top |
|
 |
Aurélien REGAT-BARREL Guest
|
Posted: Mon Jul 12, 2004 1:07 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
| Quote: | 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
|
|
| Back to top |
|
 |
Aurélien REGAT-BARREL Guest
|
Posted: Mon Jul 12, 2004 1:16 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
| Quote: | Deuxième essai :
template <class T> class ITriplet
{
public:
double GetValue (int index) const
{
if (index<0 || index>=NB_VALEURS)
throw "Erreur ITriplet::GetValue";
return data[index];
}
protected:
enum { NB_VALEURS= 3 };
double data[NB_VALEURS];
ITriplet (double a, double b, double c)
{ data[0]= a; data[1]= b; data[2]= c; }
};
class Essai: public ITriplet<Essai
{
public:
Essai (double a, double b, double c)
: ITriplet
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...
| Quote: | -o
Comment ça marche ? |
--
Aurélien REGAT-BARREL
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Mon Jul 12, 2004 1:25 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
On Mon, 12 Jul 2004 15:16:20 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba (AT) yahoo (DOT) fr.invalid>:
| Quote: | 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.
|
|
| Back to top |
|
 |
Aurélien REGAT-BARREL Guest
|
Posted: Mon Jul 12, 2004 1:44 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
| Quote: | 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
|
|
| Back to top |
|
 |
Franck Branjonneau Guest
|
Posted: Mon Jul 12, 2004 1:50 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
"Aurélien REGAT-BARREL" <nospam-aregatba (AT) yahoo (DOT) fr.invalid> écrivait:
| Quote: | Ohhhhhh...
On peut faire ça en C++...
|
Et pire encore :
| Quote: | template <class T> class ITriplet
{
public:
|
// (Le code de Fabien ne compile pas : NB_VALEURS n'est
// encore défini)
// ...
char const * GetName() const {
return static_cast< T const & >(*this).name_;
}
// ...
| Quote: | };
class Essai: public ITriplet<Essai
{
|
// ...
public:
static char name_[];
char Essai::name_[]= "Essai";
| Quote: | 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 ?
|
Ça marche pas.
--
Franck Branjonneau
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Mon Jul 12, 2004 2:37 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
On Mon, 12 Jul 2004 15:50:23 +0200, Franck Branjonneau
<fasbjx (AT) free (DOT) fr>:
| Quote: | // (Le code de Fabien ne compile pas : NB_VALEURS n'est
// encore défini)
|
Le code suivant n'est pas correct ?
struct C
{
int f() { return valeur; }
enum { valeur= 42 };
};
Et quid de celui-là ?
struct C
{
int f() { return valeur; }
static int valeur;
};
int C::valeur= 42;
|
|
| Back to top |
|
 |
Franck Branjonneau Guest
|
Posted: Mon Jul 12, 2004 3:04 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
Fabien LE LEZ <gramster (AT) gramster (DOT) com> écrivait:
| Quote: | On Mon, 12 Jul 2004 15:50:23 +0200, Franck Branjonneau
[email]fasbjx (AT) free (DOT) fr[/email]>:
// (Le code de Fabien ne compile pas : NB_VALEURS n'est
// encore défini)
Le code suivant n'est pas correct ?
struct C
{
int f() { return valeur; }
enum { valeur= 42 };
};
|
Correct.
| Quote: | Et quid de celui-là ?
struct C
{
int f() { return valeur; }
static int valeur;
};
int C::valeur= 42;
|
Correct.
template< typename >
struct C
{
int f() { return valeur; }
enum { valeur= 42 };
};
Incorrect, même si il compile.
--
Franck Branjonneau <fasbjx (AT) free (DOT) fr>
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Mon Jul 12, 2004 3:32 pm Post subject: Re: Definition d'une interface (a la Java) a l'aide de templ |
|
|
On Mon, 12 Jul 2004 17:04:11 +0200, Franck Branjonneau
<fasbjx (AT) free (DOT) fr>:
| Quote: | template< typename
struct C
{
int f() { return valeur; }
enum { valeur= 42 };
};
Incorrect
|
Grrr...
Y a-t-il moyen de s'en sortir quand on veut écrire :
template< typename >
struct C
{
int f() { return taille; }
double table [taille];
};
?
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|