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 :

subtilité des strings en C++


Sujet :

C++

  1. #1
    Membre confirmé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2004
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

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

    Informations forums :
    Inscription : Janvier 2004
    Messages : 58
    Par défaut subtilité des strings en C++
    Bonjour, je viens du C et je dois avouer qu'a mon grand age, j'ai du mal à bien saisir les subtilités du C++.
    Je dois faire une toute petite modif d'une fonction:
    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
     
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestContent,
                    Variant &responseHeaders, string &responseContent) {
     
            if (pFrom->HasConnectivity()) {
                    //FINEST("This is a keep alive timer....");
                    return true;
            }    
     
            if (!pFrom->GetCustomParameters().HasKeyChain(V_STRING, true, 1, "connectionType")) {
                    FATAL("Bogus connection");
                    pFrom->EnqueueForDelete();
                    return false;
            }    
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    FATAL("Invalid response:\n%s", STR(responseHeaders.ToString()));
                    return false;
            }    
            //2. get the raw options
            string raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false);
            }    
            printf("RAW HEADER: %s \n", STR(raw));
    }
    A la ligne 23 il semblerait qu'on crée un ojet string qui s'apelle raw, et on lui assigne une valeur qui vient d'une focntion.

    C'est parfait, je vois à peu près.

    Ce que je voudrais faire, c'est, en cas d'erreur, forcer la valeur de la string raw. Voici comment je m'y prends mal:
    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
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestContent,
                    Variant &responseHeaders, string &responseContent) {
            string raw; 
            if (pFrom->HasConnectivity()) {
                    //FINEST("This is a keep alive timer....");
                    return true;
            }    
    
            if (!pFrom->GetCustomParameters().HasKeyChain(V_STRING, true, 1, "connectionType")) {
                    FATAL("Bogus connection");
                    pFrom->EnqueueForDelete();
                    return false;
            }    
    
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    FATAL("Invalid response:\n%s", STR(responseHeaders.ToString()));
                    //return false;
                    //1.b if no Public field in the header, force the raw options
                    string raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS, SET_PARAMETER";
            }    
            else {
                    //2. get the raw options
                    string raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false);
            }    
            printf("RAW HEADER: %s \n", STR(raw));
    }
    Je crée mon string raw en ligne 5.
    Puis si la réponse n'est pas bonne, je la force en ligne 21, sinon, je fais comme avant en ligne 25.

    Ce qui ne marche pas, c'est que dans le premier cas, mon printf ligne 26 me renvoie bien la valeur reçue, mais dans le second ligne 28, il est toujours vide.

    Ce qui m'interpelle:
    C'est qu'en ligne 21 et 25, si je ne mets pas string devant raw, j'ai une erreur de compilation. Ce qui me laisse supposer que la variable raw n'est définie qu'entre les accolades, et que pour chaque bloc, j'ai un raw différent.
    l'erreur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    note: candidates are:
    /usr/include/c++/4.6/bits/basic_string.h:541:7: note: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string<char>]
    /usr/include/c++/4.6/bits/basic_string.h:549:7: note: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string<char>] <near match>
    Si une Grande âme pouvait m'expliquer comment me dépatouiller de ça, ce serait le premier rayon de soleil de ma pauvre semaine.

  2. #2
    Membre confirmé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2004
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

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

    Informations forums :
    Inscription : Janvier 2004
    Messages : 58
    Par défaut
    C'est bon, j'ai trouvé, il suffisait de comprendre les insultes du compilo...
    En fait il faut bien déclarer l'objet string en tête de fonction, mais lorsque je dois attribuer une valeur qui vient d'une autre fonction, je dois 'caster' le retour afin que le compilo sache quelle string il doit untiliser.

    Voici donc le code corrigé:
    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
     
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestContent,
                    Variant &responseHeaders, string &responseContent) {
            string raw; 
            if (pFrom->HasConnectivity()) {
                    //FINEST("This is a keep alive timer....");
                    return true;
            }    
     
            if (!pFrom->GetCustomParameters().HasKeyChain(V_STRING, true, 1, "connectionType")) {
                    FATAL("Bogus connection");
                    pFrom->EnqueueForDelete();
                    return false;
            }    
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    WARN("Invalid response:\n%s", STR(responseHeaders.ToString()));
                    //1.b if no Public field in the header, force the raw options
                    raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS, SET_PARAMETER";
            }    
            else {
                    //2. get the raw options
                    raw = STR(responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false));
            }    
            printf("RAW HEADER: %s \n", STR(raw));
    }
    avec la macro qui va bien:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define STR(x) (((string)(x)).c_str())
    JE ne comprends pas vraiment pourquoi lorsque je crée l'objet, je n'ia pas besoin de 'caster' mais je comprends que je ne pourrai jamais tout comprendre..

    Si quelqu'un veut ajouter une explication, elle sera la bienvenue

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    En fait, il faudrait voir ce que renvoie la fonction GetValue() de ta variable responseHeaders[RTSP_HEADERS].

    Si c'est une chaine de caractères C style (un const char * "classique", terminé par '\0'), tu n'as pas besoin de caster le résultat renvoyé par la fonction, l'opérateur d'affectation de std::string s'occupera de faire le travail.

    Si c'est "autre chose", il faudra sans doute veiller à faire "mieux" qu'un cast "brutal", mais envisager une conversion "cohérente"

    Par contre, c'est au niveau de printf qu'il peut y avoir un problème, car printf ne sait pas utiliser un objet de type std::string (cette fonction ne peut utiliser que des types primitifs comme argument > 1).

    Le type qui se rapproche le plus std:string que printf peut utiliser, c'est la chaine de caractères C-style.

    Il n'est pas nécessaire de caster la std::string en const char * pour l'utiliser avec printf, mais il suffit d'utiliser la fonction c_str() qui renvoie, justement, une chaine de caractères C-style
    un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    void foo(){
        std::string raw;
        /* ... */
        raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false); /* GetValue renvoie un const char * */
       printf("blabla %s",raw.c_str());
    }
    est donc parfaitement valide et fonctionne sans problème

    Ceci dit, le problème venait essentiellement de la portée des variables.

    Les variables n'existent qu'entre le moment de leur déclaration et dans la portée dans laquelle elles sont déclarées. Et les variable peuvent jouer à cache cache dans les différentes portées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void foo(){
        std::string raw("hello");
        {
            std::string raw("world"); // cache la première variable raw
            printf("%s",raw.c_str()); // affiche "world"
        }
        printf("%s",raw.c_str()); // affiche "hello"
    }
    C'est le problème auquel tu étais confronté avec tes lignes 4, 21 et 25 et que tu constatais en ligne 28 : à la ligne 4 tu créais une première variable nommée raw, aux lignes 21 et 28, tu créais une variable raw qui, dans la portée dans laquelle elle était déclarée, cachait celle qui était déclarée en ligne 4 et, en ligne 28, tu te retrouvais avec la seule variable raw qui existe encore à ce moment là (parce que tu es sorti des deux if), à savoir : celle qui avait été déclarée en ligne 4.

    La correction était donc beaucoup plus simple que ce que tu n'a fait : il "suffit" d'affecter les différentes valeurs à raw, sans déclarer une nouvelle variable (mais en utilisant celle qui est déclarée en ligne 4).

    ... Et, bien sur, d'utiliser la fonction c_str() de raw afin de pouvoir transmettre un const char * à printf en ligne 28 :
    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
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestContent,
                    Variant &responseHeaders, string &responseContent) {
            string raw; 
            if (pFrom->HasConnectivity()) {
                    //FINEST("This is a keep alive timer....");
                    return true;
            }    
     
            if (!pFrom->GetCustomParameters().HasKeyChain(V_STRING, true, 1, "connectionType")) {
                    FATAL("Bogus connection");
                    pFrom->EnqueueForDelete();
                    return false;
            }    
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    FATAL("Invalid response:\n%s", STR(responseHeaders.ToString()));
                    //return false;
                    //1.b if no Public field in the header, force the raw options
                    /* pas string raw, mais juste raw */
                    raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS, SET_PARAMETER";
            }    
            else {
                    //2. get the raw options
                    /* pas string raw, mais juste raw */
                    raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false);
            }    
            /* utiliser c_str() pour avoir un const char * */
            printf("RAW HEADER: %s \n", raw.c_str());
    }
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    raw = STR(responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false));
    Si ce cast fonctionne réellement c'est probablement grâce à un énorme coup de bol.

    Une std::string est souvent implémenté sous la forme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct std::string
    {
       char* m_str;
       int   m_size;
       int m_capacity;
       // ...
    }
     
    std::string::c_str
    {
       return m_str;
    }

    Donc je suppose que le machin renvoyé par GetValue() est une sorte de structure qui commence elle aussi par un char* donc par chance le cast fonctionne.

    C'est dommage que tu nous ais montré que la fin de l'erreur, normalement on devrait avoir une erreur du style :

    Impossible d'utiliser std::string::operator= avec le type "qqchose"

    car les seul candidats pour l'operator= disponibles sont

    operator=(const std::string&)
    operator=(char*)
    On aurait pu voir le tyep renvoyé par GetValue...

  5. #5
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Par défaut
    Citation Envoyé par Arzar Voir le message
    On aurait pu voir le tyep renvoyé par GetValue...
    Au hasard, une variante de char* ?

  6. #6
    Membre confirmé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2004
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

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

    Informations forums :
    Inscription : Janvier 2004
    Messages : 58
    Par défaut
    Wahou,

    Merci a tous pour vos réponses, je vois le C++ sous un jour nouveau.

    En ce qui concerne la portée des variables, j'étais bien conscient en le faisant que ça n'avait rien de logique, mais dans le désespoir, on tente tout

    Tout dépend donc de ce que renvoie GetValue()
    Voici donc la fonction GetValue:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
     Variant &Variant::GetValue(string key, bool caseSensitive) {
      if (caseSensitive) {
      return (*this)[key];
      } else {
     
      FOR_MAP(*this, string, Variant, i) {
      if (lowerCase(MAP_KEY(i)) == lowerCase(key))
      return MAP_VAL(i);
      }
     
      return (*this)[key];
      }
     }
    elle renvoie donc un variant, je suppose que ça n'aide pas, En cherchant un peu, je vois une classe variant qui peut être surchargée.

    Si on lui passe un string, voici ce que ça donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     Variant::Variant(const string &val) {
      CONSTRUCTOR;
      _type = V_STRING;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.s");
      _value.s = new string(val);
     }
    Donc d'après ce que je comprends, GetValue() renvoie un string.

    Ce qui veut dire que vous avez raison:
    Il n'est pas nécessaire de caster la std::string en const char * pour l'utiliser avec printf, mais il suffit d'utiliser la fonction c_str() qui renvoie, justement, une chaine de caractères C-style
    un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void foo(){
        std::string raw;
        /* ... */
        raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false); /* GetValue renvoie un const char * */
       printf("blabla %s",raw.c_str());
    }
    est donc parfaitement valide et fonctionne sans problème
    je ne devrais pas avoir à 'caster'.
    Ceci dit, comme vous pouvez le constater, j'ai mis caster entre guillemets, car je ne fais pas un cast, mais j'utilise une macro:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define STR(x) (((string)(x)).c_str())
    qui utilise bien la fonction c_str(), donc pour le printf, tout va bien.

    Ce que je ne comprends pas, c'est que si je n'utilise pas cette macro pour récupérer le retour de GetValue(), le compilo me renvoie une erreur.
    si je compile ça:
    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
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestCon
                    Variant &responseHeaders, string &responseContent) {
            string raw;
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    WARN("Invalid response:\n%s", STR(responseHeaders.ToString()));
            //      return false;
                    //1.b if no Public field in the header, force the raw options
                    raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS,  SET_PARAMETER";
            }
            else {
                    //2. get the raw options
                    raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC
                            false);
            }
            printf("RAW STRING: %s", STR(raw));
    }
    voici l'insulte du compilo:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /src/protocols/rtp/basertspappprotocolhandler.cpp: In member function âvirtual bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(RTSPProtocol*, Variant&, std::string&, Variant&, std::string&)â:
    /src/protocols/rtp/basertspappprotocolhandler.cpp:985:9: error: ambiguous overload for âoperator=â in âraw = (&(& responseHeaders)->Variant::operator[](((const char*)"headers")))->Variant::GetValue(std::basic_string<char>(((const char*)"Public"), (*(const std::allocator<char>*)(& std::allocator<char>()))), 0)â
    /src/protocols/rtp/basertspappprotocolhandler.cpp:985:9: note: candidates are:
    /usr/include/c++/4.6/bits/basic_string.h:541:7: note: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string<char>]
    /usr/include/c++/4.6/bits/basic_string.h:549:7: note: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string<char>] <near match>
    /usr/include/c++/4.6/bits/basic_string.h:549:7: note:   no known conversion for argument 1 from âVariantâ to âconst char*â
    /usr/include/c++/4.6/bits/basic_string.h:560:7: note: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string<char>]
    make[2]: *** [/src/protocols/rtp/basertspappprotocolhandler.cpp.o] Error 1
    make[1]: *** [thelib/CMakeFiles/thelib.dir/all] Error 2
    make: *** [all] Error 2
    La ligne 985 de mon code étant celle de la ligne 16 citée ici.

    Donc l'erreur du compilo me dit bien que l'erreur vient du retour de GetValue() qui renvoie un variant, or le compilo ne sait comment ranger ça dans une string.

    J'ai un peu de mal avec ces histoires de surcharges de fonctions, et je dois avouer que je ne comprends toujours pas pourquoi je dois utiliser c_str() pour renvoyer ma valeur dans le raw si je le déclare en début de fonction alors que la déclaration à la volée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    string raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false);
    passe très bien sans utiliser la focntion c_str() de ma macro.


    Si vous voulez plus d'infos sur ce qu'est un variant, voici l'ensemble des 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
    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
    155
    156
    157
    158
    159
    160
    5 Variant::Variant() {
      CONSTRUCTOR;
      _type = V_NULL;
      memset(&_value, 0, sizeof (_value));
     }
     
     Variant::Variant(const Variant &val) {
      CONSTRUCTOR;
      InternalCopy(val);
     }
     
     Variant::Variant(const bool &val) {
      CONSTRUCTOR;
      _type = V_BOOL;
      memset(&_value, 0, sizeof (_value));
      _value.b = val;
     }
     
     Variant::Variant(const int8_t &val) {
      CONSTRUCTOR;
      _type = V_INT8;
      memset(&_value, 0, sizeof (_value));
      _value.i8 = val;
     }
     
     Variant::Variant(const int16_t &val) {
      CONSTRUCTOR;
      _type = V_INT16;
      memset(&_value, 0, sizeof (_value));
      _value.i16 = val;
     }
     
     Variant::Variant(const int32_t &val) {
      CONSTRUCTOR;
      _type = V_INT32;
      memset(&_value, 0, sizeof (_value));
      _value.i32 = val;
     }
     
     Variant::Variant(const int64_t &val) {
      CONSTRUCTOR;
      _type = V_INT64;
      memset(&_value, 0, sizeof (_value));
      _value.i64 = val;
     }
     
     Variant::Variant(const uint8_t &val) {
      CONSTRUCTOR;
      _type = V_UINT8;
      memset(&_value, 0, sizeof (_value));
      _value.ui8 = val;
     }
     
     Variant::Variant(const uint16_t &val) {
      CONSTRUCTOR;
      _type = V_UINT16;
      memset(&_value, 0, sizeof (_value));
      _value.ui16 = val;
     }
     
     Variant::Variant(const uint32_t &val) {
      CONSTRUCTOR;
      _type = V_UINT32;
      memset(&_value, 0, sizeof (_value));
      _value.ui32 = val;
     }
     
     Variant::Variant(const uint64_t &val) {
      CONSTRUCTOR;
      _type = V_UINT64;
      memset(&_value, 0, sizeof (_value));
      _value.ui64 = val;
     }
     
     Variant::Variant(const double &val) {
      CONSTRUCTOR;
      _type = V_DOUBLE;
      memset(&_value, 0, sizeof (_value));
      _value.d = val;
     }
     
     Variant::Variant(const Timestamp &val) {
      CONSTRUCTOR;
      _type = V_TIMESTAMP;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.t");
      _value.t = new Timestamp;
      *_value.t = val;
      NormalizeTs();
     }
     
     Variant::Variant(const uint16_t year, const uint8_t month, const uint8_t day) {
      CONSTRUCTOR;
      _type = V_DATE;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.t");
      _value.t = new Timestamp;
      memset(_value.t, 0, sizeof (Timestamp));
      _value.t->tm_year = year - 1900;
      _value.t->tm_mon = month - 1;
      _value.t->tm_mday = day;
      _value.t->tm_hour = 0;
      _value.t->tm_min = 0;
      _value.t->tm_sec = 0;
      NormalizeTs();
     }
     
     Variant::Variant(const uint8_t hour, const uint8_t min, const uint8_t sec, const uint16_t m) {
      CONSTRUCTOR;
      _type = V_TIME;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.t");
      _value.t = new Timestamp;
      memset(_value.t, 0, sizeof (Timestamp));
      _value.t->tm_year = 70;
      _value.t->tm_mon = 0;
      _value.t->tm_mday = 1;
      _value.t->tm_hour = hour;
      _value.t->tm_min = min;
      _value.t->tm_sec = sec;
      NormalizeTs();
     }
     
     Variant::Variant(const uint16_t year, const uint8_t month, const uint8_t day,
      const uint8_t hour, const uint8_t min, const uint8_t sec, const uint16_t m) {
      CONSTRUCTOR;
      _type = V_TIMESTAMP;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.t");
      _value.t = new Timestamp;
      memset(_value.t, 0, sizeof (Timestamp));
      _value.t->tm_year = year - 1900;
      _value.t->tm_mon = month - 1;
      _value.t->tm_mday = day;
      _value.t->tm_hour = hour;
      _value.t->tm_min = min;
      _value.t->tm_sec = sec;
      NormalizeTs();
     }
     
     Variant::Variant(const char *pVal) {
      CONSTRUCTOR;
      _type = V_STRING;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.s");
      _value.s = new string(pVal);
     }
     
     Variant::Variant(const string &val) {
      CONSTRUCTOR;
      _type = V_STRING;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.s");
      _value.s = new string(val);
     }
     
     Variant::~Variant() {
      DESTRUCTOR;
      Reset();
     }
    Dans tous les cas, un tout grand merci à vous pour vos précieuses informations

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gigiskhan Voir le message
    Wahou,

    Merci a tous pour vos réponses, je vois le C++ sous un jour nouveau.

    En ce qui concerne la portée des variables, j'étais bien conscient en le faisant que ça n'avait rien de logique, mais dans le désespoir, on tente tout

    Tout dépend donc de ce que renvoie GetValue()
    Voici donc la fonction GetValue:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
     Variant &Variant::GetValue(string key, bool caseSensitive) {
      if (caseSensitive) {
      return (*this)[key];
      } else {
     
      FOR_MAP(*this, string, Variant, i) {
      if (lowerCase(MAP_KEY(i)) == lowerCase(key))
      return MAP_VAL(i);
      }
     
      return (*this)[key];
      }
     }
    elle renvoie donc un variant, je suppose que ça n'aide pas, En cherchant un peu, je vois une classe variant qui peut être surchargée.

    Si on lui passe un string, voici ce que ça donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     Variant::Variant(const string &val) {
      CONSTRUCTOR;
      _type = V_STRING;
      memset(&_value, 0, sizeof (_value));
      DYNAMIC_ALLOC("_value.s");
      _value.s = new string(val);
     }
    Donc d'après ce que je comprends, GetValue() renvoie un string.
    Ah, ben non!

    GetValue renvoie un objet de type ... Variant.

    La notion la plus proche de Variant que tu aies en C est le void *, avec un le type ereasure et l'approche orientée objet en moins : Cela peut représenter strictement tout et n'importe quoi.

    alors, bien sur, cela peut entre autres représenter une chaîne de caractères, mais cela peut tout aussi bien représenter un int qu'un double ou même la lune

    Par contre, tu devrais voir du coté de la classe Variant en question... Elle devrait te fournir une fonction (toString(), peut être) qui te permettra de récupérer l'information en étant sûr qu'elle est sous la forme d'une chaine de caractères
    Ceci dit, comme vous pouvez le constater, j'ai mis caster entre guillemets, car je ne fais pas un cast, mais j'utilise une macro:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define STR(x) (((string)(x)).c_str())
    qui utilise bien la fonction c_str(), donc pour le printf, tout va bien.
    Heuu... Excuses moi, mais moi, ce que je lis de la macro, c'est surtout (string)(x)!!! Et ca, c'est un transtypage C-style, même s'il est caché par la macro

    Ce que je ne comprends pas, c'est que si je n'utilise pas cette macro pour récupérer le retour de GetValue(), le compilo me renvoie une erreur.
    si je compile ça:
    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
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestCon
                    Variant &responseHeaders, string &responseContent) {
            string raw;
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    WARN("Invalid response:\n%s", STR(responseHeaders.ToString()));
            //      return false;
                    //1.b if no Public field in the header, force the raw options
                    raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS,  SET_PARAMETER";
            }
            else {
                    //2. get the raw options
                    raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC
                            false);
            }
            printf("RAW STRING: %s", STR(raw));
    }
    L'erreur sans la macro vient, justement, du fait que GetValue renvoie un objet de type Variant, qui n'a strictement rien à voir avec une string ni avec n'importe quel autre type que... Variant lui-même.

    Le fait que cela passe avec la macro vient, quant à lui, du fameux (string)(x) qui occasionne un cast c_style en string.

    Le problème, c'est que ta macro te fait mentir au compilateur en lui disant (je schématise ) "Tu peux me croire sur parole, ce que je te transmet est bel et bien une chaine de caractères".

    Sauf que... Si ca peut effectivement être une chaine de caractères, cela peut tout aussi bien être tout et n'importe quoi d'autre

    Ta macro arme donc un six coup avec 5 balles et fait tourner le barillet! :

    Si la valeur représentée par le Variant est bien une chaine de caractères, ca passera. Mais si la valeur est un entier (ou un float ou n'importe quel type primitif), tu risques déjà d'avoir quelques surprises.

    Et si la valeur représentée par le Variant est une classe (surtout si elle a sémantique d'entité)... Ah!!! bobo!!!
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("blabla %s",raw.c_str());
    Quitte à faire du C++, pourquoi ne pas directement faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::cout << "blable " << raw << std::endl;
    std::endl : indique la fin de la ligne (presque équivalent à '\n'.
    std::cout : cout pour c out = sortie standard. On a aussi std::cerr et std::cin pour sortie d'erreur et entrée standard.

  9. #9
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    @koala01: À ma connaissance, le cast n'est pas si "grave" ici parce qu'on ne peut pas faire un reinterpret_cast avec des objets non-pointeurs/références.

    Donc, on finit forcément par appeler un constructeur de string ou opérateur de conversion en string.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Membre confirmé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2004
    Messages
    58
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

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

    Informations forums :
    Inscription : Janvier 2004
    Messages : 58
    Par défaut
    Merci Koala01,
    Il existe effectivement une méthode ToString() qui fonctionne parfaitement.

    Voici donc le code final:
    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
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestContent,
                    Variant &responseHeaders, string &responseContent) {
            string raw;
            if (pFrom->HasConnectivity()) {
                    //FINEST("This is a keep alive timer....");
                    return true;
            }
     
            if (!pFrom->GetCustomParameters().HasKeyChain(V_STRING, true, 1, "connectionType")) {
                    FATAL("Bogus connection");
                    pFrom->EnqueueForDelete();
                    return false;
            }
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    WARN("Invalid response:\n%s", STR(responseHeaders.ToString()));
                    //return false;
                    //1.b if no Public field in the header, force the raw options
                    raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS, SET_PARAMETER";
            }
            else
            {
                    //2. get the raw options
                    raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false).ToString();
            }
            printf("RAW STRING: %s", STR(raw));
    }


    Je ne comprends un peu mieux le principe de surcharge de méthode, je pensais que la valeur renvoyée par une méthode surchargée pouvais dépendre des paramètres passés à celle-ci, visiblement, il n'en est rien.

    Par contre ce que je ne comprends toujours pas, c'est que:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     string raw = STR(responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC, false));
    Ca passe au niveau du compilo, mais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void mafonction()
    {
         string raw;
         if(1)
         {
              raw = STR(responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC, false));
         }
    }
    Ca ne passe pas

  11. #11
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    Le truc bien du compilo c'est qu'il donne un message d'erreur quand il veut/peut/sait pas faire un truc
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  12. #12
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gigiskhan Voir le message
    Merci Koala01,
    Il existe effectivement une méthode ToString() qui fonctionne parfaitement.

    Voici donc le code final:
    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
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestContent,
                    Variant &responseHeaders, string &responseContent) {
            string raw;
            if (pFrom->HasConnectivity()) {
                    //FINEST("This is a keep alive timer....");
                    return true;
            }
     
            if (!pFrom->GetCustomParameters().HasKeyChain(V_STRING, true, 1, "connectionType")) {
                    FATAL("Bogus connection");
                    pFrom->EnqueueForDelete();
                    return false;
            }
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    WARN("Invalid response:\n%s", STR(responseHeaders.ToString()));
                    //return false;
                    //1.b if no Public field in the header, force the raw options
                    raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS, SET_PARAMETER";
            }
            else
            {
                    //2. get the raw options
                    raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false).ToString();
            }
            printf("RAW STRING: %s", STR(raw));
    }
    A vrai dire, je n'aime vraiment pas les macros, surcout celle-ci qui me semble malgré tout dangereuse (à cause du cast C-style).

    Je préférerais de loin te voir utiliser le code
    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
    bool BaseRTSPAppProtocolHandler::HandleRTSPResponse200Options(
                    RTSPProtocol *pFrom, Variant &requestHeaders, string &requestContent,
                    Variant &responseHeaders, string &responseContent) {
            string raw;
            if (pFrom->HasConnectivity()) {
                    //FINEST("This is a keep alive timer....");
                    return true;
            }
     
            if (!pFrom->GetCustomParameters().HasKeyChain(V_STRING, true, 1, "connectionType")) {
                    FATAL("Bogus connection");
                    pFrom->EnqueueForDelete();
                    return false;
            }
     
            //1. Sanitize
            if (!responseHeaders[RTSP_HEADERS].HasKey(RTSP_HEADERS_PUBLIC, false)) {
                    WARN("Invalid response:\n%s", responseHeaders.ToString().c_str());
                    //return false;
                    //1.b if no Public field in the header, force the raw options
                    raw = "DESCRIBE, PAUSE, PLAY, SETUP, TEARDOWN, OPTIONS, SET_PARAMETER";
            }
            else
            {
                    //2. get the raw options
                    raw = responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC,
                            false).ToString();
            }
            printf("RAW STRING: %s", raw.c_str());
    }
    Ce n'est pas pour le deux caractères que ta macro te permet d'économiser que cela change quoi que ce soit au niveau de l'écriture, mais, au moins, le code est bien plus explicite car le service rendu par la fonction c_str() est beaucoup plus clairement établit que celui rendu par la macro STR (qui, pour le coup devrait être renommé en C_STRING par exemple, si tu veux qu'il soit plus explicite )
    Je ne comprends un peu mieux le principe de surcharge de méthode, je pensais que la valeur renvoyée par une méthode surchargée pouvais dépendre des paramètres passés à celle-ci, visiblement, il n'en est rien.
    C'est beaucoup plus complexe que cela en réalité:

    A la base, le prototype de la fonction n'a que faire du type de retour de la fonction, sauf si une affectation (ou un test quelconque) est effectuée avec.

    A ce moment là, le compilateur vérifie la signature de la fonction (et donc le type de retour), pour s'assurer que la valeur renvoyée est compatible avec le type de la variable à laquelle il doit l'affecter.

    Tu peux éventuellement observer un type de retour différent si:
    1. deux versions de la même fonction varient par le nombre ou par le type de leurs arguments
    2. tu as une version constante et une version non constante de la même fonction.
    Par contre, la fonction GetValue ne varie pas, c'est toujours la même version qui est utilisée : seule la valeur des paramètres que tu lui transmets est différente (et non le nombre ou le type des paramètres que tu dois lui transmettre).

    Après, il y a aussi moyen de faire varier le type de retour grâce au polymorphisme paramétrique, mais c'est une technique utilisée en programmation générique (des classes et des fonctions template), et c'est plus ou moins hors scope par rapport à ton problème
    Par contre ce que je ne comprends toujours pas, c'est que:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     string raw = STR(responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC, false));
    Ca passe au niveau du compilo, mais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void mafonction()
    {
         string raw;
         if(1)
         {
              raw = STR(responseHeaders[RTSP_HEADERS].GetValue(RTSP_HEADERS_PUBLIC, false));
         }
    }
    Ca ne passe pas
    C'est que le variant renvoyé par GetValue n'est pas implicitement convertible en string. C'est sans doute ce que le compilateur t'indique

    Il faudrait voir quel lest le type réel de la valeur spécifiée par RTSP_HEADERS_PUBLIC et, éventuellement, voir s'il y a moyen (au niveau de Variant) de faire une conversion pour en obtenir une chaine de caractères.

    Autrement, tu devras choisir la fonction membre de Variant "qui va bien" (toInt, toDouble ou autre) en rapport avec le type de la valeur, et le convertir toi-même (soit avec la fonction std::to_string, si tu es en C++11, soit en passant par std::stringstream).
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

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

Discussions similaires

  1. Recupérer des string
    Par frozon dans le forum Langage
    Réponses: 5
    Dernier message: 09/01/2006, 10h28
  2. DLL : récupérer des string/PChar...
    Par the_magik_mushroom dans le forum Langage
    Réponses: 8
    Dernier message: 10/11/2005, 10h58
  3. Mauvais tri des String avec des accents
    Par lbreuillard dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 23/09/2005, 12h21
  4. [débutant]modifier des string
    Par calavera dans le forum SL & STL
    Réponses: 1
    Dernier message: 16/09/2005, 20h45
  5. problème our passer des string dans tableau d'int
    Par Battosaiii dans le forum C++
    Réponses: 9
    Dernier message: 15/07/2004, 17h42

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