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

Langage C++ Discussion :

problème récurrent (dans tous les sens du terme ^^)


Sujet :

Langage C++

  1. #1
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Points : 176
    Points
    176
    Par défaut problème récurrent (dans tous les sens du terme ^^)
    Bonjour à tous,

    j'ai déjà eu ce type de "problèmes" mais je pense être maintenant mieux en mesure du comprendre comment le résoudre.
    En fait c'est assez simple, et je peux diviser le problème en 3 sous question

    1] Est-ce possible d'utiliser l'objet standard std::tuple avec un nombre variable de classes avec le même paramètre template :
    exemple : j'ai une classe A template, est-ce possible de créer un objet std::tuple<A<T>,A<U>,...,A<Machin>> avec un nombre de A<...> pouvant varier (selon les désirs de l'utilisateur de la classe)

    2] Si ce n'est pas possible, alors on crée une classe tuple "maison" :
    par exemple cette classe
    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
    template <typename S, typename ... T>
    class MultiDrawAndPromptConditionObject;
     
    template <typename S>
    class MultiDrawAndPromptConditionObject<S>;
     
    template <typename S, typename End>
    class MultiDrawAndPromptConditionObject<S, End>
    {
        public:
            MultiDrawAndPromptConditionObject(DrawAndPromptConditionObject<S,End> &obj);
     
            std::tuple<End> launch();
     
        protected:
            DrawAndPromptConditionObject<S,End> &m_obj;
    };
     
    template <typename S, typename Head, typename ... Args>
    class MultiDrawAndPromptConditionObject<S,Head,Args...>
    {
        public:
            MultiDrawAndPromptConditionObject(DrawAndPromptConditionObject<S,Head> &obj,
                                                                    MultiDrawAndPromptConditionObject<S,Args...> &encapsuledObj);
     
            std::tuple<Head,Args...> launch();
     
        private:
            DrawAndPromptConditionObject<S,Head> &m_obj;
            MultiDrawAndPromptConditionObject<S,Args...> &m_encapsuledObj;
    };
    Cette classe MultiDraw... (appelons la A) est simplement un empilement de référence sur d'autres A déjà définis composées à chaque fois d'une classe DrawAnd...(appelons la B) qui constitue la principale raison de ce tuple.
    A noter que les références seront prochainement remplacées par des pointeurs intelligents histoire de rendre ça plus propre enfin pour le moment j'attends que ça fonctionne
    L'instanciatiation fonctionne comme il faut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MultiDrawAndPromptConditionObject<const std::vector<std::string> &, int> m1(testHauteur);
        MultiDrawAndPromptConditionObject<const std::vector<std::string> &, int, int> m2(testLargeur,m1);
        MultiDrawAndPromptConditionObject<const std::vector<std::string> &, std::string, int, int> m3(promptStr,m2);
    Ma question est la suivante :
    Cette instanciation est lourde et l'est de plus en plus. Y aurait-il un moyen d'instancier le même objet m3 ci-dessus juste en déclarant quelque chose du type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MultiDrawAndPromptConditionObject<const std::vector<std::string> &, std::string, int, int> m3 = makeMultiMachin(promptStr,testHauteur,testLargeur);
    Mon instinct me soufflerait que oui (enfin faut pas être très doué pour penser que c'est possible ^^) après je ne vois pas comment faire.

    3] Cette question concerne un aspect "pratique" des tuples qui fait que ma classe ne compile pas encore :
    Voici ma fonction launch dans le cas de la classe A la plus "générale" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename S, typename Head, typename ... Args>
    std::tuple<Head,Args...> MultiDrawAndPromptConditionObject<S, Head, Args...>::launch()
    {
        return std::make_tuple(m_obj.launch(),m_encapsuledObj.launch());
    }
    Dans ma fonction Launch qui retourne un tuple et qui ne marche bien sur pas ce qui est tout à fait prévisible puisque m_encapsuledObj.launch() retourne un tuple de <a,b,...,> au lieu de retourner quelque chose du type a,b,....
    Ma question est la suivante : comment découper un tuple pour pouvoir placer son contenu dans un autre tuple avec un autre élément devant le contenu du tuple (c'est pas très bien expliqué mais en fait ça consiste simplement à expliquer comment résoudre le problème posé par le code ci-dessus)

    Voilà j'espère que vous arriverez à comprendre ce que j'exprime souvent avec difficultés

  2. #2
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par Lintel-oo Voir le message
    Ma question est la suivante : comment découper un tuple pour pouvoir placer son contenu dans un autre tuple avec un autre élément devant le contenu du tuple (c'est pas très bien expliqué mais en fait ça consiste simplement à expliquer comment résoudre le problème posé par le code ci-dessus)
    Regarde du coté de la fonction get et de la structure tuple_element.

    Pour le reste, je m'avoue "un peu" dépassé

  3. #3
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    1/ Je ne suis pas certain d'avoir compris ton besoin, mais je dirais que les variadics templates répondent à ton problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    template<class> struct A;
     
    template<class... Arg>
    class B
    {
      using type = std::tuple<A<Arg>...>;
    };
    (non testé, mais l'idée est là, à adapter au besoin)

    Si ce n'est pas ca que tu cherches à faire, alors je n'ai pas compris ce point.

    2/ Avec ton make_ tu peux déterminer les types des paramètres (déduction template), donc tu connais le type de retour, ensuite il te suffit de faire des appels récursif à tes makes. Là où tu vas être "un peu" géné c'est que tu as utilisé des références pour la récursion dans ton tuple, utiliser directement des objets est une solution viable.

    NB: Et utiliser correctement std::tuple et les variadics est une solution bien plus simple, cf point 1.
    NB2: Et sans le C++11 on a boost::pp.

    3/ En utilisant get tu devrais pouvoir t'en sortir. L'algorithme étant de déplier tes tuples dans des fonctions/foncteurs templates variadics pour tout regrouper au final.

  4. #4
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Points : 176
    Points
    176
    Par défaut
    Merci beaucoup pour ces indications c'est exactement ce que je cherchais je vous tient au courant


    Edit :
    3/ En utilisant get tu devrais pouvoir t'en sortir. L'algorithme étant de déplier tes tuples dans des fonctions/foncteurs templates variadics pour tout regrouper au final
    Oui c'est ça que je voudrais faire maintenant, mais je me demandais juste quel était le point d'arrêt : existe-t-il quelque chose dans la classe tuple qui puisse m'indiquer quand on doit arrêter d'appeler get, autrement dit qui appelle autant de fois get qu'il y a d'éléments dans le tuple ?


    Edit 2 : en fait j'ai utilisé std::tuple_size et ça compile maintenant c'est bon par contre je sais pas si ça va marcher ni si c'est très propre qu'en pensez vous ?
    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
    template<unsigned int MAX, unsigned int N, typename S, class... Arg>
    void modifyTuple(std::tuple<Arg...> &toReturn, const std::tuple<DrawAndPromptConditionObject<S,Arg>...> &t)
    {
        if(N<MAX)
        {
            std::get<N>(toReturn) = std::get<N>(t).launch();
            modifyTuple<MAX,N+1,S,Arg...>(toReturn,t);
        }
    }
     
    template<typename S, class... Arg>
    class MultiDrawAndPromptConditionObject2
    {
        public:
            MultiDrawAndPromptConditionObject2(DrawAndPromptConditionObject<S,Arg>... arg) :
                m_tuple(arg...)
            {}
     
            std::tuple<Arg...> launch()
            {
                std::tuple<Arg...> toReturn;
                modifyTuple<std::tuple_size<decltype(m_tuple)>::value,0,S,Arg...>(toReturn,m_tuple);
                return toReturn;
            }
     
        private:
            std::tuple<DrawAndPromptConditionObject<S,Arg>...> m_tuple;
    };

    En fait il s'avère que ça fonctionne pas si bien que ça puisque j'obtiens plein d'erreurs de compilation lorsque j'essaie de déclarer une instance de la classe notamment au niveau de l'appel récursif
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    modifyTuple<MAX,N+1,S,Arg...>(toReturn,t);
    Je précise également que j'ai légèrement changé la classe en question pour plus de possibilités :
    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
    template<template <typename A, typename B> class T, unsigned int MAX, unsigned int N, typename S, class... Arg>
    void modifyTuple(std::tuple<Arg...> &toReturn, std::tuple<T<S,Arg>...> &t)
    {
        if(N<MAX)
        {
            std::get<N>(toReturn) = std::get<N>(t).launch();
            modifyTuple<T,MAX,N+1,S,Arg...>(toReturn,t);
        }
    }
     
    template<template <typename A, typename B> class T, typename S, class... Arg>
    class MultiDrawAndPromptConditionObject
    {
        public:
            MultiDrawAndPromptConditionObject(T<S,Arg>... arg) :
                m_tuple(arg...)
            {}
     
            std::tuple<Arg...> launch()
            {
                std::tuple<Arg...> toReturn;
                modifyTuple<T,std::tuple_size<decltype(m_tuple)>::value,0,S,Arg...>(toReturn,m_tuple);
                return toReturn;
            }
     
        private:
            std::tuple<T<S,Arg>...> m_tuple;
    };

  5. #5
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Points : 176
    Points
    176
    Par défaut
    En fait j'avais totalement zappé qu'on ne pouvait spécialiser une fonction partiellement donc j'ai tout mis dans une classe et là je crois que ça fonctionne :
    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
    template<template <typename A, typename B> class T, unsigned int MAX, unsigned int N, typename S, class... Arg>
    class modifyTuple
    {
        public:
            modifyTuple(std::tuple<Arg...> &toReturn, std::tuple<T<S,Arg>...> &t)
            {
                std::get<N>(toReturn) = std::get<N>(t).launch();
                modifyTuple<T,MAX,N+1,S,Arg...> m(toReturn,t);
            }
    };
     
    template<template <typename A, typename B> class T, unsigned int MAX, typename S, class... Arg>
    class modifyTuple<T,MAX,MAX,S,Arg...>
    {
        public:
            modifyTuple(std::tuple<Arg...> &toReturn, std::tuple<T<S,Arg>...> &t) {}
    };
    Enfin ça reste à confirmer

    Edit : au final je pense qu'on peut considérer le problème comme résolu merci pour votre aide

  6. #6
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonjour,

    (Juste une remarque, j'ai écrit le post ci-dessous alors qu'il n'y avait encore que le post initial, donc il ne tient pas compte des autres messages depuis)

    Personnellement, je ne suis vraiment pas fan d'utiliser la récurrence en manipulant des tuple car comme ton exemple le montre on se retrouve rapidement sans trop le vouloir avec des structures emboitées dans des structures à la manière des poupées russes ou encore à manipuler des types du style tuple<A, tuple<B, tuple<C, ... >>>.

    Je pense qu'il vaut mieux le plus tôt possible essayer "d’aplatir" la représentation c'est à dire dans ton cas passer d'une classe Multi qui possède un draw + un Multi qui contient à son tour un Draw + un Multi etc. etc. à un unique objet Multi qui ne contient qu'un seul membre de type tuple collectant tous les draw : tuple<Draw<S, A>, Draw<S, B>, Draw<S, C> ...>

    Ex :
    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
     
    template <typename S, typename... Args>
    class MultiDrawAndPromptConditionObject
    {
    public:
       MultiDrawAndPromptConditionObject(DrawAndPromptConditionObject<S, Args>&...  DAndPConditions_):
    		DAndPConditions(DAndPConditions_...)
       {
       }
     
    private:
       std::tuple<DrawAndPromptConditionObject<S, Args>&... > DAndPConditions;
    };
     
    int main()
    {
       DrawAndPromptConditionObject<const std::vector<std::string> &, int> testHauteur;
       DrawAndPromptConditionObject<const std::vector<std::string> &, int> testLargeur;
       DrawAndPromptConditionObject<const std::vector<std::string> &, std::string> promptStr;
     
       MultiDrawAndPromptConditionObject<const std::vector<std::string> &, int, int, std::string> m1(testHauteur, testLargeur, promptStr);
    Maintenant pour launch() :
    Même constat, si on implémente launch de manière récursive de nombreuses difficultés surgissent et il va probablement falloir créer des tas de struct helper, des spécialisations partielles, rajouter des subtilités pour éviter d'avoir un résultat sous la forme tuple<A, tuple<B, tuple<C, ...>>> etc.

    Une autre solution consiste à utiliser des listes d'indices. L'idée est d'avoir une structure template <size_t...> struct index_tuple ainsi qu'une structure template <typename... Args>struct to_index_tuple(/* */}; définit de telle sorte que to_index_tuple<A, B, C>::type vaut en fait index_tuple<0, 1, 2>. Grâce à to_index_tuple on peut donc récupérer assez facilement une liste d'indice de même taille que le nombre d’élément dans le tuple, le but étant de pouvoir utiliser cette liste en conjonction avec std::get pour pouvoir appliquer uniformément une opération sur chaque élément du tuple en une seule passe, sous la forme std::get<Indices>(tup)... (donc après expansion que ressemblera à std::get<0>(tup), std::get<1>(tup), std::get<2>(tup) etc). Le gros avantage c'est que la partie difficile (la récursion) est masquée dans la génération de la liste d'indice.

    D'ailleurs on peut remarquer que index_tuple et to_index_tuple sont tellement utiles qu'il ont été proposé pour intégration dans la biblio standard pour le futur C++14 (Proposal N3493)

    Muni de to_index_tuple, implémentation de launch() devient très simple :

    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
     
    template <typename S, typename... Args>
    class MultiDrawAndPromptConditionObject
    {
    public:
       MultiDrawAndPromptConditionObject(const DrawAndPromptConditionObject<S, Args>&...  DAndPConditions_):
    		DAndPConditions(DAndPConditions_...)
    	{
    	}
     
       std::tuple<Args...> launch()
       {
          typename to_index_tuple<Args...>::type indices;
          return launch_imp(indices);
       }
     
    private:
       template <size_t... Idx>
       std::tuple<Args...> launch_imp(index_tuple<Idx...>)
       {
          return std::make_tuple(std::get<Idx>(DAndPConditions).launch()...);
       }
     
       std::tuple<DrawAndPromptConditionObject<S, Args>... > DAndPConditions;
    };
    Le code complet :

    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
     
    #include <tuple>
    #include <iostream>
    #include <vector>
    #include <tuple>
    #include <string>
    #include <typeinfo>
     
    template <size_t...>
    struct index_tuple {};
     
    template <size_t Sp, class IntTuple, size_t Ep>
    struct make_index_tuple_imp;
     
    template <size_t Sp, size_t... Indices, size_t Ep>
    struct make_index_tuple_imp<Sp, index_tuple<Indices...>, Ep>
    {
        typedef typename make_index_tuple_imp<Sp+1, index_tuple<Indices..., Sp>, Ep>::type type;
    };
     
    template <size_t Ep, size_t ...Indices>
    struct make_index_tuple_imp<Ep, index_tuple<Indices...>, Ep>
    {
        typedef index_tuple<Indices...> type;
    };
     
    template <size_t Ep, size_t Sp>
    struct  make_index_tuple
    {
        typedef typename make_index_tuple_imp<Sp, index_tuple<>, Ep>::type type;
    };
     
     
     template<typename... Types>
     struct to_index_tuple
     {
    	 typedef typename make_index_tuple<sizeof...(Types), 0>::type type;
     };
     
     
    template <typename T, typename U>
    struct DrawAndPromptConditionObject
    {
    	U launch()
    	{
    	    std::cout << "launch " << typeid(U).name() << std::endl;
    	    return U();
        }
    };
     
     
    template <typename S, typename... Args>
    class MultiDrawAndPromptConditionObject
    {
     
    public:
    	MultiDrawAndPromptConditionObject(const DrawAndPromptConditionObject<S, Args>&...  DAndPConditions_):
    		DAndPConditions(DAndPConditions_...)
    	{
    	}
     
    	std::tuple<Args...> launch()
    	{
    		return launch_imp(typename to_index_tuple<Args...>::type());
    	}
     
    private:
     
    	template <size_t... Idx>
    	std::tuple<Args...> launch_imp(index_tuple<Idx...>)
    	{
    		return std::make_tuple(std::get<Idx>(DAndPConditions).launch()...);
    	}
     
    	 std::tuple<DrawAndPromptConditionObject<S, Args>... > DAndPConditions;
    };
     
     
    int main()
    {
    	DrawAndPromptConditionObject<const std::vector<std::string> &, int> testHauteur;
    	DrawAndPromptConditionObject<const std::vector<std::string> &, int> testLargeur;
    	DrawAndPromptConditionObject<const std::vector<std::string> &, std::string> promptStr;
     
    	MultiDrawAndPromptConditionObject<const std::vector<std::string> &, int, int, std::string> m1(testHauteur, testLargeur, promptStr);
    	m1.launch();
    }

  7. #7
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Points : 176
    Points
    176
    Par défaut
    Merci pour cette solution, mais je pense que je vais conserver ce que j'ai fait dans un sens c'est moins complexe et ça marche comme je le souhaite et sans tuple imbriqué.
    Juste une remarque, j'ai écrit le post ci-dessous alors qu'il n'y avait encore que le post initial, donc il ne tient pas compte des autres messages depuis
    Je comprends mieux
    Je reste donc sur cela
    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
    template<template <typename A, typename B> class T, unsigned int MAX, unsigned int N, typename S, class... Arg>
    class modifyTuple
    {
        public:
            modifyTuple(std::tuple<Arg...> &toReturn, std::tuple<T<S,Arg>...> &t)
            {
                std::get<N>(toReturn) = std::get<N>(t).launch();
                modifyTuple<T,MAX,N+1,S,Arg...>(toReturn,t);
            }
    };
     
    template<template <typename A, typename B> class T, unsigned int MAX, typename S, class... Arg>
    class modifyTuple<T,MAX,MAX,S,Arg...>
    {
        public:
            modifyTuple(std::tuple<Arg...> &toReturn, std::tuple<T<S,Arg>...> &t) {}
    };
     
     
     
    template<template <typename A, typename B> class T, typename S, class... Arg>
    class MultiDrawAndPromptConditionObject
    {
        public:
            MultiDrawAndPromptConditionObject(const T<S,Arg> &... arg) :
                m_tuple(arg...)
            {}
     
            std::tuple<Arg...> launch()
            {
                std::tuple<Arg...> toReturn;
                modifyTuple<T,std::tuple_size<decltype(m_tuple)>::value,0,S,Arg...>(toReturn,m_tuple);
                return toReturn;
            }
     
        private:
            std::tuple<T<S,Arg>...> m_tuple;
    };

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. lecture d'une plage dans tous les sens
    Par didier.schmit dans le forum Macros et VBA Excel
    Réponses: 11
    Dernier message: 05/04/2009, 09h04

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