Je l'ai surchargé pour pouvoir remplir un vector<int> ou bien un vector
<std::string>
Mais là je voudrais la même fonction pour un vector<unsigned int>, et je me
dis que plutôt que de refaire une troisième fonction identique, je pourrais
utiliser les templates.
Mais là je voudrais la même fonction pour un vector<unsigned int>, et je me dis que plutôt que de refaire une troisième fonction identique, je pourrais utiliser les templates.
Yep. Je dirais même plus : tu as déjà deux fonctions qui font la même chose, il y a donc un problème.
while((start = chaine.find_first_not_of(delimiters, start)) != std::string::npos) { std::string::size_type end= chaine.find_first_of(delimiters, start); Element e; Convertir (e, chaine.substr(start, end-start)); tokens.push_back (e); start=end; /* Note : je n'ai pas changé la boucle car je ne sais pas ce que tu veux faire exactement */ } }
On 10 Aug 2004 17:13:18 GMT, "Michaël Delva"
<michael_delva@i_cant_remember.com>:
Mais là je voudrais la même fonction pour un vector<unsigned int>, et je me
dis que plutôt que de refaire une troisième fonction identique, je pourrais
utiliser les templates.
Yep. Je dirais même plus : tu as déjà deux fonctions qui font la même
chose, il y a donc un problème.
while((start = chaine.find_first_not_of(delimiters, start)) !=
std::string::npos)
{
std::string::size_type
end= chaine.find_first_of(delimiters, start);
Element e;
Convertir (e, chaine.substr(start, end-start));
tokens.push_back (e);
start=end;
/* Note : je n'ai pas changé la boucle car je ne sais pas ce que tu
veux faire exactement */
}
}
Mais là je voudrais la même fonction pour un vector<unsigned int>, et je me dis que plutôt que de refaire une troisième fonction identique, je pourrais utiliser les templates.
Yep. Je dirais même plus : tu as déjà deux fonctions qui font la même chose, il y a donc un problème.
while((start = chaine.find_first_not_of(delimiters, start)) != std::string::npos) { std::string::size_type end= chaine.find_first_of(delimiters, start); Element e; Convertir (e, chaine.substr(start, end-start)); tokens.push_back (e); start=end; /* Note : je n'ai pas changé la boucle car je ne sais pas ce que tu veux faire exactement */ } }
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme reponse...
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
Fabien LE LEZ <gramster@gramster.com> writes:
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme
reponse...
A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme reponse...
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
Arnaud Debaene
Michaël Delva wrote:
Salut à tous,
J'ai créé une fonction qui me permet de remplir un vecteur par les différents éléments d'une chaine std::string:
<snip>
Je l'ai surchargé pour pouvoir remplir un vector<int> ou bien un vector <std::string>
Mais là je voudrais la même fonction pour un vector<unsigned int>, et je me dis que plutôt que de refaire une troisième fonction identique, je pourrais utiliser les templates.
Seulement je sais pas faire :(
Comment je peux faire ça?
Commences par analyser ton code existant : tes 2 fonctions sont strictement identiques à une ligne près :
tokens.push_back(StrToInt(chaine.substr(start, end-start).c_str ())); //version int
A partir de là, on voit que, pour écrire une version générique de ta fonction, il va nous falloir un paramètre supplémentaire : un objet/fonction/truc d'insertion qui prend en entrée un std::string et recrache en sortie la conversion de cette chaîne en type adapté à ton conteneur cible.
Appelons cette "chose" StringConverter, et prenons comme convention que c'est un objet, et que c'est son opérateur () qui doit être appelé pour faire cette conversion. La version générique de StringConverter s'écrit ainsi:
template <class T> struct StringConverter { inline T operator() (const std::string& s) { return s; } }; Cette version générique fonctionnera pour n'importe quel type "implicitement constructible" depuis une std::string, y compris std::string elle-même!
Maintenant, écrivons une spécialisation de notre petite classe de conversion pour les int : template<> struct StringConverter<int> { inline int operator() (const std::string& s) { return StrToInt(s); } };
Tu peux écrire d'autres spécialisations de StringConverter pour tous les types dont tu peux avoir besoin : unsigned int ou n'importe quoi d'autre.
Maintenant, il ne nous reste plus qu'à écrire une version template de ta fonction Tokenize. Il y a un seul paramètre template : le type du vecteur de sortie. template <class OutputType> void __fastcall Tokenize(const std::string & chaine, std::vector<OutputType> &tokens, const std::string & delimiters) { StringConverter<OutputType> converter; //objet qui va être utilisé pour obtenir la conversion de chaque sous-chaîne en OutputType. tokens.clear();
Et voilà! Pour l'appel de cete fonction, tu n'as même pas besoin de spécifier le paramètre template, il est déduit du type du paramètre "tokens" : std::vector<int> mes_tokens; Tokenize(ma_chaine, mes_tokens, mes_delimiteurs); //appelée avec un vector<int>, donc OutputType==int.
Notes bien que c'est une solution parmi d'autres, et pas forcément la meilleure, vu qu'il faut définir une spécialisation de TypeConverter pour tous les types que tu veux utiliser (il est certainement possible de faire mieux avec des typetraits), mais c'est une 1ère approche.
<standard disclaimer> Code écrit à la va-vite et non testé, fourni sans aucune garantie :-) </standard disclaimer>
Arnaud
Michaël Delva wrote:
Salut à tous,
J'ai créé une fonction qui me permet de remplir un vecteur par les
différents éléments d'une chaine std::string:
<snip>
Je l'ai surchargé pour pouvoir remplir un vector<int> ou bien un
vector <std::string>
Mais là je voudrais la même fonction pour un vector<unsigned int>, et
je me dis que plutôt que de refaire une troisième fonction identique,
je pourrais utiliser les templates.
Seulement je sais pas faire :(
Comment je peux faire ça?
Commences par analyser ton code existant : tes 2 fonctions sont strictement
identiques à une ligne près :
tokens.push_back(StrToInt(chaine.substr(start, end-start).c_str ()));
//version int
A partir de là, on voit que, pour écrire une version générique de ta
fonction, il va nous falloir un paramètre supplémentaire : un
objet/fonction/truc d'insertion qui prend en entrée un std::string et
recrache en sortie la conversion de cette chaîne en type adapté à ton
conteneur cible.
Appelons cette "chose" StringConverter, et prenons comme convention que
c'est un objet, et que c'est son opérateur () qui doit être appelé pour
faire cette conversion. La version générique de StringConverter s'écrit
ainsi:
template <class T> struct StringConverter
{
inline T operator() (const std::string& s)
{
return s;
}
};
Cette version générique fonctionnera pour n'importe quel type "implicitement
constructible" depuis une std::string, y compris std::string elle-même!
Maintenant, écrivons une spécialisation de notre petite classe de conversion
pour les int :
template<> struct StringConverter<int>
{
inline int operator() (const std::string& s)
{
return StrToInt(s);
}
};
Tu peux écrire d'autres spécialisations de StringConverter pour tous les
types dont tu peux avoir besoin : unsigned int ou n'importe quoi d'autre.
Maintenant, il ne nous reste plus qu'à écrire une version template de ta
fonction Tokenize. Il y a un seul paramètre template : le type du vecteur de
sortie.
template <class OutputType> void __fastcall Tokenize(const std::string &
chaine, std::vector<OutputType> &tokens, const std::string & delimiters)
{
StringConverter<OutputType> converter; //objet qui va être utilisé pour
obtenir la conversion de chaque sous-chaîne en OutputType.
tokens.clear();
Et voilà! Pour l'appel de cete fonction, tu n'as même pas besoin de
spécifier le paramètre template, il est déduit du type du paramètre "tokens"
:
std::vector<int> mes_tokens;
Tokenize(ma_chaine, mes_tokens, mes_delimiteurs); //appelée avec un
vector<int>, donc OutputType==int.
Notes bien que c'est une solution parmi d'autres, et pas forcément la
meilleure, vu qu'il faut définir une spécialisation de TypeConverter pour
tous les types que tu veux utiliser (il est certainement possible de faire
mieux avec des typetraits), mais c'est une 1ère approche.
<standard disclaimer> Code écrit à la va-vite et non testé, fourni sans
aucune garantie :-) </standard disclaimer>
J'ai créé une fonction qui me permet de remplir un vecteur par les différents éléments d'une chaine std::string:
<snip>
Je l'ai surchargé pour pouvoir remplir un vector<int> ou bien un vector <std::string>
Mais là je voudrais la même fonction pour un vector<unsigned int>, et je me dis que plutôt que de refaire une troisième fonction identique, je pourrais utiliser les templates.
Seulement je sais pas faire :(
Comment je peux faire ça?
Commences par analyser ton code existant : tes 2 fonctions sont strictement identiques à une ligne près :
tokens.push_back(StrToInt(chaine.substr(start, end-start).c_str ())); //version int
A partir de là, on voit que, pour écrire une version générique de ta fonction, il va nous falloir un paramètre supplémentaire : un objet/fonction/truc d'insertion qui prend en entrée un std::string et recrache en sortie la conversion de cette chaîne en type adapté à ton conteneur cible.
Appelons cette "chose" StringConverter, et prenons comme convention que c'est un objet, et que c'est son opérateur () qui doit être appelé pour faire cette conversion. La version générique de StringConverter s'écrit ainsi:
template <class T> struct StringConverter { inline T operator() (const std::string& s) { return s; } }; Cette version générique fonctionnera pour n'importe quel type "implicitement constructible" depuis une std::string, y compris std::string elle-même!
Maintenant, écrivons une spécialisation de notre petite classe de conversion pour les int : template<> struct StringConverter<int> { inline int operator() (const std::string& s) { return StrToInt(s); } };
Tu peux écrire d'autres spécialisations de StringConverter pour tous les types dont tu peux avoir besoin : unsigned int ou n'importe quoi d'autre.
Maintenant, il ne nous reste plus qu'à écrire une version template de ta fonction Tokenize. Il y a un seul paramètre template : le type du vecteur de sortie. template <class OutputType> void __fastcall Tokenize(const std::string & chaine, std::vector<OutputType> &tokens, const std::string & delimiters) { StringConverter<OutputType> converter; //objet qui va être utilisé pour obtenir la conversion de chaque sous-chaîne en OutputType. tokens.clear();
Et voilà! Pour l'appel de cete fonction, tu n'as même pas besoin de spécifier le paramètre template, il est déduit du type du paramètre "tokens" : std::vector<int> mes_tokens; Tokenize(ma_chaine, mes_tokens, mes_delimiteurs); //appelée avec un vector<int>, donc OutputType==int.
Notes bien que c'est une solution parmi d'autres, et pas forcément la meilleure, vu qu'il faut définir une spécialisation de TypeConverter pour tous les types que tu veux utiliser (il est certainement possible de faire mieux avec des typetraits), mais c'est une 1ère approche.
<standard disclaimer> Code écrit à la va-vite et non testé, fourni sans aucune garantie :-) </standard disclaimer>
Arnaud
Fabien LE LEZ
On 11 Aug 2004 09:49:01 +0200, Jean-Marc Bourguet :
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme reponse...
Autre message ? Je sais que j'ai écrit deux fois le même message, mais c'est parce que le premier a disparu de ma boîte d'envoi (qui garde pourtant les messages jusqu'à ce que je les efface à la main), et n'apparaît pas sur mon serveur (news.free.fr). Je ne sais pas du tout ce qui s'est passé.
-- ;-)
On 11 Aug 2004 09:49:01 +0200, Jean-Marc Bourguet <jm@bourguet.org>:
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme
reponse...
Autre message ? Je sais que j'ai écrit deux fois le même message, mais
c'est parce que le premier a disparu de ma boîte d'envoi (qui garde
pourtant les messages jusqu'à ce que je les efface à la main), et
n'apparaît pas sur mon serveur (news.free.fr). Je ne sais pas du tout
ce qui s'est passé.
On 11 Aug 2004 09:49:01 +0200, Jean-Marc Bourguet :
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme reponse...
Autre message ? Je sais que j'ai écrit deux fois le même message, mais c'est parce que le premier a disparu de ma boîte d'envoi (qui garde pourtant les messages jusqu'à ce que je les efface à la main), et n'apparaît pas sur mon serveur (news.free.fr). Je ne sais pas du tout ce qui s'est passé.
-- ;-)
Jean-Marc Bourguet
Fabien LE LEZ writes:
On 11 Aug 2004 09:49:01 +0200, Jean-Marc Bourguet :
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme reponse...
Autre message ? Je sais que j'ai écrit deux fois le même message, mais c'est parce que le premier a disparu de ma boîte d'envoi (qui garde pourtant les messages jusqu'à ce que je les efface à la main), et n'apparaît pas sur mon serveur (news.free.fr). Je ne sais pas du tout ce qui s'est passé.
Je l'ai lu sur news.free.fr, en reponse a un de mes messages ou il est question de marais et de crocodiles :-)
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
Fabien LE LEZ <gramster@gramster.com> writes:
On 11 Aug 2004 09:49:01 +0200, Jean-Marc Bourguet <jm@bourguet.org>:
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme
reponse...
Autre message ? Je sais que j'ai écrit deux fois le même message, mais
c'est parce que le premier a disparu de ma boîte d'envoi (qui garde
pourtant les messages jusqu'à ce que je les efface à la main), et
n'apparaît pas sur mon serveur (news.free.fr). Je ne sais pas du tout
ce qui s'est passé.
Je l'ai lu sur news.free.fr, en reponse a un de mes messages ou il est
question de marais et de crocodiles :-)
A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
On 11 Aug 2004 09:49:01 +0200, Jean-Marc Bourguet :
J'ai bien aime ton choix pour l'autre message ou tu donnes la meme reponse...
Autre message ? Je sais que j'ai écrit deux fois le même message, mais c'est parce que le premier a disparu de ma boîte d'envoi (qui garde pourtant les messages jusqu'à ce que je les efface à la main), et n'apparaît pas sur mon serveur (news.free.fr). Je ne sais pas du tout ce qui s'est passé.
Je l'ai lu sur news.free.fr, en reponse a un de mes messages ou il est question de marais et de crocodiles :-)
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
Fabien LE LEZ
On 11 Aug 2004 11:47:49 +0200, Jean-Marc Bourguet :
Je l'ai lu sur news.free.fr, en reponse a un de mes messages ou il est question de marais et de crocodiles :-)
Ah, OK ! Je n'avais pas compris que tu parlais de l'autre thread. Quand tu dis "l'autre message" alors que j'en ai envoyé 30, c'est difficile de suivre ;-)
Euh... D'ailleurs, de quoi tu parles exactement ? Tu as écrit "Fabien LE LEZ writes:" mais avec aucune citation. Message parti trop vite ?
-- ;-)
On 11 Aug 2004 11:47:49 +0200, Jean-Marc Bourguet <jm@bourguet.org>:
Je l'ai lu sur news.free.fr, en reponse a un de mes messages ou il est
question de marais et de crocodiles :-)
Ah, OK !
Je n'avais pas compris que tu parlais de l'autre thread.
Quand tu dis "l'autre message" alors que j'en ai envoyé 30, c'est
difficile de suivre ;-)
Euh... D'ailleurs, de quoi tu parles exactement ? Tu as écrit "Fabien
LE LEZ <gramster@gramster.com> writes:" mais avec aucune citation.
Message parti trop vite ?
On 11 Aug 2004 11:47:49 +0200, Jean-Marc Bourguet :
Je l'ai lu sur news.free.fr, en reponse a un de mes messages ou il est question de marais et de crocodiles :-)
Ah, OK ! Je n'avais pas compris que tu parlais de l'autre thread. Quand tu dis "l'autre message" alors que j'en ai envoyé 30, c'est difficile de suivre ;-)
Euh... D'ailleurs, de quoi tu parles exactement ? Tu as écrit "Fabien LE LEZ writes:" mais avec aucune citation. Message parti trop vite ?
-- ;-)
Michaël Delva
Merci à vous deux pour vos réponses et éclaircissements...
Je commence à me rendre compte de la puissance des templates!
Faudra que je m'y interesse sérieusement bientôt!
Encore merci...
Merci à vous deux pour vos réponses et éclaircissements...
Je commence à me rendre compte de la puissance des templates!
Merci à vous deux pour vos réponses et éclaircissements...
Je commence à me rendre compte de la puissance des templates!
Faudra que je m'y interesse sérieusement bientôt!
Encore merci...
Michaël Delva
std::vector<int> mes_tokens; Tokenize(ma_chaine, mes_tokens, mes_delimiteurs); //appelée avec un vector<int>, donc OutputType==int.
Notes bien que c'est une solution parmi d'autres, et pas forcément la meilleure, vu qu'il faut définir une spécialisation de TypeConverter pour tous les types que tu veux utiliser (il est certainement possible de faire mieux avec des typetraits), mais c'est une 1ère approche.
<standard disclaimer> Code écrit à la va-vite et non testé, fourni sans aucune garantie :-) </standard disclaimer>
Arnaud
Ton code marche parfaitement bien ;)
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
std::vector<int> mes_tokens;
Tokenize(ma_chaine, mes_tokens, mes_delimiteurs); //appelée avec un
vector<int>, donc OutputType==int.
Notes bien que c'est une solution parmi d'autres, et pas forcément la
meilleure, vu qu'il faut définir une spécialisation de TypeConverter
pour tous les types que tu veux utiliser (il est certainement possible
de faire mieux avec des typetraits), mais c'est une 1ère approche.
<standard disclaimer> Code écrit à la va-vite et non testé, fourni
sans aucune garantie :-) </standard disclaimer>
Arnaud
Ton code marche parfaitement bien ;)
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
std::vector<int> mes_tokens; Tokenize(ma_chaine, mes_tokens, mes_delimiteurs); //appelée avec un vector<int>, donc OutputType==int.
Notes bien que c'est une solution parmi d'autres, et pas forcément la meilleure, vu qu'il faut définir une spécialisation de TypeConverter pour tous les types que tu veux utiliser (il est certainement possible de faire mieux avec des typetraits), mais c'est une 1ère approche.
<standard disclaimer> Code écrit à la va-vite et non testé, fourni sans aucune garantie :-) </standard disclaimer>
Arnaud
Ton code marche parfaitement bien ;)
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
Arnaud Debaene
Michaël Delva wrote:
Ton code marche parfaitement bien ;) Ouf! ;-)
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
Googlise un peu sur le sujet. L'idée serait par exemple d'avoir une seule version de StringConverter pour tous les types numériques intégraux (int, short, long, unsigned ou pas), et de sélectionner cette spécialisation pour tous ces types en se basant sur un "trait" (ou "tag"si tu préfère) qui indique que ces types sont efectivement des numériques intégraux.
Arnaud
Michaël Delva wrote:
Ton code marche parfaitement bien ;)
Ouf! ;-)
Et tu parles de typetraits, que je ne connais pas... ça consiste en
quoi?
Googlise un peu sur le sujet. L'idée serait par exemple d'avoir une seule
version de StringConverter pour tous les types numériques intégraux (int,
short, long, unsigned ou pas), et de sélectionner cette spécialisation pour
tous ces types en se basant sur un "trait" (ou "tag"si tu préfère) qui
indique que ces types sont efectivement des numériques intégraux.
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
Googlise un peu sur le sujet. L'idée serait par exemple d'avoir une seule version de StringConverter pour tous les types numériques intégraux (int, short, long, unsigned ou pas), et de sélectionner cette spécialisation pour tous ces types en se basant sur un "trait" (ou "tag"si tu préfère) qui indique que ces types sont efectivement des numériques intégraux.
Arnaud
Fabien LE LEZ
On 11 Aug 2004 11:13:30 GMT, "Michaël Delva" :
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
J'imagine qu'il s'agit des "traits" (je ne sais pas s'il y a un équivalent en français). Il s'agit de classes dont tous les membres sont statiques, et qui donne des informations générales (généralement, sur un type).
Le plus classique est std::numeric_limits<>.
Autre exemple :
class TerminaisonZero { static char const caractere_fin= ' '; };
class TerminaisonFinLigne { static char const caractere_fin= 'n'; };
template <class Trait> class BasicString { public: size_t Longueur() { int reponse= 0; while (data[reponse] != Trait::caractere_fin) ++reponse; return reponse; } };
"TerminaisonZero" et "TerminaisonFinLigne" sont des traits.
-- ;-)
On 11 Aug 2004 11:13:30 GMT, "Michaël Delva"
<michael_delva@i_cant_remember.com>:
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
J'imagine qu'il s'agit des "traits" (je ne sais pas s'il y a un
équivalent en français). Il s'agit de classes dont tous les membres
sont statiques, et qui donne des informations générales (généralement,
sur un type).
Le plus classique est std::numeric_limits<>.
Autre exemple :
class TerminaisonZero
{
static char const caractere_fin= ' ';
};
class TerminaisonFinLigne
{
static char const caractere_fin= 'n';
};
template <class Trait> class BasicString
{
public:
size_t Longueur()
{
int reponse= 0;
while (data[reponse] != Trait::caractere_fin) ++reponse;
return reponse;
}
};
Et tu parles de typetraits, que je ne connais pas... ça consiste en quoi?
J'imagine qu'il s'agit des "traits" (je ne sais pas s'il y a un équivalent en français). Il s'agit de classes dont tous les membres sont statiques, et qui donne des informations générales (généralement, sur un type).
Le plus classique est std::numeric_limits<>.
Autre exemple :
class TerminaisonZero { static char const caractere_fin= ' '; };
class TerminaisonFinLigne { static char const caractere_fin= 'n'; };
template <class Trait> class BasicString { public: size_t Longueur() { int reponse= 0; while (data[reponse] != Trait::caractere_fin) ++reponse; return reponse; } };