Le code de cet exemple vient en deux versions (deux « saveurs »), soit C++ 03 et C++ 11. Avec la version C++ 03, certains traits sont implémentés manuellement, alors qu'ils sont offerts par le standard depuis C++ 11. Bien entendu, préférez C++ 17 si votre compilateur le supporte.
Ceci est une implémentation complète (dans la mesure de ce que j'ai choisi de faire; on pourrait aller plus loin et considérer les cas où const char* et const wchar_t* servent de type source pour les conversions texte à texte) de lexical_cast (dont nous avons discuté dans l'article disponible ici).
En bref, cette implémentation ne repose que sur des bibliothèques standards (le code devrait être pleinement portable, modulo des bogues qui auraient pu m'échapper... ou d'occasionnelles bogues de bibliothèque standard, ce qui se produit à l'occasion). |
|
J'ai conservé les types étiquettes et les fonctions spécialisées sur cette base, pour réduire les disparités avec les versions antérieures, mais on aurait pu s'y prendre autrement cette fois. |
|
Le trait est_texte<T> s'avèrera pour tout type T de la forme basic_string<C> pour un type C donné. Je l'ai aussi enrichi de support pour basic_string_view<C>, ce qui demande toutefois un autre ajustement... |
|
... car si le type basic_string_view<C> est excellent à titre de type de paramètre pour des chaînes de caractères non-modifiables, il est beaucoup moins à propos à titre de type de retour. Ainsi, j'utiliserai unview_t<T> pour transformer les types de retour qui auraient été de la forme basic_string_view<C> en basic_string<C> quand je retournerai des valeurs des diverses implémentations de lexical_cast. |
|
Les diverses implémentations de lexical_cast suivent. Notez que si le contexte dans lequel vous travaillez ne vous permet pas de signaler une erreur de conversion par la voie d'une levée d'exception, il existe d'autres stratagèmes pour signaler le problème. |
|
La fonction lexical_cast appelée par le code client réalise ici son aiguillage par if constexpr, ce qui constitue un gain net de simplicité et d'efficacité en comparaison avec les approches antérieures. |
|
Le reste n'est qu'un petit exemple de code de test. |
|
Ceci est une implémentation complète (dans la mesure de ce que j'ai choisi de faire; on pourrait aller plus loin et considérer les cas où const char* et const wchar_t* servent de type source pour les conversions texte à texte) de lexical_cast (dont nous avons discuté dans l'article disponible ici).
#include <type_traits>
#include <string>
#include <sstream>
#include <algorithm>
#include <iostream>
class implicite {};
class texte_dest {};
class texte_texte {};
class general {};
template <class>
struct est_texte : std::false_type {
};
template <class C>
struct est_texte<std::basic_string<C>> : std::true_type {
};
template <class D, class S>
struct traits_conversion {
using type = std::conditional_t<
std::is_same<D,S>::value || std::is_convertible<S, D>::value,
implicite,
std::conditional_t<
est_texte<D>::value,
std::conditional_t<
est_texte<S>::value,
texte_texte,
texte_dest
>,
general
>
>;
};
template <class D, class S>
D lexical_cast(const S &src, general) {
stringstream sstr;
sstr << src;
D dest;
sstr >> dest; // on suppose une conversion sans erreur; ajuster au besoin
return dest;
}
template <class D, class S>
D lexical_cast(const S &src, texte_dest) {
using value_type = typename D::value_type;
basic_stringstream<value_type> sstr;
sstr << src;
return sstr.str();
}
template <class D, class S>
D lexical_cast(const S &src, texte_texte) {
return { begin(src), end(src) };
}
template <class D, class S>
D lexical_cast(const S &src, implicite) {
return src;
}
//#include <iostream>
//template <class T, class U>
// void diag() {
// using namespace std;
// cout << typeid(T).name() << " <-- \n\t" << typeid(U).name() << endl;
// cout << "\tMeme type? " << is_same<T,U>::value << endl;
// cout << "\t" << typeid(traits_conversion<T, U>::type()).name() << endl;
// }
template <class D, class S>
D lexical_cast(S &&src) {
//diag<D, S>();
return lexical_cast<D, S>(std::forward<S>(src), typename traits_conversion<D, S>::type{});
}
#include <iostream>
using namespace std;
class X {};
istream& operator>>(istream &is, X&) {
return is;
}
ostream& operator<<(ostream &os, const X&) {
return os;
}
struct Y {
Y() = default;
Y(const X&) {}
};
istream& operator>>(istream &is, Y&) {
return is;
}
ostream& operator<<(ostream &os, const Y&) {
return os;
}
int main() {
string s = "3";
auto i = lexical_cast<int>(s); // sérialisation par flux
s = lexical_cast<string>(i); // sérialisation par flux, texte en sortie
auto ws = lexical_cast<wstring>(i); // idem
s = lexical_cast<string>(s); // implicite
ws = lexical_cast<wstring>(s); // texte à texte
s = lexical_cast<string>(ws); // idem
X x;
Y y;
y = lexical_cast<Y>(x); // implicite
x = lexical_cast<X>(y); // via flux
}
Pour en savoir plus sur quelques outils auxiliaires utilisés ci-dessous, voir :
Ceci est une implémentation complète (dans la mesure de ce que j'ai choisi de faire; on pourrait aller plus loin et considérer les cas où const char* et const wchar_t* servent de type source pour les conversions texte à texte) de lexical_cast (dont nous avons discuté dans l'article disponible ici).
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
class implicite {};
class texte_dest {};
class texte_texte {};
class general {};
template <bool, class, class>
struct static_if_else;
template <class SiVrai, class SiFaux>
struct static_if_else<true, SiVrai, SiFaux> {
typedef SiVrai type;
};
template <class SiVrai, class SiFaux>
struct static_if_else<false, SiVrai, SiFaux> {
typedef SiFaux type;
};
template <class T, class U>
struct meme_type {
enum { value = false };
};
template <class T>
struct meme_type<T,T> {
enum { value = true };
};
template <class S, class D>
class est_convertible {
typedef char oui_type;
struct non_type { char _[3]; } ;
static oui_type test(D);
static non_type test(...);
static S creer();
public:
enum { value = sizeof(test(creer()))==sizeof(oui_type) };
};
template <class>
struct est_texte {
enum { value = false };
};
template <class C>
struct est_texte<basic_string<C> > {
enum { value = true };
};
template <class D, class S>
struct traits_conversion {
typedef typename static_if_else<
meme_type<D,S>::value || est_convertible<S, D>::value,
implicite,
typename static_if_else<
est_texte<D>::value,
typename static_if_else<
est_texte<S>::value,
texte_texte,
texte_dest
>::type,
general
>::type
>::type type;
};
template <class D, class S>
D lexical_cast(const S &src, general) {
stringstream sstr;
sstr << src;
D dest;
sstr >> dest;
return dest;
}
template <class D, class S>
D lexical_cast(const S &src, texte_dest) {
typedef typename
D::value_type value_type;
basic_stringstream<value_type> sstr;
sstr << src;
return sstr.str();
}
template <class D, class S>
D lexical_cast(const S &src, texte_texte) {
return D(src.begin(), src.end());
}
template <class D, class S>
D lexical_cast(const S &src, implicite) {
return src;
}
//template <class T, class U>
// void diag() {
// cout << typeid(T).name() << " <-- \n\t" << typeid(U).name() << endl;
// cout << "\tMeme type? " << meme_type<T,U>::value << endl;
// cout << "\t" << typeid(traits_conversion<T, U>::type()).name() << endl;
// }
template <class D, class S>
D lexical_cast(const S &src) {
//diag<D, S>();
return lexical_cast<D, S>(src, typename traits_conversion<D, S>::type());
}
class X {};
istream& operator>>(istream &is, X&) {
return is;
}
ostream& operator<<(ostream &os, const X&) {
return os;
}
struct Y {
Y() { }
Y(const X&) {}
};
istream& operator>>(istream& is, Y&) {
return is;
}
ostream& operator<<(ostream&os, const Y&) {
return os;
}
int main() {
string s = "3";
int i = lexical_cast<int>(s); // sérialisation par flux
s = lexical_cast<string>(i); // sérialisation par flux, texte en sortie
s = lexical_cast<string>(s); // implicite
wstring ws = lexical_cast<wstring>(s); // texte à texte
X x;
Y y;
y = lexical_cast<Y>(x); // implicite
x = lexical_cast<X>(y); // via flux
}