OVH Cloud OVH Cloud

Utilisation de spécialisation de template pour convertir.

33 réponses
Avatar
Stephane Wirtel
Bonjour,

Est-il possible de créer un template via spécialisation permettant
de convertir des types définit (bool, int, string, ...) vers un
std::string ?

Je m'explique.

J'essaie de créer une classe permettant de lire des settings provenant d'un
fichier de conf.

Donc, dedans, j'ai créé une méthode générique, du style :

class Settings {
private:
typedef std::map <std::string, std::string> MapSettings;
MapSettings mSettings;
public:
template <class T> const T& Read (const std::string& pVariable,
const T& pDefaultValue) {
/**
* Si variable présente dans la map on retourne la valeur
* trouvée, sinon, on retourne la valeur par défaut.
* On rajoute tout de même pDefaultValue dans map pour une prochaine
* utilisation.
*/

mSettings[pVariable] = TranslateToString (pDefaultValue);

return pDefaultValue;
}
};

Ce que j'aimerais c'est que TranslateToString prenne ma valeur et la
transforme directement en chaine de caractère.
Pour cela, j'avais pensé à un template sur une fonction ou sur une méthode,
mais cela ne semble pas fonctionner correctement. Voir mon post précédent.

Mais sinon, j'avais pensé à ceci.

dans ma classe Settings, il y aurait la méthode TranslateToString définie
comme suit :
class Settings {
private:
...
public:
...
template <class T> std::string TranslateToString (const T& pValue) {
}
template <> std::string TranslateToString <bool> (const bool& pValue) {
return (pValue ? "True" : "False");
}
template <> std::string TranslateToString <int> (const int& pValue) {
return "Chaine de test...";
}
};

Est-ce que quelqu'un peut m'aider ? Il me manque une solution pour résoudre
mon problème qui je pense doit certainement avec une réponse de ce type.

Merci d'avance,


Stéphane
--
Stephane Wirtel <stephane.wirtel@belgacom.net>

10 réponses

1 2 3 4
Avatar
Vincent Lascaux
"Stephane Wirtel" a écrit dans le message de
news: d40sf1$bjf$
Bonjour,

Est-il possible de créer un template via spécialisation permettant
de convertir des types définit (bool, int, string, ...) vers un
std::string ?


Tu peux aller voir boost::lexical_cast
Ta fonction TranslateToString peut être implémentée comme ca (et c'est ce
que fait boost à peu de chose près)

template<class T>
std::string TranslateToString(const T& value)
{
std::stringstream s;
s << value;
return s.str();
}

Pour que ca marche, il faut qu'il y ait un opérateur ostream << T (il y a ca
pour tous les types de bases, et tu devras le définir si tu veux traduire
des classes à toi)

--
Vincent

Avatar
Stephane Wirtel
template<class T>
std::string TranslateToString(const T& value)
{
std::stringstream s;
s << value;
return s.str();
}

Pour que ca marche, il faut qu'il y ait un opérateur ostream << T (il y a
ca pour tous les types de bases, et tu devras le définir si tu veux
traduire des classes à toi)


Merci vincent,

Actuelle, cela se limite uniquement à des types primitifs, int, char*
(string) et bool, et certainement double.




--
Stephane Wirtel

Avatar
Christophe de Vienne
Stephane Wirtel wrote:

Bonjour,

Est-il possible de créer un template via spécialisation permettant
de convertir des types définit (bool, int, string, ...) vers un
std::string ?

Je m'explique.

J'essaie de créer une classe permettant de lire des settings provenant
d'un fichier de conf.

Donc, dedans, j'ai créé une méthode générique, du style :

class Settings {
private:
typedef std::map <std::string, std::string> MapSettings;
MapSettings mSettings;
public:
template <class T> const T& Read (const std::string& pVariable,
const T& pDefaultValue) {
/**
* Si variable présente dans la map on retourne la valeur
* trouvée, sinon, on retourne la valeur par défaut.
* On rajoute tout de même pDefaultValue dans map pour une
prochaine * utilisation.
*/

mSettings[pVariable] = TranslateToString (pDefaultValue);

return pDefaultValue;
}
};

Ce que j'aimerais c'est que TranslateToString prenne ma valeur et la
transforme directement en chaine de caractère.
Pour cela, j'avais pensé à un template sur une fonction ou sur une
méthode, mais cela ne semble pas fonctionner correctement.


Peux-tu être plus précis pour le cas présent : qu'est-ce qui ne fonctionne
pas ?

Voir mon post
précédent.

Mais sinon, j'avais pensé à ceci.

dans ma classe Settings, il y aurait la méthode TranslateToString définie
comme suit :
class Settings {
private:
...
public:
...
template <class T> std::string TranslateToString (const T& pValue)
{ }
template <> std::string TranslateToString <bool> (const bool&
pValue) {
return (pValue ? "True" : "False");
}
template <> std::string TranslateToString <int> (const int&
pValue) {
return "Chaine de test...";
}
};


Pouquoi passer par des templates alors que la surchage fonctionne ?

std::string TranslateToString(std::string const & value) { return value; }
std::string TranslateToString(bool value) { return value ? "True" :
"False"; }

etc...

A la limite, j'utiliserais template pour faire une implémentation par
défaut :

template<typename T> std::string TranslateToString(T const & value) {
std::stringstream o;
o << value;
return o.str();
}



A+

Christophe

Avatar
Stephane Wirtel
template<typename T> std::string TranslateToString(T const & value) {
std::stringstream o;
o << value;
return o.str();
}
Effectivement, c'est ce que Vincent m'avait proposé hier soir, et que j'ai appliqué.

Le tout fonctionne nickel.

Pour quelle raison les templates ?
1) J'essaie d'apprendre les templates, et de les maitriser.
2) Voir leurs possibilités.
3) Pas envie de faire plusieurs copier/coller de la même fonction.

Mais merci de tes propositions,

Stéphane

Avatar
Stephane Wirtel
Dans la même idée, après le TranslateToString,

le StringTo.

Voici ce que j'essaie :

template <class T> const T& StringTo (const std::string& pValue) {
std::istringstream iss (pValue, std::istringstream::in);
T data;
iss >> data;
return data;
}


Je me suis basé sur l'exemple donné :
http://www.cplusplus.com/ref/iostream/istringstream/istringstream.html

Et voici la fameuse méthode qui appel StringTo

class DsSettings : public DsSingleton <DsSettings> {
private:
...
typedef std::map <std::string, std::string> MapSettings;
MapSettings mSettings;
...
public:
template <class T> const T& Read (const std::string& pVariable, const T& pDefaultValue) {
if (!VariableIsPresent (pVariable)) {
mSettings[pVariable] = TranslateToString (pDefaultValue);
mIsModified = true;
return pDefaultValue;
} else {
return StringTo (mSettings[pVariable]);
}
}
};

DsSettings::Read est utilisée de la manière suivante :

DsSettings& mSettings = DsSettings::GetInstance();
int smtpPort = mSettings.Read ("Port", 25);
int hostname = mSettings.Read ("Hostname", "localhost");

Donc, tout comme la fonction TranslateToString qui me permettait de transformer le type des valeurs vers des std::string,
j'aimerais savoir si il est possible de transformer mes std::string vers les types employés lors de l'appel de ma méthode template ?


Merci d'avance,

Stéphane
Avatar
Samuel Krempp
le Tuesday 19 April 2005 10:43, écrivit :

DsSettings::Read est utilisée de la manière suivante :

DsSettings& mSettings = DsSettings::GetInstance();
int smtpPort = mSettings.Read ("Port", 25);
int hostname = mSettings.Read ("Hostname", "localhost");

Donc, tout comme la fonction TranslateToString qui me permettait de
transformer le type des valeurs vers des std::string, j'aimerais savoir si
il est possible de transformer mes std::string vers les types employés
lors de l'appel de ma méthode template ?


euh, mais c'est ce que fait la fonction stringTo, où est le problème ?

Juste un détail à propos du code ici :
string hostname = mSettings.Read ("Hostname", "localhost");
(je suppose que c'est string hostname, pas int hostname comme dans le

message..)

utiliser la valeur par défaut pour en déduire le type voulu marche dans le
cas général, mais là le type ne sera pas string (plutot const char[9], ou
char * ..).
il faudrait expliciter le parametre template :

string hostname = mSettings.Read<string> ("Hostname", "localhost");

(et en passant, on pourrait spécialiser stringTo pour T=string :
template <> const std::string&
StringTo<std::string> (const std::string& pValue) {
return pValue;
}

ça éviterait de construire un stringstream inutilement pour lire un
string ...

c'est le genre de détails que boost::lexical_cast gère probablement
directement)


--
Sam

Avatar
Stephane Wirtel
Donc, tout comme la fonction TranslateToString qui me permettait de
transformer le type des valeurs vers des std::string, j'aimerais savoir
si il est possible de transformer mes std::string vers les types employés
lors de l'appel de ma méthode template ?


euh, mais c'est ce que fait la fonction stringTo, où est le problème ?
Le voici le problème :

DsSettings.hpp: In member function `const T& DsSante::DsSettings::Read(const
std::string&, const T&) [with T = char[5]]':
DsSante.cpp:22: instantiated from here
DsSettings.hpp:51: error: no matching function for call to `StringTo(
std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'
DsSettings.hpp: In member function `const T& DsSante::DsSettings::Read(const
std::string&, const T&) [with T = char[6]]':
DsSante.cpp:23: instantiated from here
DsSettings.hpp:51: error: no matching function for call to `StringTo(
std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'
DsSettings.hpp: In member function `const T& DsSante::DsSettings::Read(const
std::string&, const T&) [with T = int]':
DsSante.cpp:25: instantiated from here
DsSettings.hpp:51: error: no matching function for call to `StringTo(
std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'
make: *** [DsSante.o] Error 1




Juste un détail à propos du code ici :
string hostname = mSettings.Read ("Hostname", "localhost");
(je suppose que c'est string hostname, pas int hostname comme dans le

message..)
Effectivement, il s'agit bien de string hostname, j'ai fais une ptite

boulette ;-)


utiliser la valeur par défaut pour en déduire le type voulu marche dans le
cas général, mais là le type ne sera pas string (plutot const char[9], ou
char * ..).
il faudrait expliciter le parametre template :

string hostname = mSettings.Read<string> ("Hostname", "localhost");
Je ne veux utiliser un template de manière explicite, j'aimerais que cela

soit implicite, ce qui me permettra de créer du code bien plus dynamique.

(et en passant, on pourrait spécialiser stringTo pour T=string :
template <> const std::string&
StringTo<std::string> (const std::string& pValue) {
return pValue;
}

ça éviterait de construire un stringstream inutilement pour lire un
string ...
Je suis tout à fait d'accord avec toi.



c'est le genre de détails que boost::lexical_cast gère probablement
directement)
J'essaie de faire cela sans boost, afin d'apprendre.


Le but de tout cela, est de simplement stocké les valeurs lûe depuis un
fichier XML, ou plat et de les stocker dans une map, vu que ne je ne peux
pas faire de template sur une map ;-) je passe directement par des strings,

d'ou mon typedef std::map <std::string, std::string> MapSettings;

Donc, en gros, tout nouveau paramètre, je le transforme en string pour
l'introduire dans la map, et quand je désire récupérer sa valeur,
j'aimerais que la chaine de caractère se transforme dans le type de retour.


Avatar
Samuel Krempp
le Tuesday 19 April 2005 14:14, écrivit :

DsSettings.hpp: In member function `const T&
DsSante::DsSettings::Read(const
std::string&, const T&) [with T = int]':
DsSante.cpp:25: instantiated from here
DsSettings.hpp:51: error: no matching function for call to `StringTo(
std::basic_string<char, std::char_traits<char>, std::allocator<char>
&)'



ah oui j'avais pas pensé à ça, pour stringTo le type T ne peut être déduit,
puisque le paramètre de la fonction ne dépend pas de T..

il faut modifier Read :
return StringTo<T> (mSettings[pVariable]);

string hostname = mSettings.Read<string> ("Hostname", "localhost");
Je ne veux utiliser un template de manière explicite, j'aimerais que cela

soit implicite, ce qui me permettra de créer du code bien plus dynamique.


euh, bon, mais parfois il n'y a pas vraiment le choix.
Ici, "localhost" sera de type char[9], donc la déduction de T à partir du
paramètre ne donnera rien de bon.
solutions :
1. expliciter le parametre template comme je disais (ça ne diminue pas la
généricité du code à mon avis..)
2. string hostname = mSettings.Read("Hostname", string("localhost"));
-> impose de taper plus
3. spécialiser Read pour T=const char* pour éviter le problème
-> duplique inutilement du code. enfin, du coup on peut carrément éviter
l'appel à stringTo dans ce Read<const char*> spécialisé..

--
Sam


Avatar
Jean-Marc Bourguet
Samuel Krempp writes:

3. spécialiser Read pour T=const char* pour éviter le problème
-> duplique inutilement du code. enfin, du coup on peut carrément éviter
l'appel à stringTo dans ce Read<const char*> spécialisé..


Qu'est-ce que ça change? La spécialisation sur char
const[9] sera meilleure et générée si besoin est. Une
surchage du genre

template <int N>
void Read(std::string const&, char const (&p) [N]);

devrait elle être préférée si j'ai bien compris (en tout
cas, ça passe avec como 4.3.3 et g++ 3.3.4).

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
Samuel Krempp
le Tuesday 19 April 2005 15:50, écrivit :

Samuel Krempp writes:

3. spécialiser Read pour T=const char* pour éviter le problème
-> duplique inutilement du code. enfin, du coup on peut carrément éviter
l'appel à stringTo dans ce Read<const char*> spécialisé..


Qu'est-ce que ça change? La spécialisation sur char
const[9] sera meilleure et générée si besoin est. Une
surchage du genre
template <int N>
void Read(std::string const&, char const (&p) [N]);



ah oui, je voulais juste dire qu'il ne fallait pas laisser le code générique
gèrer le cas char[N], mais effectivement il faudrait *surcharger* pour
char* (ou char[N] ..), et non pas *spécialiser* comme j'ai dit. j'ai
tendance à faire la confusion quand je vais trop vite, alors que surcharge
et spécialisation sont des mécanismes bien différents.

Bref, les surcharges
void Read(std::string const&, char const* );
ou
template <int N>
void Read(std::string const&, char const (&p) [N]);

permettraient de faire ce qu'on veut.

--
Sam


1 2 3 4