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 :

Passer d'un code avec pointeur nu vers un code avec pointeur "smart"


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juillet 2018
    Messages
    27
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2018
    Messages : 27
    Points : 27
    Points
    27
    Par défaut Passer d'un code avec pointeur nu vers un code avec pointeur "smart"
    Bonjour la communauté,

    Pour utiliser le module réseau de SFML j'ai besoin de bûcher (hélas) les pointeurs.
    Après quelques tâtonnements (et beaucoup d'aide de Laurent du forum SFML), j'arrive au code suivant qui compile ET qui fonctionne !
    C++
    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
    ... // lancement du listener serveur : allo
     
    std::vector <sf::TcpSocket*> V_client;
    bool tousPresents(false);
     
    while(!tousPresents)
        {
            // déclaration d'un socket à affecter au prochain client qui le demande
            sf::TcpSocket *joueur = new sf::TcpSocket();
            if (allo.accept(*joueur) == sf::Socket::Done)
            {
                selecteur.add(*joueur);
                V_client.push_back(joueur);
            }
            if (V_client.size() == nbJoueurs) tousPresents=true;
        }
    sauf que :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector <sf::TcpSocket*> V_client;
    c'est mal, ai-je lu par ici... Il semblerait qu'on ne code plus en C++ avec des pointeurs nus mais avec des pointeurs intelligents (le mieux, à mon sens, serait de coder sans pointeur du tout mais bon).
    Débutant mais discipliné, je tente donc de remplacer le pointeur nu par un pointeur unique_ptr. cela donne ceci (<memory> est inclus) et le compilo est réglé pour accepter c++11:

    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
        std::vector <std::unique_ptr<sf::TcpSocket>> V_joueur;
        bool tousPresents(false);
     
        while(!tousPresents)
            {
                // déclaration d'un socket à affecter au prochain client qui le demande
                std::unique_ptr<sf::TcpSocket> joueur(new sf::TcpSocket); // <= ça le compilo n'aime pas ! :/
     
                if (allo.accept(*joueur) == sf::Socket::Done)
                {
                    selecteur.add(*joueur);
                    V_joueur.push_back(joueur);
                }
                if (V_joueur.size() == nbJoueurs) tousPresents=true;
            }
    Comme indiqué en commentaire, le compilo ouvre "new_allocator.h" et fini sur une erreur :
    error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = sf::TcpSocket; _Dp = std::default_delete<sf::TcpSocket>]'|
    Erreur évidemment incompréhensible à mon niveau.

    Par ailleurs, je me suis aussi laissé dire que push_back et unique_ptr, ce n'était pas compatible...

    Quelques pistes, conseils et indications de la syntaxe correcte serait donc TRES bienvenus.
    Merci de m'avoir lu.

    Rick.

  2. #2
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 128
    Points : 33 055
    Points
    33 055
    Billets dans le blog
    4
    Par défaut
    Salut,

    As-tu bien inclus TcpSocket.h ?
    Sinon vu que le destructeur de NonCopyable est protégé, il faudrait je pense ajouter un ~TcpSocket() = default; ou ~TcpSocket() {} dans la déclaration publique de la classe pour que unique_ptr le trouve et l'utilise (il semble ne pas pouvoir fallback sur le destructeur pourtant publique de Socket).

    Plutôt qu'un new, utiliser std::make_unique.
    Par ailleurs, je me suis aussi laissé dire que push_back et unique_ptr, ce n'était pas compatible...
    Gné ?

    Tu peux tenter de ne pas utiliser de pointeurs du tout, TcpSocket devrait être déplaçable (ou devrait pouvoir l'être).

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,

    On peut assez facilement adapter ton code pour qu'il utilise std::unique_ptr... Cela prendrait une forme proche de
    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
    /* un alias de type pour la facilité */
    using PointerType = std::unique_ptr<sf::TcpSocket>;
    std::vector <PointerType> V_client;
    /* Ce qui revient, si tu ne veux pas de l'alias de type, à
     std::vector<std::unique_ptr<sf::TcpSocket>> V_client;
     */
    bool tousPresents(false);
    while(!tousPresents)
        {
            // (1)
            auto joueur = std::make_unique<sf::TcpSocket>();
            // (2)
            if (allo.accept(*(joueur.get()) ) == sf::Socket::Done)
            {
                //(2)
                selecteur.add(*(joueur.get()));
                // (3)
                V_client.push_back(std::move(joueur));
            }
            if (V_client.size() == nbJoueurs) tousPresents=true;
        }
    Et voilà... Le tour est joué
    (1) - std::make_unique est "le moyen par excellence" pour obtenir un std::unique_ptr (accessible depuis C++14)
    (2) - a) On peut obtenir le "pointeur sous-jacent" (le sf::TcpSocket * ) d'un std::unique_ptr au travers de la fonction membre get.
    (2) - b) Pour obtenir une référence (constante ou non), il faut donc déréférencer ce qui est renvoyé par la fonction get
    (3) - Comme std::unique_ptr n'est pas copiable (et que sf::TcpSocket ne l'est d'ailleurs pas non plus), il faut utiliser la version de push_back qui utilise la move semantic (voir le point (2) de la doc)
    Pour forcer à l'utilisation de cette sémantique particulière, nous devons utiliser la fonction std::move

    Ce qu'il faut retenir quand tu manipuleras V_client:
    1- Tu dois toujours accéder aux éléments qu'il contient sous forme de référence (car la copie est interdite pour std::unique_ptr). Par exemple, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    /* parcourt tous les éléments contenus dans V_client : */
    for(auto /* const */ & it : V_client){
        /* utiliser it ici à notre convenance
    }
    2- Si tu retires un élément de V_client, le pointeur sous jacent (sf::TcpSocket * ) sera automatiquement détruit grâce à l'utilisation de std::unique_ptr
    3- Quand V_client sera détruit, tous les éléments (de type std::unique_ptr) seront automatiquement détruits, ce qui provoquera la destruction correct de leur pointeur sous-jacent

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juillet 2018
    Messages
    27
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2018
    Messages : 27
    Points : 27
    Points
    27
    Par défaut
    Bonjour la communauté,

    Tout d'abord merci pour ces retours rapides !

    As-tu bien inclus TcpSocket.h ?
    En fait, TcpSocket est un objet de la classe SFML Network.hpp. J'ai bien inclus cette classe dans le main présenté plus haut.

    Gné ?
    Utiliser push_back() avec un unique_ptr demande, comme l'explique koala, d'utiliser std::move; donc on arrive à nos fins mais ce n'est pas immédiat...

    Le code proposé par koala fonctionne parfaitement (fallait-il en douter ?) et les explication sont très claires !
    J'ai quelques questions complémentaires sur ce qui suit :

    Citation Envoyé par koala01 Voir le message
    (...)
    Ce qu'il faut retenir quand tu manipuleras V_client:
    1- Tu dois toujours accéder aux éléments qu'il contient sous forme de référence (car la copie est interdite pour std::unique_ptr). Par exemple, sous une forme proche de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    /* parcourt tous les éléments contenus dans V_client : */
    for(auto /* const */ & it : V_client){
        /* utiliser it ici à notre convenance
    }
    (...)
    Je ne suis pas familier avec ce type de boucle for ; est-ce que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    for(unsigned int i(0); i<V_client.size(); i++)
    {
    ... 
    V_client[i]->receive(paquet); // receive(sf::Packet) est une fonction associée à un TcPSocket.
    ...
    }
    ne fonctionne-t-il pas également (il semble que si mais peut être existe-il un piège caché) ?

    Ensuite si je place ce code dans une fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bool Serveur::lancement(int nbJoueurs, unsigned short int port, std::string adrIp)
    dans Serveur.cpp d'une classe Serveur, dois-je définir allo (m_allo), V_client (m_V_client) et selecteur (m_selecteur) comme variables dans Serveur.h afin que leur porté soit étendues à toutes les fonction de la classe Serveur ? (Je ne suis pas sûr que ma question soit claire... Dsl)

    Merci de m'avoir lu.
    Rick.

  5. #5
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 128
    Points : 33 055
    Points
    33 055
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Rick_Cplusplus Voir le message
    Utiliser push_back() avec un unique_ptr demande, comme l'explique koala, d'utiliser std::move; donc on arrive à nos fins mais ce n'est pas immédiat...
    Ça peut l'être si tu construits ton objet en même temps. vector.push_back(std::make_unique<Toto>("tata"));
    Fort heureusement que le compilo ne déplace pas à ton insu!

    Ton code devrait être
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    // déclaration d'un socket à affecter au prochain client qui le demande
            std::unique_ptr<sf::TcpSocket> joueur = std::make_unique<sf::TcpSocket>();
            if (allo.accept(*joueur) == sf::Socket::Done)
            {
                selecteur.add(*joueur); // tu pousses une référence, est-ce normal ?
                V_client.push_back(std::move(joueur));
            }
    Je m'étais concentré dans ton code original sur ton // <= ça le compilo n'aime pas ! :/ mais l'erreur ne devrait pas venir de cette ligne mais du push_back plus bas.

    Citation Envoyé par Rick_Cplusplus Voir le message
    Je ne suis pas familier avec ce type de boucle for ; est-ce que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    for(unsigned int i(0); i<V_client.size(); i++)
    {
    ... 
    V_client[i]->receive(paquet); // receive(sf::Packet) est une fonction associée à un TcPSocket.
    ...
    }
    ne fonctionne-t-il pas également (il semble que si mais peut être existe-il un piège caché) ?
    C'est strictement identique, cette boucle est une range-for-loop, c'est plus direct pour simplement itérer sur l'ensemble d'une collection.
    Pour un vector c'est pas flagrant, sur une liste ou map c'est le bonheur par rapport aux notations utilisant des itérateurs imo.

    Citation Envoyé par Rick_Cplusplus Voir le message
    Ensuite si je place ce code dans une fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bool Serveur::lancement(int nbJoueurs, unsigned short int port, std::string adrIp)
    dans Serveur.cpp d'une classe Serveur, dois-je définir allo (m_allo), V_client (m_V_client) et selecteur (m_selecteur) comme variables dans Serveur.h afin que leur porté soit étendues à toutes les fonction de la classe Serveur ? (Je ne suis pas sûr que ma question soit claire... Dsl)
    Je n'ai pas compris mais oui si tu mets des variables membres elles sont utilisables dans l'ensemble de la classe.. C'est même leur but et intérêt

  6. #6
    Nouveau membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juillet 2018
    Messages
    27
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juillet 2018
    Messages : 27
    Points : 27
    Points
    27
    Par défaut
    Bonjour la communauté,

    J'ai désormais les éléments dont j'avais besoin et j'ai pu implémenter mes classes Client et Serveur de façon satisfaisante pour moi ET pour le compilo.
    Je place donc le sujet en résolu !

    Encore merci pour votre soutien.
    Rick.

  7. #7
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 128
    Points : 33 055
    Points
    33 055
    Billets dans le blog
    4
    Par défaut
    Autant prêcher pour ma paroisse : Client & Serveur.

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

Discussions similaires

  1. Réponses: 10
    Dernier message: 26/06/2011, 10h15
  2. Réponses: 1
    Dernier message: 12/05/2011, 12h48
  3. Generation de code avec swig C++ vers java
    Par doommick31 dans le forum C++
    Réponses: 1
    Dernier message: 22/01/2010, 12h53
  4. Réponses: 2
    Dernier message: 30/12/2009, 20h44

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