OVH Cloud OVH Cloud

Problème de template

18 réponses
Avatar
Michaël Delva
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:

void __fastcall Tokenize(const std::string & chaine, std::vector<int> &
tokens, const std::string & delimiters)
{
tokens.clear();

std::string::size_type start=0, end;

while((start = chaine.find_first_not_of(delimiters, start)) !=
std::string::npos)
{
end = chaine.find_first_of(delimiters, start);
tokens.push_back(StrToInt(chaine.substr(start, end-start).c_str
()));
start=end;
}
}


void __fastcall Tokenize(const std::string & chaine, std::vector
<std::string> & tokens, const std::string & delimiters)
{
tokens.clear();

std::string::size_type start=0, end;

while((start = chaine.find_first_not_of(delimiters, start)) !=
std::string::npos)
{
end = chaine.find_first_of(delimiters, start);
tokens.push_back(chaine.substr(start, end-start).c_str());
start=end;
}
}

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?

Merci d'avance

8 réponses

1 2
Avatar
Jean-Marc Bourguet
Fabien LE LEZ writes:

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 ?


Dans l'autre thread, tu donne la solution a ce probleme-ci.

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


Avatar
Michaël Delva
Fabien LE LEZ wrote in
news::

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;
}
};

typedef BasicString<TerminaisonZero> ChaineClassique;
typedef BasicString<TerminaisonFinLigne> ChaineLigne;

"TerminaisonZero" et "TerminaisonFinLigne" sont des traits.




C'est ce que j'avais vu sur Internet, mais j'avoue ne pas saisir leur
utilité dans mon cas???


Avatar
kanze
"Arnaud Debaene" wrote in message
news:<4119d35b$0$3174$...

Michaël Delva wrote:

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?



[...]

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.


En plus, même si je ne suis pas pour la généricité à outrance, je crois
que j'écrirais la fonction de façon à utiliser des itérateurs. Et le
convertisseur comme paramètre.

template <class OutputType> void __fastcall Tokenize(const std::string &
chaine, std::vector<OutputType> &tokens, const std::string & delimiters)


template< typename OutIter, typename Cvt >
void
Tokenize(
std::string const& source,
OutIter dest,
Cvt convert,
std::string const& delimiters )
{
size_t start = source.find_first_not_of( delimiters ) ;
while ( start != std::string::npos ) {
size_t end
= source.find_first_of( delimiters, start ) ;
*dest ++ = convert( source.substr( start, end ) ;
start = source.find_first_not_of( delimiters, end ) ;
}
}

À l'appel, on passe quelque chose comme std::back_inserter( monVector )
comme paramètre de dest. Et quelque chose comme StringConverter< int >()
comme convert -- on pourrait aussi envisager une fonction qui n'a pas ce
paramètre :

template< typename OutIter >
void
Tokenize(
std::string const& source,
OutIter dest,
std::string const& delimiters )
{
return Tokenize(
source,
dest,
StringConverter< std::iterator_traits< OutIter >::value_type >(),
delimiters ) ;
}

On pourrait aussi envisager passer une paire d'itérateurs comme source.
Dans ce cas-là, on commencera par définir des objets fonctionnels qui
correspondent à find_first_not_of et à find_first_of :

class IsElement
{
public:
IsElement( std::string const& list )
: myList( list )
{
}
bool operator()( char ch ) const
{
return std::find( list.begin(), list.end(), ch ) != list.end() ;
}
private:
std::string myList ;
} ;

La même chose pour IsNotElement, et on pourrait écrire :

template< typename FwdIter, typename OutIter, typename Cvt >
void
Tokenize(
FwdIter begin,
FwdIter end,
OutIter dest,
Cvt convert,
std::string const& delimiter )
{
FwdIter tokenBegin
= std::find_if( begin, end, IsNotElement( delimiter ) ) ;
while ( tokenBegin != end ) {
FwdIter tokenEnd
= std::find_if( tokenBegin, end, IsElement( delimiter ) ) ;
*dest ++ = convert( std::string( tokenBegin, tokenEnd ) ) ;
// à moins que convert prend aussi des itérateurs...
tokenBegin
= std::find_if( tokenEnd, end, IsNotElement( delimiter ) ) ;
}
}

Mais là, je dirais que c'est seulement si Michaël a envie d'expérimenter
avec les concepts de la STL. On n'a rarement besoin d'autant de
généricité.

[...]
<standard disclaimer> Code écrit à la va-vite et non testé, fourni
sans aucune garantie :-) </standard disclaimer>


De même.

--
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


Avatar
Michaël Delva
Je suis réellement impressionné par ce genre de code...

Pas facile à comprendre les templates!! Et dire que tu as du passer 10
minutes à écrire ça!

Merci pour le code, il va être source d'inspiration un jour ou l'autre j'en
suis sûr...
Avatar
Arnaud Debaene
wrote:
on pourrait aussi envisager une fonction qui
n'a pas ce paramètre :

template< typename OutIter >
void
Tokenize(
std::string const& source,
OutIter dest,
std::string const& delimiters )
{
return Tokenize(
source,
dest,
StringConverter< std::iterator_traits< OutIter
::value_type >(), delimiters ) ;



Je n'en suis pas sûr, mais il ne faudrait pas un "typename" là, pour le
paramètre template de StringConverter ?

Arnaud


Avatar
Michel Michaud
Dans news:, Fabien LE
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).


« Trait » est un mot français ayant ce sens, c'est en anglais
que c'est emprunté du français !

(penses-y : « un des traits de caractères de Fabien est qu'il
semble toujours joyeux... :-) »)

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
kanze
"Arnaud Debaene" wrote in message
news:<411b58de$0$26989$...
wrote:
on pourrait aussi envisager une fonction qui
n'a pas ce paramètre :

template< typename OutIter >
void
Tokenize(
std::string const& source,
OutIter dest,
std::string const& delimiters )
{
return Tokenize(
source,
dest,
StringConverter< std::iterator_traits< OutIter
::value_type >(), delimiters ) ;



Je n'en suis pas sûr, mais il ne faudrait pas un "typename" là, pour
le paramètre template de StringConverter ?


Probablement. Juste avant le std::iterator_traits, puisque le value_type
est d'une part un nom dépendant, et de l'autre un type.

J'avoue que je n'ai pas trop l'habitude d'y penser ; jusqu'il y a peu,
mes compilateurs ne l'exigeaient pas. (En fait, jusqu'il y a peu, ce
genre de chose dépassait largement les capabilités du compilateur dont
je me servais en production.) Et j'ai saisi le code rapidement, comme
ça, sans essayer de le compiler ni quoique ce soit. C'était juste pour
donner des idées -- ce genre de chose devient un peu idiomatique, au
moins dans certains cercles.

En fait, si j'avais à écrire quelque chose de semblable dans le code de
production, je définirais probablement une classe de base pour
l'insertion des champs ; j'en dériverai à l'appel pour créer une classe
qui connaît d'une part la destination où il doit mettre les champs, et
de l'autre comment les convertir au type voulu. La généricité par
l'héritage, et non au moyen d'un template. Pour plusieurs raisons, mais
surtout parce que j'ai à faire avec des anciens compilateurs qu'on ne
met pas à jour (et des anciens collègues qui ne se mettent pas à jour
aussi).

Sauf que j'ai déjà une classe FieldArray qui permet la découpe en
champs -- on lui affecte une chaîne de caractères, et on accède aux
champs comme si c'était un std::vector. Ce qui permettrait quelque chose
du genre :

GB_CharacterSeparatedFields< char > fields( ',' ) ;
fields = source ;
std::transform( fields. begin() + 1, fields.end(),
std::back_inserter( dest ),
StringConverter< int >() ) ;

(GB_CharacterSeparaotedFields ne permet qu'un seul caractère de
séparation. Mais il serait assez facile d'en créer un qui permet une
liste de délimiteurs -- toute la logique sauf la découpe se trouve dans
la classe de base, et la découpe se fait au moyen d'un délégué créer par
la classe dérivée.)

Sinon, il ne faut pas non plus negliger les possibilités de Boost regex
pour ce genre de travail.

--
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



Avatar
Michaël Delva
Sinon, il ne faut pas non plus negliger les possibilités de Boost regex
pour ce genre de travail.


Ou bien Boost Tokenizer aussi

1 2