OVH Cloud OVH Cloud

Fonctions template

7 réponses
Avatar
Zoubidaman
Salut à tous,

mon problème consiste à créer une classe et des fonctions templates.

Bien que je me sois beaucoup renseigné sur le sujet, je me pose pas mal
de questions:

Dans une classe normale (et que je voudrais modifier), j'ai surchargé des
fonctions qui servent à remplir des ListBox et des ComboBox.

Ex:
//-----------------------------------------------------------------------
//Permet de remplir un ComboBox avec la liste des statistiques
void __fastcall Remplir_Box::RemplirStats(TComboBox * Objet)
{
Objet->Clear();

Configuration *Options = Configuration::getInstance();

for (int i=0; i < Options->GetNbreStats(); i++)
Objet->AddItem(Options->Stat_Nom_Long(i),this);

Objet->ItemIndex = 0;
}

//-----------------------------------------------------------------------
//Permet de remplir un ComboBox avec la liste des statistiques
void __fastcall Remplir_Box::RemplirStats(TListBox * Objet)
{
Objet->Clear();

Configuration *Options = Configuration::getInstance();

for (int i=0; i < Options->GetNbreStats(); i++)
Objet->AddItem(Options->Stat_Nom_Long(i),this);

Objet->ItemIndex = 0;
}

Comme vous le voyez, ces deux fonctions sont identiques hormis le fait
que les arguments ont changé...

Je me suis dit que je pouvais éviter cela en créant une fonction
template...

Donc:

1) Ai-je tort de penser cela?
2) Je dois donc faire des fonctions template... Mais la classe qui les
encapsule doit-elle être également template?
3) Comment s'écrirait la fonction ci-dessus en template? (Pour que je
puisse m'en inspirer pour les autres fonctions ;-) )

Merci d'avance!!

7 réponses

Avatar
Vivien Gallinaro
Zoubidaman wrote:

Dans une classe normale


Tu veux dire qu'une classe template ne serait pas une classe normale ?-P
(Bon, en un sens, c'est pas faux. Ce sont plutôt les instances, qu'il
faudrait considérer comme _des_ classes normales...)

(et que je voudrais modifier), j'ai surchargé des
fonctions qui servent à remplir des ListBox et des ComboBox.

void __fastcall Remplir_Box::RemplirStats(TComboBox * Objet)
{
// code
}

void __fastcall Remplir_Box::RemplirStats(TListBox * Objet)
{
// code
}

Comme vous le voyez, ces deux fonctions sont identiques hormis le fait
que les arguments ont changé...

Je me suis dit que je pouvais éviter cela en créant une fonction
template...

Donc:

1) Ai-je tort de penser cela?


Je ne pense pas que les templates soient vraiment appropriés dans ton
cas. Tes paramètres étant des pointeurs, et puisque tu utilises les
mêmes membres pour les deux, j'opterais pour une solution à base
d'héritage, si cela est possible.
C'est réalisable, pour peu qu'il existe quelque chose comme :

class TBox
{
public:
virtual void Clear() =0;
virtual void AddItem (T, U) =0;
unsigned ItemIndex;
// ...
};

Avec T, si on note T' le type de retour de Configuration::Stat_Nom_Long
(int), T==T' ou T' implicitement convertible en T (typiquement, string
pour les deux, j'imagine ; ou bien du const char*...).
Avec U une base publique de Remplir_Box (ou Remplir_Box lui-même).
Et telle que TBox soit une base publique de ComboBox et de ListBox.

Si tu peux réunir ces conditions, tu devrais pouvoir écrire :

void __fastcall Remplir_Box::RemplirStats(TBox * Objet)
{
// code
}

A l'exécution, je ne crois pas que tu perdes du temps, ou alors si peu,
pour les appels de fonctions virtuelles. Par contre, tu gagnes en temps
de compilation, en taille du (ou des) binaire généré, et en simplicité
d'écriture et de débugage, en particulier si tu n'as pas une bonne
expérience des templates.

2) Je dois donc faire des fonctions template... Mais la classe qui les
encapsule doit-elle être également template?


Non, une classe "normale" peut parfaitement avoir une fonction membre
template. Les choses peuvent même être sérieusement plus compliquées que
ça, puisqu'une classe template peut avoir des méthodes qui sont
"re"-templates, comme :

template <class T> struct A {
template <class U> void f (const U&);
};

Qui est utile, par exemple, pour faire des conversions, mais qui rend la
définition out-of-line de la fonction un peu plus chevelue encore que la
moyenne des templates (je t'épargne ça).

Une autre possibilité, pour rendre son code bien illisible, est de
surcharger une fonction "normale" avec une fonction template, il me
semble. Mais je suis pas trop sûr de la façon de faire, là tout de
suite... (Donc peut-être même que je dis une grosse bêtise, mais cette
assertion est tout le temps valable, et souvent vérifiée ;-/ )

3) Comment s'écrirait la fonction ci-dessus en template? (Pour que je
puisse m'en inspirer pour les autres fonctions ;-) )


En utilisant les templates, je pense qu'on écrirait quelque chose comme ça :

class Remplir_Box {
template <class TypeDeBox> void RemplirStats (TypeDeBox * Objet);
};

template <class TypeDeBox>
void __fastcall Remplir_Box::RemplirStats(TypeDeBox * Objet)
{
// code
}


Pour le __fastcall, je ne garantis pas que c'est là qu'il faut le
mettre, mais avec des decl specifier qui commencent par "__", je dirais
qu'on s'éloigne du standard pour se rapprocher de trucs pas très propres ;)

Il est possible aussi que "pointeur de TypeDeBox" ne soit pas plus
approprié qu'un "plain TypeDeBox", mais ça ne me semble pas probable. Il
faudrait que dans certains cas, tu veuilles recopier ta *Box. Pour le
peu d'expérience que j'ai en programmation de GUI, ça ne paraît pas
faire sens...

Mon conseil perso, vraiment, c'est d'utiliser du polymorphisme dynamique
(c'est à dire un héritage). La raison essentielle qui pourrait t'en
empêcher serait que ComboBox et ListBox n'aient pas de base commune
convenable et appartiennent à une lib que tu ne peux pas modifier.
Réunir ces deux conditions me paraît assez peu probable...


Par contre, question design, j'avais deux trois question, mais je pense
que la réponse est systématiquement : « Configuration ne veut pas qu'on
lui parle de GUI »...

Il en subsiste quand même une, de question :
J'ai des doutes sur la raison d'être du deuxième argument d'AddItem(),
mais je devine qu'il permet à la *Box de prévenir quand on lui
chatouille l'élément en question. Un callback, quoi. Sauf que dans ce
cas, elle appelle quoi au juste, la *Box ? Une certaine fonction membre
de l'objet que tu lui passes ? J'ai tort de penser que c'est un peu
pouilleux, et qu'il aurait été préférable de passer un function object ?

Si c'est bien le cas (ie la *Box appelle une fonc membre déterminée de
l'objet), et que tu utilises la version template, le jour où tu passes
une *Box qui veut appeler une autre méthode (va savoir pourquoi, mais
pourquoi pas...), qui n'existe pas dans Remplir_Box, je pense que le
débugage risque d'être nettement plus compliqué.

Merci d'avance!!


Oui, enfin... il s'en est fallu de peu que j'embrouille les pinceaux de
tout le monde. D'ailleurs, j'avais un petit sondage :

« Vous préférez quand je ne réfléchis pas avant de dire des bêtises
simples, ou quand je réfléchis avant de dire des bêtises compliquées ? »

Mais pour le coup, j'ai suffisamment réfléchi pour enlever beaucoup de
bêtises compliquées... ;)

Gourgouilloult du Clapotis
En espérant qu'ils en reste le moins possible

Avatar
Vivien Gallinaro
class Remplir_Box {
template <class TypeDeBox> void RemplirStats (TypeDeBox * Objet);
};

template <class TypeDeBox>
void __fastcall Remplir_Box::RemplirStats(TypeDeBox * Objet)
{
// code
}


Mince, j'ai oublié de préciser que si tu optes pour cette solution, tu
devra tout laisser dans le .h, y compris la définition de la fonction.

C'est dit de façon simpliste, mais c'est le piège le plus classique.

Gourgou

Avatar
Zoubidaman
Vivien Gallinaro wrote in
news::

class Remplir_Box {
template <class TypeDeBox> void RemplirStats (TypeDeBox * Objet);
};

template <class TypeDeBox>
void __fastcall Remplir_Box::RemplirStats(TypeDeBox * Objet)
{
// code
}


Mince, j'ai oublié de préciser que si tu optes pour cette solution, tu
devra tout laisser dans le .h, y compris la définition de la fonction.

C'est dit de façon simpliste, mais c'est le piège le plus classique.

Gourgou




Merci de ta réponse...

Concernant l'héritage dynamique, il faudrait que j'aille voir, car
effectivement TListBox et TComboBox dérivent tous deux de TObject.

Le problème étant que l'héritage est nouveau pour moi, j'ai encore jamais
fait...

Néanmoins, j'ai réussi à passer par un template:

template <typename T>
void __fastcall RemplirStats(
T Objet)
{
Objet->Clear();

Configuration *Options = Configuration::getInstance();

for (int i=0; i < Options->GetNbreStats(); i++)
Objet->AddItem(Options->Stat_Nom_Long(i),Objet);

Objet->ItemIndex = 0;
}

Le deuxième paramètre de AddItem étant bien un callback, puisqu'il faut
mettre le pointeur de l'objet qui se remplit...

Avec le code tel quel, ça marche très bien, mais si passer par l'héritage
se révéle être une solution plus simple (?) ou en tout cas plus
appropriée, il faudrait que je m'y intéresse, donc je vais relire de ce
pas ton premier post...

Merci encore!!


Avatar
Zoubidaman
Zoubidaman wrote in
news::

Vivien Gallinaro wrote in
news::

class Remplir_Box {
template <class TypeDeBox> void RemplirStats (TypeDeBox * Objet);
};

template <class TypeDeBox>
void __fastcall Remplir_Box::RemplirStats(TypeDeBox * Objet)
{
// code
}


Mince, j'ai oublié de préciser que si tu optes pour cette solution,
tu devra tout laisser dans le .h, y compris la définition de la
fonction.

C'est dit de façon simpliste, mais c'est le piège le plus classique.

Gourgou




Merci de ta réponse...

Concernant l'héritage dynamique, il faudrait que j'aille voir, car
effectivement TListBox et TComboBox dérivent tous deux de TObject.

Le problème étant que l'héritage est nouveau pour moi, j'ai encore
jamais fait...

Néanmoins, j'ai réussi à passer par un template:

template <typename T>
void __fastcall RemplirStats(
T Objet)
{
Objet->Clear();

Configuration *Options = Configuration::getInstance();

for (int i=0; i < Options->GetNbreStats(); i++)
Objet->AddItem(Options->Stat_Nom_Long(i),Objet);

Objet->ItemIndex = 0;
}

Le deuxième paramètre de AddItem étant bien un callback, puisqu'il
faut mettre le pointeur de l'objet qui se remplit...

Avec le code tel quel, ça marche très bien, mais si passer par
l'héritage se révéle être une solution plus simple (?) ou en tout cas
plus appropriée, il faudrait que je m'y intéresse, donc je vais relire
de ce pas ton premier post...

Merci encore!!



Bon, alors 5 mn de recherche dans l'aide de C++ Builder m'ont amené à
considérer l'héritage comme étant la meilleure solution à mon problème.

Il s'avère en effet que dans la cascade d'héritages que subissent les
composants graphiques de la VCL, il y a un parent commun à ListBox et
ComboBox qui contient la méthode virtuelle AddItem: TCustomListControl


Donc je me retrouve avec une méthode du type (ex:)

void __fastcall TForm1::Remplir(TCustomListControl * Objet)
{
Objet->AddItem("coucou",Objet);
}

Ce qui est effectivement beaucoup plus simple que mes fonctions de
template.

Sinon pour le _fastcall, il s'agit d'une extension de Builder

//--- Tiré de l'aide de Builder
Description

Le modificateur __fastcall permet de déclarer des fonctions attendant que
des paramètres soient transmis dans les registres. Les trois premiers
paramètres sont transmis (depuis la gauche vers la droite) dans EAX, EDX et
dans ECX, s'ils rentrent dans le registre. Les registres ne sont pas
utilisés si le paramètre est de type virgule flottante ou structure.

Donc voilà...

Et concernant les templates, bien que je me doute que leur utilisation
doit-être infinie, dans quels cas principalement est-on amené à les
utiliser?

Voilà...



Avatar
adebaene
Zoubidaman wrote in message news:...

Et concernant les templates, bien que je me doute que leur utilisation
doit-être infinie, dans quels cas principalement est-on amené à les
utiliser?


Utilisation la plus "courante" : faire un "conteneur" (un tableau, une
liste chainée, une table de hash, ...) d'un type T quelconque (ou
presque quelconque).

Ex : std::vector, std::list, std::basic_string, etc...

Arnaud

Avatar
Fabien LE LEZ
On Mon, 11 Aug 2003 20:18:20 +0200, Vivien Gallinaro
wrote:

filer au compilateur une partie du boulot qu'on a tendance à laisser
pour l'exécution, et ça s'appellerait les templates expressions


Tu parles de ce genre de trucs ?
<http://boost.org/libs/static_assert/static_assert.htm>


--
Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/
et http://www.aminautes.org/forums/serveurs/tablefr.html
Archives : http://groups.google.com/advanced_group_search
http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html

Avatar
Vivien Gallinaro
Fabien LE LEZ wrote:

filer au compilateur une partie du boulot qu'on a tendance à laisser
pour l'exécution, et ça s'appellerait les templates expressions


Tu parles de ce genre de trucs ?
<http://boost.org/libs/static_assert/static_assert.htm>


Beaucoup plus simple que ça : je ne sais absolument pas de quoi il
s'agit. ;)

Merci beaucoup pour le lien.

Gourgou