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

C++ Discussion :

Problèmes de conversions.


Sujet :

C++

  1. #1
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut Problèmes de conversions.
    Bonjour à tous, j'aimerais savoir quel serait le meilleur moyen de définir mes types de façon à avoir les conversions que je souhaite.

    Soit une classe template Matrix possédant 6 types d'iterateur :
    -normal_iterator (ne garantit que de pouvoir accéder aux cases à toutes les cases mais ne garantit pas l'ordre).
    -const_normal_iterator
    -lign_iterator
    -const_lign_iterator
    -column_iterator
    -const column_iterator

    Existe-t'il un moyen qui demande le moins de code possible (plus il y a de code, plus les évolutions sont difficiles), de permettre à ces conversions :

    -Il n'existe aucun lien entre deux iterators (qu'ils soient lign, const, column, ou normal) qui ne viennent pas de la même classe matrice (pas les même template).
    -On peut comparer deux iterators (operator ==, !=) mais pas les operator < ou >.
    -Les conversions iterator->normal_iterator ou const_normal_iterator sont implicites.
    -Les conversions qqch_iterator->const_qqch_iterator sont implicites
    -Les conversions normal_iterator->qqch_iterator ou const_qqch_iterator sont explicites.
    -Les conversions const_normal_iterator->const_qqch_iterator sont explicites.
    -Il existe les operator +,-,+=,-=,... sur tous les iterators.
    -Conversion explicite pointeur<->iterator
    -J'ai peut être oublier qqch.

    Aujourd'hui, j'ai définit tous ces nested à la main, avec toutes leurs fonctions (assez énorme : environ 30 par iterator) et sa me donne :

    -un header illisible.
    -un inl illisible.
    -une galère quand je veux rajouter une classe de politique à ma classe Matrix.
    -une galère si je veux modifier quoi que ce soit.

    Je vous demande donc votre point de vue :

    -Est-ce que certains de mes iterator peuvent-ils devenir des typedefs ?
    -Si oui, lesquels, si non y a t-il plus simple ?

    Merci.

  2. #2
    Membre averti
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Points : 410
    Points
    410
    Par défaut
    Bonjour,

    Toujours pas de réponse après 24h. Je pense que ta question est large et fait peur. En tout cas c'est l'effet qu'elle m'a fait hier quand je l'ai lue.

    Bon je me risque à donner mon avis, même s'il ne répond pas à la question

    Je crois qu'il existe dans tout programme un peu compliqué, des structures de bas-niveau qui sont complexes à écrire et à maintenir, et pour lesquelles la meilleure stratégie de dev consiste à les peaufiner et à ne plus y revenir.

    J'insiste sur le "bas-niveau". Il faut que la fonctionnalité de ces structures soit non-équivoque et peu sujet à évolution.

    Est-ce que ta classe Matrix ne fait-elle pas partie de ces structures-là ?

    -une galère quand je veux rajouter une classe de politique à ma classe Matrix.
    Pourquoi ?

  3. #3
    Membre confirmé Avatar de Nhaps
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    350
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Nord (Nord Pas de Calais)

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

    Informations forums :
    Inscription : Mars 2011
    Messages : 350
    Points : 603
    Points
    603
    Par défaut
    Personellement, hier j'ai cru tombé face à un devoir. Et j'ai eu plus que trés peur !
    Windev 23 - SQL SERVER - PHP
    Play : TFT - Jeux indé

  4. #4
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Personellement, hier j'ai cru tombé face à un devoir. Et j'ai eu plus que trés peur !
    Non, ce n'est pas un devoir.

    Est-ce que ta classe Matrix ne fait-elle pas partie de ces structures-là ?
    Oui, disons que je souhaite avoir une classe Matrix pour mes différents projets qui réponde à mes besoin tout en étant facile d'utilisation (pas forcément en performances).

    Pourquoi ?
    Parce que dans les .inl, je suis obligé de spécifier les template de la classe matrix. En plus, si on imagine que je veuille rajouter de nouveaux types d'iterator (reverse_iterator), j'en aurait pour en temps assez énorme.

    Conclusion : Les conversions en c++, c'est horrible quand on veut faire quelque chose de compliquer !

  5. #5
    Membre averti
    Homme Profil pro
    Analyse système
    Inscrit en
    Novembre 2008
    Messages
    227
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyse système
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2008
    Messages : 227
    Points : 311
    Points
    311
    Par défaut
    Peut-être un peu hors sujet, mais pourquoi ne pas utiliser la notion de matrice qui existe dans LAPACK++, surtout que du coup tu profites de toutes les méthodes de manipulation de matrice. Il y a un équivalent (basée dessus) dans Boost également.

  6. #6
    Membre averti
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Points : 410
    Points
    410
    Par défaut
    Re-bonjour,

    Si tu lis les headers de la stl, tu verras que c'est le même problème.
    Mais de fait, cela fonctionne bien parce qu'on n'y touche jamais.

    Je ne pense pas qu'il y ait de solution particulièrement élégante (au sens : non-verbeuse) à des problèmes de briques de bases omnipotentes et ultra-réutilisables comme celle que tu veux faire.

    En tout cas je n'en ai pas. Je serais ravi d'avoir tors.

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Voilà typiquement le problème fait pour la programmation générique générative.
    C'est tout à fait possible de faire ceci sans redondance de code comme tu peux avoir l'impression de le faire et en gérant la combinatoire. Un grand pas dans la puissance du C++ .Mots clés de ta recherche : type list, politiques, traits, Boost.MPL

    Quelques liens :
    Loki
    (au moins pour toute la doc)
    Boost.MPL
    Un tuto sur les traits et politiques
    Un billet introductif de notre ami Alp (en anglais)

    Tu es mur pour Modern C++ Design, C++ Template metaprogramming, et autres joyeusetés

  8. #8
    Membre averti
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Points : 410
    Points
    410
    Par défaut
    Arghhhhh.

    [Complètement HS]

    3DA, à force de lire ici ta motivation sur le sujet (depuis quelques semaines), tu va finir par réveiller en moi les démons de la métaprog que j'avais finalement réussi à faire taire.

    Est-ce que tu peux me citer un projet dont les codes sont accessible (et pourquoi pas qui recherche quelques jours de main d'œuvre), qui utilise vraiment du gros template et pour lequel on peut entrer dans le code sans s'arracher la moitié des cheveux ?

    [/HS]

  9. #9
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    3DArchi, après avoir regarder MDC++ et boost::MPL, je ne voit pas ce qui pourrait m'aider (pour les opérateurs qui ne sont pas membre, je vois comment faire, mais sinon ?)
    Serait-il possible que tu me donnes un indice supplémentaire.

  10. #10
    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
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Je ne me suis pas penché dans le détail sur ton problème, mais à priori, ça devrait ressembler à des concepts (Comparable ==, NonOrdonnable (<, >), conversion implicites, conversions explicites, etc..), à des types lists sur tes itérateurs, à un parcours sur les différents couples possible (c'est certainement là qu'est ton explosion combinatoire) et d'appliquer une méta-fonction qui prend les couples, va chercher un trait sur les concepts à définir et les définis.

  11. #11
    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
    Tu pourrais faire quelque chose de ce genre (je me suis préoccupé que des conversions)
    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
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
     
    /*Debut de la partie TMP*/
     
    //Début de la définition des listes de types template
    template<template<class> class...>
    struct TTL;
     
    template<class T>
    class NullTT;
     
    typedef TTL<NullTT> NullTTL;
    //Fin
     
    //Class de traits, à spécialiser, cf exemple
    template<template<class> class>
    struct conv_traits
    {
      //Par défaut pas de conversion
      typedef NullTTL ImplicitTTL;
      typedef NullTTL ExplicitTTL;
    };
     
    //Classe contenant les données de l'itérateur
    //Séparation interface/donnée via une composition
    template<class>
    struct impl_iterator
    {
      /*fonctions communes nécessaires*/
       ~impl_iterator(){ /*code si besoin*/ }
    };
     
    //Classe de concepte pour les conversion
    //s'intercalle entre les données et l'interface
    template<class, class, class>
    struct conv_concept;
     
    //Spécialisations pour un parcourt TMP des 2 listes
    //Cas de bases et cas de récurrences
    template<class T>
    struct conv_concept<T,NullTTL,NullTTL>
    {
      protected:
        impl_iterator<T> m_impl;
        ~conv_concept(){}; //cf MC++D
     
      public:
        //constructeur de copie et opérateur d'assignation par défaut
        conv_concept(const conv_concept&) =default;
        conv_concept& operator=(const conv_concept&) =default;
        //constructeur depuis l'implémentation
        conv_concept(impl_iterator<T>& impl) : m_impl(impl) {}
    };
     
    template<class T, template<class> class Head, template<class> class... Tail, class U>
    struct conv_concept<T,TTL<Head,Tail...>,U> : conv_concept<T,TTL<Tail...>,U>
    {
      private:
        typedef conv_concept<T,TTL<Tail...>,U> base;
     
      public:
        //constructeur de copie et opérateur d'assignation par défaut
        conv_concept(const conv_concept&) =default;
        conv_concept& operator=(const conv_concept&) =default;
        //constructeur depuis l'implémentation
        conv_concept(impl_iterator<T>& impl) : base(impl) {}
     
        operator Head<T>()
        { return Head<T>(this->m_impl); }
     
      protected:
        ~conv_concept(){}; //cf MC++D
    };
     
    template<class T, template<class> class Head, template<class> class... Tail>
    struct conv_concept<T,NullTTL,TTL<Head,Tail...>> : conv_concept<T,NullTTL,TTL<Tail...> >
    {
      private:
        typedef conv_concept<T,NullTTL,TTL<Tail...> > base;
     
      public:
        //constructeur de copie et opérateur d'assignation par défaut
        conv_concept(const conv_concept&) =default;
        conv_concept& operator=(const conv_concept&) =default;
        //constructeur depuis l'implémentation
        conv_concept(impl_iterator<T>& impl) : base(impl) {}
     
        explicit operator Head<T>()
        { return Head<T>(this->m_impl); }
     
      protected:
        ~conv_concept(){}; //cf MC++D
    };
     
    //Define pour générer une classe d'itérateur
    #define ITERATOR(name) template<class T> \
    struct name : \
      conv_concept<T, \
        conv_traits<name>::ImplicitTTL, \
        conv_traits<name>::ExplicitTTL> \
    { \
      private: \
        typedef conv_concept<T, \
          conv_traits<name>::ImplicitTTL, \
          conv_traits<name>::ExplicitTTL> base; \
    \
      public: \
        name(const name&) =default; \
        name& operator=(const name&) =default; \
        name(impl_iterator<T>& impl) : base(impl) {} \
    }; \
     
    /*Fin de la partie TMP*/
     
    //Déclarations anticipées des iétrateurs
    //Je les fais pas toute
    template<class> class normal_iterator;
    template<class> class const_normal_iterator;
    template<class> class lign_iterator;
    template<class> class const_lign_iterator;
     
    //spécialisations de la classe de trait pour les conversions
    //
    template<>
    struct conv_traits<normal_iterator>
    {
      typedef TTL<const_normal_iterator,NullTT> ImplicitTTL;
      typedef TTL<lign_iterator,const_lign_iterator,NullTT> ExplicitTTL;
    };
     
    template<>
    struct conv_traits<const_normal_iterator>
    {
      typedef TTL<NullTT> ImplicitTTL;
      typedef TTL<const_lign_iterator,NullTT> ExplicitTTL;
    };
     
    template<>
    struct conv_traits<lign_iterator>
    {
      typedef TTL<normal_iterator,const_normal_iterator,NullTT> ImplicitTTL;
      typedef TTL<NullTT> ExplicitTTL;
    };
     
    template<>
    struct conv_traits<const_lign_iterator>
    {
      typedef TTL<const_normal_iterator,NullTT> ImplicitTTL;
      typedef TTL<NullTT> ExplicitTTL;
    };
     
    ITERATOR(normal_iterator)
    ITERATOR(const_normal_iterator)
    ITERATOR(lign_iterator)
    ITERATOR(const_lign_iterator)
    Alors j'ai récodé une classe listes de types template, en adaptant un peu tu dois pouvoir écrire moins de code en utilisant les conteneurs de types de TMP (je ne sais pas le comportement de ces conteneurs via les types template, c'est pour ca qu'il faudra peut-etre adpaté)

    J'ai posé une NullTT et un NullTTL pour 2 raisons, la première c'est que c'est adapté de MC++D et la seconde c'est que je trouve les conditions d'arrets des parcours plus clairs comme ceci.

    L'idée est d'avoir des classes de traits qui pour un type template d'itérateur donne 2 listes de types template d'itérateurs, une pour les conversions explicite, l'autre pour les implicite.

    Ensuite on sépare l'interface des itérateurs des données (qui seront les même, l'interfaces offrant les restrictions), et entre les données et l'interface on intercalle des classes qui offrent les opérateurs par des héritages publiques successif.

    Il s'agit donc d'un algo de parcours des deux listes de types template (on pourrait faire un parcours simultané pour réduire la profondeur du parcours) ces classes n'étant là que pour ajouter des fonctions à la classe (comme une classe de politique en quelque sorte), le destructeur est protegé (je te laisse relire MC++D pour les détails).

    Le dernière étape et l'utilisation d'une macro pour rendre la déclaration d'un itérateur simple (le code est toujours le même).

    Ensuite dans l'exemple, il faut faire une déclaration anticpée de tout nos itérateurs puis spécialiser les classes de traits pour eux, et enfin définir les classes avec la macro.

    On pourrait aller plus loin dans la généricité en remarquant des analogie entre const non-const et lign et column, je n'ai pas réflechie au problème, mais en l'état ca sera plutôt à base de macro que de template (car colum,lign,const interviennent dans le nom même de la classe).

    Au début j'avais choisie un héritage protegé pour la classe d'implémentation (ca permet d'avoir une conversion implicite vers impl_iterator dans les classes fille mais pas dehors, cf GotW LSP interne). Mais l'héritage pose problème pour les conversions explicites, il refuse de la faire même si l'opérateur est déclaré lorsque le type de destination est un type de base inaccessible (ca vient du fonctionnement de static_cast).

    On voit dans les classes template d'itérateur un constructeur en public prenant un paramètre un impl_iterator, malgrès les apparence ce n'est pas vraiment un problème, un utilisateur externe n'a pas accés à impl_iterator (on pourrait mettre toute une partie du code qui ne sert qu'en interne dans un namespace anonyme).

  12. #12
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Merci beaucoup. En fait, je n'avais pas penser à utiliser les opérateurs de conversions : je n'utilisait que les constructeurs.
    Les opérateurs de conversion ne sont-ils pas dangereux ?
    explicit sur les opérateurs de conversion c'est du c++0x ?

    Je vois maintenant mieux comment le faire.

  13. #13
    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
    J'ai utilisé les opérateurs de cast car il me semble plus simple de rajouter des opérateurs de cast que de réduire (*) les constructeurs, surtout quand il faut distinguer explicit/implicit.

    Pour la dagerosité des opérateurs de cast, je ne crois pas que ce soit plus dangereux qu'ils soit fait par des opérateurs ou des constructeurs, mais je laisse le soin au plus expérimenté que moi de confirmer/infirmer.

    Pour le caractère C++1x, je ne sais pas. Je sais juste qu'avec gcc 4.5 ca marche.

    (*) L'idée typique de réduire les constrcteurs c'est de faire :
    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
     
    struct A
    {
      A(const A&) { /*cf après (**) */ }
      template<class T>
      A(const T&)
      { /*code ne compilant que si T est convertible en A*/ }
    };
     
    //Ce qui avec une classe template peut devenir
    template<class T>
    struct A
    {
      A(const A&) { /*cf après (**) */ }
      template<class U>
      A(const A<U>&)
      { /*code ne compilant que si A<U> est convertible en A<T>*/ }
    };
    Le mécanisme des templates fait que la validité du code ne sera vérifier qu'au moment de l'instancation du constructeur que tu veux utiliser, et donc si la convertion est impossible il y aura une erreur, sinon non.

    Mais je ne crois pas que ca permette de distinguer explicit/implicit.

    (**) Le constrcuteur de copie a besoin d'être explictement écrit si celui par défaut ne convient pas, même si le code est le même que celui de la version template. La raison est qu'un constructeur template ne peut pas être un constructeur de copie, et donc si tu ne l'explicites pas le compilateur génère celui par défaut, ce qui est génant si celui par défaut ne convient pas. (cf EffC++ par exemple). Ce mécanisme peut-être utilisé dans les classes de pointeurs (ou conteneurs) inteligent par exemple.

  14. #14
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Oui les opérateurs déclaré explicit c'est du C++1x
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  15. #15
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    Pour avoir eu a regler le meme probleme dans nt2, la solution est la suivante : n'avoir qu'un type nested d'iterator sur ton contneur, celui qui aprcourt dans l'ordre optimal de stockage. Ensuite tu fournit des classes d'adaptation de cette iterateur vers les autres et une petite fonction pour passer de l'un a l'autre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    matrix<float> x;
    matrix<float>::iterator cur = x.begin();
    matrix<float>::iterator end = x.end();
    std::transform(cur,end, foo); // applique foo sur tt les elements dans l'ordre du stockage
     
    std::transform( row_major_iterator(cur), row_major_iterator(end), foo ); // force l'ordre ligne par ligne
     
    std::transform( peano_iterator(cur), peano_iterator(end), foo ); // force l'ordre de parcours a etre le parcours de Peano
    Ensuite, je te conseille de t'arranger pour manipuler des Range plutot que des Iterator dans tes algortihmes.

Discussions similaires

  1. problème de conversion de dimension dans BUSINESS OBJECT
    Par greatmaster1971 dans le forum Deski
    Réponses: 4
    Dernier message: 28/04/2014, 13h15
  2. - [CAST ou CONVERT] Problème de conversion de date
    Par Boublou dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 06/07/2004, 14h31
  3. Problème de conversion 3DS->.X
    Par JBernn dans le forum DirectX
    Réponses: 5
    Dernier message: 08/04/2004, 19h08
  4. Problème de conversion unicode
    Par djmalo dans le forum C
    Réponses: 5
    Dernier message: 09/03/2004, 11h48
  5. Réponses: 11
    Dernier message: 02/09/2003, 14h20

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