IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SL & STL C++ Discussion :

Algorithmes std::transform et std::for_each avec une std::map


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Inscrit en
    Février 2010
    Messages
    5
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Février 2010
    Messages : 5
    Par défaut Algorithmes std::transform et std::for_each avec une std::map
    Bonjour,

    je poursuis mon exploration de parties de la STL que j'avais jusqu'à présent hélas négligé : les algorithmes, et notamment, pour le cas qui nous intéresse, std::for_each et std::transform

    Je pense avoir correctement saisi leur fonctionnement dans plusieurs cas de figures, mais bien évidemment, je n'y suis pas encore parvenu pour le mien

    Je dispose d'une classe qui a, comme variable interne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     std::map<unsigned int, Parcours_Utilisateur> m_m_Etat_Parcours_Utilisateur;
    Dans une des fonctions membre de cette classe, je souhaite parcourir cette std::map, pour, en fonction d'un paramètre connu de cette seule fonction, y apporter d'éventuelles modifications. À ce jour, j'ai ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    bool ConnecteurClientImpl::supprime_Client_Connecte(int i_Identifiant_Client)
    {
        std::list<int> l_Temporaire(m_m_Etat_Parcours_Utilisateur.size(), i_Identifiant_Client );
        std::transform(m_m_Etat_Parcours_Utilisateur.begin(), m_m_Etat_Parcours_Utilisateur.end(), l_Temporaire.begin(), m_m_Etat_Parcours_Utilisateur.begin(), action_Sur_Suppression_Client());
    }
    Ce code ne passe pas à la compilation car il semblerait qu'il n'accepte pas un itérateur sur un std::pair pour l'itérateur dans lequel il doit écrire les résultats. D'où ma première question : est-ce possible ?

    Le second point qui ne me satisfait pas, c'est que afin de pouvoir communiquer à la fonction passée en paramètre de l'algorithme le paramètre i_Identifiant_Client, je n'ai pas trouvé mieux que de créer une liste et de la passer en tant que second itérateur de parcours pour le second paramètre de la fonction appelée. Y avait-il moyen de faire mieux ou plus élégants pour que la fonction action_Sur_Suppression_Client() puisse connaître i_Identifiant_Client ?


    Troisième point : cette fonction est aujourd'hui définie comme une fonction objet. Pourquoi pas, sauf que j'aurais préféré en faire une fonction membre de la classe ConnecteurClientImpl. Est-ce possible ?*Toutes mes tentatives ont été vaines, mais peut-être est-ce dû à mon manque de maîtrise des pointeurs de fonction et approchants …. Je n'y suis parvenu que si la fonction était statique, ce que je ne désire pas.


    Enfin, quatrième et dernier point : les différences entre for_each et transform ne sont pas toujours évidentes et ce que j'ai pu lire suite à mes recherches est contradictoire : il est affirmé dans une de mes lectures que for_each peut appeler des fonctions à effet de bord, alors que transform ne doit pas.

    Dans les cours C++, c'est quasi l'inverse qui y est expliqué : for_each ne doit en aucun cas modifier le contenu des itérateurs qu'il parcourt alors que transform, si.

    Lequel a raison ? Ou bien c'est moi qui ai mal compris le terme « side effect » du premier article qui est en anglais …

    Merci une nouvelle fois à ceux qui pourront m'apporter leurs lumière à ma compréhension incomplète.

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    (Re),
    Citation Envoyé par TH-Gemini Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    bool ConnecteurClientImpl::supprime_Client_Connecte(int i_Identifiant_Client)
    {
        std::list<int> l_Temporaire(m_m_Etat_Parcours_Utilisateur.size(), i_Identifiant_Client );
        std::transform(m_m_Etat_Parcours_Utilisateur.begin(), m_m_Etat_Parcours_Utilisateur.end(), l_Temporaire.begin(), m_m_Etat_Parcours_Utilisateur.begin(), action_Sur_Suppression_Client());
    }
    Ce code ne passe pas à la compilation car il semblerait qu'il n'accepte pas un itérateur sur un std::pair pour l'itérateur dans lequel il doit écrire les résultats. D'où ma première question : est-ce possible ?
    Oui. Il faut continuer à explorer la STL
    En fait, il faut regarder du côté des itérateurs d'insertion (<iterator>) et utiliser un std::inserter pour le résultat. Le foncteur devra bien sûr retourner une pair. Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    #include <iostream>
    #include <map>
    #include <list>
    #include <algorithm>
    #include <iterator>
    #include <sstream>
     
    struct foncteur
    {
       std::pair<int,int> operator()(std::pair<int,int>const&map_assoc_, int list_item_)const
       {
          return std::pair<int,int>(map_assoc_.first,map_assoc_.second+list_item_);
       }
    };
     
    struct output_foncteur
    {
       std::string operator()(std::pair<int,int>const&map_assoc_)const
       {
          std::ostringstream oss117;
          oss117<<"["<<map_assoc_.first<<"] = "<<map_assoc_.second;
          return oss117.str();
       }
    };
     
    int main()
    {
       std::map<int,int> mon_map;
       mon_map[0] = 0;
       mon_map[1] = 1;
       mon_map[2] = 2;
       mon_map[3] = 3;
       mon_map[4] = 4;
       std::list<int> ma_liste;
       ma_liste.push_back(0);
       ma_liste.push_back(1);
       ma_liste.push_back(2);
       ma_liste.push_back(3);
       ma_liste.push_back(4);
       std::map<int,int> resultat;
       std::transform(
          mon_map.begin(),
          mon_map.end(),
          ma_liste.begin(),
          std::inserter(resultat,resultat.begin()),
          foncteur()
       );
     
       std::transform(
          resultat.begin(),
          resultat.end(),
          std::ostream_iterator<std::string>(std::cout,"\n"),
          output_foncteur()
       );
     
       return 0;
    }
    Citation Envoyé par TH-Gemini Voir le message
    Le second point qui ne me satisfait pas, c'est que afin de pouvoir communiquer à la fonction passée en paramètre de l'algorithme le paramètre i_Identifiant_Client, je n'ai pas trouvé mieux que de créer une liste et de la passer en tant que second itérateur de parcours pour le second paramètre de la fonction appelée. Y avait-il moyen de faire mieux ou plus élégants pour que la fonction action_Sur_Suppression_Client() puisse connaître i_Identifiant_Client ?
    Toujours la STL : bind1st dans functional. Mais, là je te conseille une fonction libre plutôt qu'un foncteur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <iostream>
    #include <map>
    #include <list>
    #include <algorithm>
    #include <iterator>
    #include <sstream>
    #include <functional>
     
    std::pair<int,int> foncteur(std::pair<int,int>map_assoc_, int increment)
    {
       return std::pair<int,int>(map_assoc_.first,map_assoc_.second+increment);
    }
     
    struct output_foncteur
    {
       std::string operator()(std::pair<int,int>const&map_assoc_)const
       {
          std::ostringstream oss117;
          oss117<<"["<<map_assoc_.first<<"] = "<<map_assoc_.second;
          return oss117.str();
       }
    };
     
    int main()
    {
       std::map<int,int> mon_map;
       mon_map[0] = 0;
       mon_map[1] = 1;
       mon_map[2] = 2;
       mon_map[3] = 3;
       mon_map[4] = 4;
       std::map<int,int> resultat;
       std::transform(
          mon_map.begin(),
          mon_map.end(),
          std::inserter(resultat,resultat.begin()),
          std::bind2nd(std::ptr_fun(foncteur),10)
       );
     
       std::transform(
          resultat.begin(),
          resultat.end(),
          std::ostream_iterator<std::string>(std::cout,"\n"),
          output_foncteur()
       );
     
       return 0;
    }
    Si d'aventure tu avais vraiment besoin d'un foncteur, alors le plus simple serait de passer par Boost.Bind, car la STL après 2 arguments, elle ne sait plus faire

    Citation Envoyé par TH-Gemini Voir le message
    Troisième point : cette fonction est aujourd'hui définie comme une fonction objet. Pourquoi pas, sauf que j'aurais préféré en faire une fonction membre de la classe ConnecteurClientImpl. Est-ce possible ?*Toutes mes tentatives ont été vaines, mais peut-être est-ce dû à mon manque de maîtrise des pointeurs de fonction et approchants …. Je n'y suis parvenu que si la fonction était statique, ce que je ne désire pas.
    Pour utiliser une fonction membre comme foncteur : std::bind1st comme vu avec std::mem_fun toujours dans <functional>. Mais avec la STL, si ta fonction a plus d'un argument, ça ne le fait pas. Là aussi, il faudrait passer par boost
    :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    #include <iostream>
    #include <map>
    #include <list>
    #include <algorithm>
    #include <iterator>
    #include <sstream>
     
    struct output_foncteur
    {
       std::string operator()(std::pair<int,int>const&map_assoc_)const
       {
          std::ostringstream oss117;
          oss117<<"["<<map_assoc_.first<<"] = "<<map_assoc_.second;
          return oss117.str();
       }
    };
     
    struct ma_struct
    {
       int mon_increment;
       void just_do_it()
       {
          std::map<int,int> mon_map;
          mon_map[0] = 0;
          mon_map[1] = 1;
          mon_map[2] = 2;
          mon_map[3] = 3;
          mon_map[4] = 4;
          std::map<int,int> resultat;
          mon_increment = 10;
          std::transform(
             mon_map.begin(),
             mon_map.end(),
             std::inserter(resultat,resultat.begin()),
             std::bind1st(std::mem_fun(&ma_struct::do_operation),this)
          );
     
          std::transform(
             resultat.begin(),
             resultat.end(),
             std::ostream_iterator<std::string>(std::cout,"\n"),
             output_foncteur()
          );
       }
       std::pair<int,int> do_operation(std::pair<int,int> map_assoc_)const
       {
          return std::pair<int,int>(map_assoc_.first,map_assoc_.second+mon_increment);
       }
    };
     
     
    int main()
    {
       ma_struct().just_do_it();
       return 0;
    }
    Citation Envoyé par TH-Gemini Voir le message
    Enfin, quatrième et dernier point : les différences entre for_each et transform ne sont pas toujours évidentes et ce que j'ai pu lire suite à mes recherches est contradictoire : il est affirmé dans une de mes lectures que for_each peut appeler des fonctions à effet de bord, alors que transform ne doit pas.

    Dans les cours C++, c'est quasi l'inverse qui y est expliqué : for_each ne doit en aucun cas modifier le contenu des itérateurs qu'il parcourt alors que transform, si.

    Lequel a raison ? Ou bien c'est moi qui ai mal compris le terme « side effect » du premier article qui est en anglais …

    Merci une nouvelle fois à ceux qui pourront m'apporter leurs lumière à ma compréhension incomplète.
    Je ne les aie pas lu. Donc difficile de te répondre.

    La lecture du jour : Les algorithmes de la STL, par notre ami r0d

  3. #3
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    J'ai survolé les 2 articles. La confusion vient de ce que l'on peut ou non modifier.
    std::for_each prend 2 arguments, un range (begin,end) et un foncteur qui s'applique sur chaque élément du range.
    Se pose trois questions :
    1. Peut-on modifier le range ?
    2. Peut-on modifier le foncteur ?
    3. Peut-on modifier l'élément donné en paramètre ?

    (1) : non (pour des raisons évidentes).
    (2) : le premier article dit oui et je suis convaincu par l'argumentaire. Rien dans la norme n'indique le contraire et je n'ai pas vu d'exemple où le compilateur râle.
    (3) : oui. Selon que le paramètre sera pris par référence ou par référence constante (ou valeur), on pourra ou non modifier le paramètre.

    std::transform prend 3 arguments : un range (begin,end), un itérateur d'insertion et un foncteur qui prend en argument l'élément et retourne la valeur qui est assignée au second itérateur.

    Se pose quatre questions :
    1. Peut-on modifier le range ?
    2. Peut-on modifier l'itérateur ?
    3. Peut-on modifier le foncteur ?
    4. Peut-on modifier l'élément donné en paramètre ?

    (1) : non (pour des raisons évidentes).
    (2) : std::transform le fait pour toi en ajoutant le résultat du foncteur et en incrémentant l'itérateur.
    (3) : non : c'est ce que dit l'article en anglais et l'explication me semble pertinente : tu ne peux modifier le foncteur car l'ordre d'appel n'étant pas garanti, tu ne maîtrises pas les effets de bords. Et la norme est très clair là dessus :
    Citation Envoyé par Norme/Requires std::transform
    op and binary_op shall not have any side effects.
    (4) à mon avis la question la plus délicate. La norme dit que le foncteur ne doit pas avoir d'effet de bord. Donc en toute logique, la réponse devrait être non.

    Il s'en suit que pour std::for_each, les foncteurs peuvent être :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    struct foncteur
    {
        void operator()(std::pair<int const ,int>&map_assoc_); // tout est modifiable
        void operator()(std::pair<int const ,int>&map_assoc_)const;  // on peut modifier le paramètre mais pas le foncteur
        void operator()(std::pair<int const ,int>const &map_assoc_); // on peut modifier le foncteur mais pas le paramètre
        void operator()(std::pair<int const ,int>&map_assoc_)const; // on peut rien modifier
    };
    Et pour std::transform:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    struct foncteur
    {
        void operator()(std::pair<int const ,int>&map_assoc_)const; // on peut rien modifier
    };

  4. #4
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    std::for_each(first,last,op) peut être appelé pour modifier les éléments d'un range - je ne vois pas de problème avec ça, et la norme non plus. L'opération effectuée est op(*(first + n)), mais op a tout à fait le droit de prendre une référence non constante en paramètre et donc de modifier *(first + n).

    std::transform(first,last,result,op) est utilisé pour transformer les valeurs dans un range en d'autres valeurs - avec le foncteur correspondant. Si nécessaire, la transformation peut se faire 'en place' (first == result) puisque transform effectue l'opération suivant : *(result + n) = op(*(first + n)). Ceci dit, lorsqu'on utilise cette transformation en place, il convient de se demander si le jeu en vaut la chandelle - on risque de forcer l'appel à l'opérateur de copie alors que ce n'est pas nécessaire (autant utiliser for_each()). Bien évidemment, la question ne se pose plus si on utilise la forme de transform qui prends deux range en entrée (std::tranform(first1, last1, first2, result, binary_op), ou l'opération effectuée est *(result + n) = binary_op(*(first1 + n), *(first2 + n)).

    Et pour répondre à ta question, le problème vient de action_Sur_Suppression_Client(), qui ne doit pas prendre les bons paramètres ou renvoyer le bon type. Cette classe doit être définie comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct action_Sur_Suppression_Client
    {
      int operator()(const std::pair<unsigned int, Parcours_Utilisateur>& valeur)
      { ... }
    };
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Et pour répondre à ta question, le problème vient de action_Sur_Suppression_Client(), qui ne doit pas prendre les bons paramètres ou renvoyer le bon type. Cette classe doit être définie comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct action_Sur_Suppression_Client
    {
      int operator()(const std::pair<unsigned int, Parcours_Utilisateur>& valeur)
      { ... }
    };
    J'ai plutôt l'impression que son problème vient du dernier itérateur, celui qui reçoit le résultat de la transformation. C'est le begin() d'un map. Et l'itérateur d'un map ne peut être utilisé pour ajouter ou mettre à jour des éléments dans la map. D'où la nécessité de passer par un std::inserter.

  6. #6
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    J'ai plutôt l'impression que son problème vient du dernier itérateur, celui qui reçoit le résultat de la transformation. C'est le begin() d'un map. Et l'itérateur d'un map ne peut être utilisé pour ajouter ou mettre à jour des éléments dans la map. D'où la nécessité de passer par un std::inserter.
    Tu sous-entends que j'ai mal lu le post de TH-Gemini ?

    Attention hein, ce n'est pas parce que c'est vrai que je vais me laisser faire. J'ai ma fierté moi monsieur (et un mal de crane carabiné depuis 2 jours. Tiens, et si j'allais voir un médecin ?)
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Tu sous-entends que j'ai mal lu le post de TH-Gemini ?
    Pour être franc, c'est parce que j'ai eu le même réflexe que toi : le PO a du se tromper sur la signature du foncteur, chose assez courante avec un map. Puis, je me suis dit que le message était suffisamment long et argumenté pour que ce ne soit pas ce type d'erreur. J'ai regardé à plusieurs reprises le code avant de voir le problème sur l'itérateur résultat.

    Citation Envoyé par Emmanuel Deloget Voir le message
    et un mal de crane carabiné depuis 2 jours. Tiens, et si j'allais voir un médecin ?)
    2 approches :
    -> la chimie EST-UN progrès dont il faut savoir bénéficier ;
    -> il faut laisser le temps au temps et accepter la souffrance.

  8. #8
    Nouveau membre du Club
    Inscrit en
    Février 2010
    Messages
    5
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Février 2010
    Messages : 5
    Par défaut
    Encore une fois, merci pour vos précieuses informations. Je testerai les suggestions de 3DArchi dès que je parviendrais à trouver un petit peu de temps. Son diagnostic semble le bon, car c'était bien sur le paramètre itérateur résultat que se situait mon problème.

    Je vais en profiter pour découvrir un peu mieux les différents types d'itérateur, en espérant que j'y trouverai bientôt des cas d'utilisation pour me faire la main avec.

    Concernant mon message initial, j'aurais peut-être dû également préciser le foncteur action_Sur_Suppression_Client(), ce qui aurait permis d'éviter de créer la confusion. Pas taper je ferais mieux la prochaine fois Le mal de tête devrait ainsi vite passer

Discussions similaires

  1. [Mapping] Problème mapping avec une collection Map
    Par hpnet dans le forum Hibernate
    Réponses: 2
    Dernier message: 16/07/2012, 11h25
  2. [XSLT 1.0] Transformation d'un XML avec une hiérarchie imbriquée en XML simple
    Par ekoralewski dans le forum XSL/XSLT/XPATH
    Réponses: 0
    Dernier message: 25/03/2011, 11h24
  3. [CakePHP] Valider un formulaire avec une image map
    Par pausg dans le forum Bibliothèques et frameworks
    Réponses: 1
    Dernier message: 26/11/2010, 12h33
  4. la tag logic iterate avec une liste Map
    Par casawi dans le forum Struts 1
    Réponses: 9
    Dernier message: 07/03/2007, 20h27
  5. Problème de fonction "const" avec une std::map
    Par Clad3 dans le forum SL & STL
    Réponses: 3
    Dernier message: 02/01/2007, 12h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo