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 :

Libérer une ressource déplacée avec std::move.


Sujet :

Langage C++

  1. #1
    Invité
    Invité(e)
    Par défaut Libérer une ressource déplacée avec std::move.
    Salut!
    J'essaie de faire un gestionnaire de ressources, et je voudrais qu'il puisse se gérer dans un contexte multi-thread.

    Je fais donc ceci :

    J'instancie la ressource avec new (ou bien sans le new) ça dépend si j'ai besoin d'une allocation dynamique ou pas.
    Je déplace le contenu de la ressource dans une autre variable qui n'est pas un pointeur.

    Et j'utilise un compteur pour savoir combien de fois la ressource est copiée, ainsi, si elle est utilisée dans plusieurs thread, celle-ci n'est pas détruite tant que le thread n'a pas fini de l'utiliser.
    Je voudrais un truc très similaire à std::shared_pointer mais sans devoir faire forcément un compteur de référence ou bien un pointeur car je pourrais très bien charger des ressource depuis la mémoire, ou bien depuis un fichier, sans devoir retourner un shared_ptr sur la ressource à chaque fois, car ça m'embête au plus au point de devoir le ré-écrire à chaque fois.
    De plus mes ressource ne sont pas copiable, mais plutôt déplaçable, c'est donc un std::unique_ptr qui serait plutôt à utiliser dans ce cas, mais cette classe n'a pas de compteur de références.

    Bref j'ai donc tenté de créer un code de ce genre (avec une capsule RAII autour) :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    int i = 1;
    int mi = std::forward<int>(i);
    std::cout<<&mi<<std::endl;
    delete &mi;
    std::cout<<&mi<<std::endl;

    Malheureusement ce code crash lors de la deletion, et j'ai cette erreur à l'exécution :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    free() invalid pointer.
    Y a t'il moyen de libérer le contenu d'une ressource dont le contenu à été déplacer dans une autre variable ?
    Ou bien faut t'il impérativement utiliser la même variable que celle qui alloue le contenu ?

    Merci.

    PS : je sais qu'il est impossible de libérer du contenu qui n'a pas été allouer avec new mais je chercher actuellement une alternative.
    Dernière modification par Invité ; 24/09/2014 à 21h13.

  2. #2
    Invité
    Invité(e)
    Par défaut
    J'ai fait ça, dans le cas de l'utilisation de pointeurs dans un contexte multi-thread :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    int* i = new int(5);
    int& ri = std::forward<int&>(*i);
    delete &ri;

    En encapsulant ce code dans une classe avec un compteur pour compter le nombre de fois que un objet de cette classe est copié, et si il n'y a plus de copie, je détruis libère la mémoire.

  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
    J'ai pas tout compris à ton message, mais la solution semble clairement être celle que tu ne veux pas utiliser : shared_ptr.

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Je ne comprends pas. Si tu n'utilises pas make_shared() et make_unique() (et encore, je ne suis même pas sûr que make_unique() soit un problème), tu peux complètement séparer le chargement de la ressource de la gestion de son pointeur, simplement en faisant retourner un pointeur nu à ta fonction de chargement. Ou en retournant un unique_ptr<> dont tu utiliserais la méthode release() si tu veux que l'objet appartienne à un shared_ptr à la place.

    De plus, shared_ptr possède, tout comme unique_ptr, un constructeur de déplacement (ce qui évite les déréférencements inutiles pour incrémenter/décrémenter le compteur de référence). Tu peux donc utiliser un shared_ptr quand c'est nécessaire sans pour autant trop souffrir au niveau performances.
    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.

  5. #5
    Invité
    Invité(e)
    Par défaut
    Ok, mais bon j'ai trouvé une solution qui marche :
    Code cpp : 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
     
    template <typename R>
    class SmartPointer {
        public :
        SmartPointer() :
        rm (ResourceManager<R>::get()),
        localisation("0") {
            counter = new unsigned int(0);
        }
        SmartPointer(R* resource) :
        rm (ResourceManager<R>::get()),
        localisation(rm.make_resource(resource)) {
            counter = new unsigned int(0);
            (*counter)++;
        }
        SmartPointer(const SmartPointer& sp) :
        rm (sp.rm),
        localisation(sp.localisation){
            counter = sp.counter;
            (*counter)++;
        }
        template <typename D>
        SmartPointer(const SmartPointer<D>& sp) :
        rm (ResourceManager<R>::get()),
        localisation(rm.make_resource(sp.get())) {
            (*sp.counter)++;
            counter = new unsigned int(0);
            (*counter)++;
        }
        SmartPointer& operator=(const SmartPointer& sp) {
            rm = sp.rm;
            localisation = sp.localisation;
            (*sp.counter)++;
            (*counter)++;
            return *this;
        }
        template <typename D>
        SmartPointer& operator=(const SmartPointer<D>& sp) {
            rm = ResourceManager<R>::get();
            localisation = rm.make_resource(sp.get());
            counter = new unsigned int(0);
            (*counter)++;
            return *this;
        }
        unsigned int getCounter() {
            return *counter;
        }
        R* operator-> () {
            return const_cast<R*>(rm.getResourceByPath(localisation));
        }
        /*R& operator* () {
           return *reinterpret_cast<const R*>(rm.getResourceByPath(localisation));
        }*/
        R* get() const {
            return const_cast<R*>(rm.getResourceByPath(localisation));
        }
        ~SmartPointer() {
            (*counter)--;
            if (*counter == 0) {
                rm.deleteResourceByPath(localisation, ResourceManager<R>::TYPE::INTERNAL);
                delete counter;
            }
        }
        unsigned int* counter;
        private :
        ResourceManager<R>& rm;
        std::string localisation;
     
    };
    template <typename T, typename... A>
    SmartPointer<T> make_smart(A&&... args) {
        SmartPointer<T> sp(new T(std::forward<A>(args)...));
        return sp;
    }

    En fait j'utilise 2 sortes de ressources, des ressources internes et externes, si je veux que le compteur soit passer d'un thread à l'autre, j'ai besoin de passer un std::shared_ptr au thread, car je ne sais pas quand je peux détruire la ressource. (Donc elle sera détruite lorsque plus aucun thread ne l'utilisera)

    Pour les ressources j'utilise une classe spéciale (resourcemanager) en ayant ajouté le chargement des ressources à partir d'un fichier ou bien à partir de la mémoire, ma classe ResourceManager est un singleton, donc, je ne peux pas utiliser un std::shared_ptr dessus vu que ça pose problème dans l'utilisation d'un context multi-thread donc je dois utiliser la lazy initialisation et non pas le double check et faire un truc comme ça :

    Code cpp : 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
     
    /**
    * \file resourceCache.h
    * \class ResourceCache
    * \brief manage a cache which holds resource managers of every type.
    * \author Duroisin.L
    * \version 1.0
    * \date 1/02/2014
    */
    class ODFAEG_CORE_API ResourceCache {
        public :
        /** \fn ResourceCache()
        *   \brief constructor.
        */
        ResourceCache() {
            counter = new unsigned int(0);
        }
        ResourceCache(const ResourceCache& cache) {
            resourceManagers = cache.resourceManagers;
            counter = cache.counter;
            (*counter)++;
        }
        /** \fn addResourceManager (ResourceManager<R, I>& rmi, std::string name)
        *   \brief add a resource manager to the cache.
        *   \param ResourceManager<R, I>& rmi : the resource maanger to add.
        *   \param std::string name : an unique id which is associate to the resource manager.
        */
        inline void addResourceManager (ResourceManagerBase& rmi, std::string name);
        /** \fn resourceManager (std::string name)
        *   \brief get a resource manager of the cache.
        *   \return the resource manager related to the given id,
        *   throw an error if there's no resource manager associated to this id.
        */
        template <typename R, typename I>
        ResourceManager<R, I>& resourceManager(std::string name);
        private :
        std::map<std::string, std::reference_wrapper<ResourceManagerBase>> resourceManagers; /**> holds the resources managers and the ids.*/
    };
    Donc je dois utiliser une référence ici et non pas un pointeur, vu que ResourceManager<R, I> est une référence sur un singleton.

    Je pense que je vais mettre un compteur dans le classe ResourceCache et passer le cache à mes différents threads et ça devrait être bon.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Bon finalement j'ai fais mes propres classes pour gérer la durée de vie des ressources internes et externes :

    http://lolilolightdevblog.wordpress....core-partie-3/

    Le conténaire gère la durée de vie de la ressource (même dans un contexte multi-thread) mais l'utilisateur peut libérer aussi la ressource quand il le souhaite si la ressource doit avoir une durée de vie plus longue que son conténaire, et si l'utilisateur oublie de libérée la ressource, celle-ci est automatiquement détruite à la fin du programme lors de la destruction des singletons.
    Je pense que ceci est moins lourd que de le faire un std::shared_ptr ou std::unique_ptr. (Qui ne permettent pas de modifier la durée de vie d'une ressource)

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Tu n'as donc pas encore compris que justement les shared_ptr augmentent la durée de vie lors du partage, et que les unique_ptr transmette la gestion de la durée de vie au propriétaire.

    C'est précisément leur raison d'être.

    Je n'arrive pas à comprendre comment tu fais pour trouver quasiment tous les outils standard inadaptés à ton cas.
    J'en viens à croire que tu recodes Qt...

    Tu compte avoir un Object, aussi?

    Par ailleurs, sur ton blog, utilise donc les balises html <pre>, pour rendre tes codes lisibles.
    éventuellement, tu pourras utiliser <pre style="scroll:auto;overflow:scroll">
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  8. #8
    Invité
    Invité(e)
    Par défaut
    Ha bon std::unique_ptr fourni la durée de vie au std::shared_ptr ? (Je pensais que std::unique_ptr, détruisais le pointeur à sa destruction, bref, encore un truc nouveau que j'ai appris, cela ne fais pas très longtemps que j'utilise ces deux classes)

    Et avec qt il n'y a pas moyen de faire des signaux et des slots sur des fonctions anonymes si ?


    Sinon je n'ai trouvé aucun balise pour mettre du code, je pense que je vais essayer ton truc.

    Sinon je ne veux pas faire de std::shared_ptr sur le cache et devoir les passer aux thread, cela serait trop lourd je pense.

  9. #9
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Avant le "c'est trop lourd, je pense", essaie.
    Les performances, passé la complexité algorithmique, ça se mesure.

    Quant à la sémantique des pointeurs, lis donc la documentation de boost.smart_pointers.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  10. #10
    Invité
    Invité(e)
    Par défaut
    Ok et je suppose que pour changer la propriété il faut déplacer le contenu d'un std::unique_ptr dans un autre avec std::move. (Tout comme pour les simples valeurs ou bien les références d'ailleurs)

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    void ResourceManager<R, I>::fromFile(std::string path, const I& identifiant) {
    std::unique_ptr<R> ptr = std::make_unique<R>();
    //Chargement de la ressource.
    resources.insert(std::make_pair(identifiant, std::move(ptr));
    }

    PS : sinon est ce que dans ce cas-ci le pointeur est bien détruit ?

    Code cpp : 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
     
    void t1func(std::shared_ptr<int> ptr) {
        ptr = std::make_shared<int>(5);
        std::cout<<*ptr<<std::endl;
    }
    void t2func(std::shared_ptr<int> ptr) {
        ptr = std::make_shared<int>(5);
        std::cout<<*ptr<<std::endl;
    }
    int main (int argv, char* argc[]) {
        odfaeg::shared_ptr<int> ptr;   
        std::thread t1(&t1func, ptr);
        std::thread t2(&t2func, ptr);
        t1.join();
        t2.join();    
        return 0;
    }
    Il ne peut pas détruire le pointeur du main car celui-ci est null.
    Il ne peut pas détruire les pointeurs dans les threads non plus car, si t1 alloue le pointeur, puis t2 l'alloue, que t1 le libére et puis que t2 le libère il va y avoir une erreur de double-free. (Car les std::shared_ptr des threads partagent le même std::shared_ptr c'est à dire celui déclaré dans le main.)
    La destruction doit donc se faire lorsque tout les threads ont fini leur exécution donc.

    Je ne suis pas sûr que std::shared_ptr fasse la destruction.
    Tandis que mon système si.
    Dernière modification par Invité ; 26/09/2014 à 17h18.

  11. #11
    Invité
    Invité(e)
    Par défaut
    Bon, je pense que j'ai trouvé la solution finalement :

    Utiliser un std::shared_ptr sur la std::map du cache.

    (Car c'est le contenaire qui doit être partagé ici, pas les ressources du contenaire qui de plus doivent être des références)

  12. #12
    Invité
    Invité(e)
    Par défaut
    Mwai non ça me fait un vilain crash, std::shared_ptr semble ne pas aimer le std::reference_wrapper, je pense que je vais tout simplement, garder mon code actuel qui fonctionne.

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

Discussions similaires

  1. Libérer une ressource sans la fermer (socket)
    Par Miaow dans le forum Langage
    Réponses: 1
    Dernier message: 10/03/2014, 22h48
  2. Réponses: 8
    Dernier message: 13/01/2012, 11h47
  3. Libérer les ressources d'une fenêtre
    Par MiniCesc dans le forum Windows Presentation Foundation
    Réponses: 3
    Dernier message: 29/09/2010, 11h22
  4. Réponses: 2
    Dernier message: 24/08/2008, 18h58
  5. Réponses: 6
    Dernier message: 02/05/2007, 13h15

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