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 :

shared_ptr, deleter et templates


Sujet :

Langage C++

  1. #1
    Membre éclairé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    836
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 836
    Par défaut shared_ptr, deleter et templates
    Mon problème est relativement simple.
    Je suis en train d'écrire une bibliothèque générique, et celle-ci utilise les smart_ptr du tr1 (ou de boost, puisque ce sont les mêmes)

    Seulement, à l'initialisation (constructeur donc) de la classe, je souhaite initialiser le constructeur de shared_ptr en lui refilant une méthode (virtuelle) de destruction, au cas ou le shared_ptr servirait à gérer une structure, du genre SDL_Surface, qui ne s'initialise ni ne se détruit pas de façon simple, mais en utilisant des fonctions qui "remplacent" malloc et free.

    J'ai donc un constructeur de classe template <_Tp> qui initialise un shared_ptr<_Tp> en lui passant l'adresse d'un deleter virtuel pur nommé classe<_Tp>::Free.
    Evidement, ça merde à la compilation, et je suis presque sûr qu'il s'agit d'une erreur de logique très bête.
    En même temps, j'ai un sacré mélange: template, fonction virtuelle pure, shared_ptr, deleter... et je dois admettre que je ne suis pas habitué à l'utilisation des shared_ptr :/

    Bref, voici le bout de code incriminé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template <typename _Tp>
    __bitmap<_Tp>::__bitmap()
    :m_Surface((_Tp*)(0),&__bitmap<_Tp>::Free)
    {
    }
    Le bout de code qui instancie le code précédent:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    __sdl_bitmap::__sdl_bitmap():__bitmap<SDL_Surface>()
    {
    }
    Et les erreurs affiliées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    /usr/include/c++/4.6/tr1/shared_ptr.h||In constructor ‘std::tr1::__shared_count<_Lp>::__shared_count(_Ptr, _Deleter) [with _Ptr = SDL_Surface*, _Deleter = void (__bitmap<SDL_Surface>::*)(SDL_Surface*), __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:|
    /usr/include/c++/4.6/tr1/shared_ptr.h:569|44|instantiated from ‘std::tr1::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Tp1*, _Deleter) [with _Tp1 = SDL_Surface, _Deleter = void (__bitmap<SDL_Surface>::*)(SDL_Surface*), _Tp = SDL_Surface, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’|
    /usr/include/c++/4.6/tr1/shared_ptr.h:1006|30|instantiated from ‘std::tr1::shared_ptr<_Tp>::shared_ptr(_Tp1*, _Deleter) [with _Tp1 = SDL_Surface, _Deleter = void (__bitmap<SDL_Surface>::*)(SDL_Surface*), _Tp = SDL_Surface]’|
    ../pfen/bitmap_base.h:76|42|instantiated from ‘__bitmap<_Tp>::__bitmap() [with _Tp = SDL_Surface]’|
    /home/berenger/devel/C_Cpp/pfen/sdlimplementation/sdlbitmap.cpp:4|24|instantiated from here|
    /usr/include/c++/4.6/tr1/shared_ptr.h|328|error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘__d (...)’, e.g. ‘(... ->* __d) (...)’|
    /usr/include/c++/4.6/tr1/shared_ptr.h||In member function ‘void std::tr1::_Sp_counted_base_impl<_Ptr, _Deleter, _Lp>::_M_dispose() [with _Ptr = SDL_Surface*, _Deleter = void (__bitmap<SDL_Surface>::*)(SDL_Surface*), __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:|
    /home/berenger/devel/C_Cpp/pfen/sdlimplementation/sdlbitmap.cpp:78|1|instantiated from here|
    /usr/include/c++/4.6/tr1/shared_ptr.h|264|error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘((std::tr1::_Sp_counted_base_impl<SDL_Surface*, void (__bitmap<SDL_Surface>::*)(SDL_Surface*), (__gnu_cxx::_Lock_policy)2u>*)this)->std::tr1::_Sp_counted_base_impl<SDL_Surface*, void (__bitmap<SDL_Surface>::*)(SDL_Surface*), (__gnu_cxx::_Lock_policy)2u>::_M_del (...)’, e.g. ‘(... ->* ((std::tr1::_Sp_counted_base_impl<SDL_Surface*, void (__bitmap<SDL_Surface>::*)(SDL_Surface*), (__gnu_cxx::_Lock_policy)2u>*)this)->std::tr1::_Sp_counted_base_impl<SDL_Surface*, void (__bitmap<SDL_Surface>::*)(SDL_Surface*), (__gnu_cxx::_Lock_policy)2u>::_M_del) (...)’|
    Petites précisions:
    La bibliothèque que je développe est censée permettre de gérer des graphismes (ceux de l'interface utilisateur, en fait. Elle doit gérer le système de fenêtrage) de manière générique et donc, portable, histoire de me permettre d'avoir un truc qui me permette si nécessaire de changer de bibliothèque en deux temps trois mouvement pour faire évoluer la future application et de ne pas être obligé de rentrer quantité de #ifdef win32 #elsif ... .
    (je vais la commencer avec la SDL, le temps d'avoir un truc fonctionnel, et quand le besoin s'en fera sentir, je pourrais faire évoluer vers des bibliothèques plus poussées en recodant juste quelques fonctions, appartenant à une bibliothèque d'interfaçage)
    Vu que la mémoire est quelque chose dont l'utilisation peut enfler très vite dans un jeu (notamment à cause des textures) je préfère avoir une base de classes des plus robustes, et qui, même si elles consomment un peu plus que des pointeurs normaux, me permettent de limiter grandement les problèmes de fuites de mémoire.

    J'ai donc, comme on peut le voir dans les messages d'erreur, 1 bibliothèque principale, contenant les mécanismes, mais aucune fonction gérant réellement l'affichage ou la gestion des images, et une bibliothèque d'interfaçage, qui elle, utilise la SDL pour gérer la problématique d'affichage a l'écran, et le chargement/utilisation des textures.
    Je teste le tout avec une appli contenant les tests unitaires.
    Dans le genre usine a gaz, c'est plutôt pas mal, donc, mais une fois que la base sera implémentée correctement, je n'aurai plus trop de risques de ce côté la, ça me fera un souci de moins à penser quand j'attaquerai le corps de l'appli.

    Je pense avoir donné toutes les informations nécessaires? (Sinon, je complèterai si on me demande d'autres détails, naturellement)

    PS: le nommage des méthodes et membre n'est pas encore uniforme, je sais, mais je ne sais pas encore exactement comment il sera au final.
    Vu que je compte publier cette bibliothèque en LGPL, je pense que je vais m'inspirer des conventions de nommages utilisées par gcc, mais je ne me suis pas encore fixé.

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Un Deleter d de type D pour un shared_ptr possède les prérequis suivants :
    D shall be CopyConstructible. The copy constructor and destructor of D shall not throw exceptions. The expression d(p) shall be well formed, shall have
    well defined behavior, and shall not throw exceptions.

    Si j'ai bien suivi, pour que ce que tu as fourni en deleteur marche, il faudrait faire p->d(), et non pas d(p). D'où le soucis.

    Le plus simple, faire de ton deleteur une fonction libre ou un foncteur, quitte à ce que dans le corps de cette fonction, tu appelles une fonction virtuelle.
    Il y a moyen d'écrire ça avec un adaptateur de foncteur (mem_func ou bind), mais je ne suis pas certain que tu y gagnes en clarté.

    Et si les perfs sont importantes, il vaut mieux créer ton shared_ptr avec make_shared qu'avec son constructeur.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Membre éclairé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    836
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 836
    Par défaut
    Merci, je regarderai ça se soir.
    Ton explication semble plutôt cohérente avec les erreurs que j'ai, mais j'attends d'avoir vérifié que ça marche avant de mettre le sujet en validé.

    La lisibilité... Bah, de toute façon, je n'ai jamais trouvé que les classes utilisant des templates étaient très lisibles, surtout quand le paramètre template est au coeur du métier de celles-ci... Et en théorie, une fois ce coeur écrit, s'il l'est correctement, je ne devrai plus avoir a y revenir, donc autant tout faire le plus proprement et efficacement possible pendant que je bosse dessus.

    Je n'avais jamais entendu parler de make_shared, il à l'air intéressant.
    En fait, si je comprend bien, ça sert à créer en même temps l'objet et le pointeur, supprimant du coup les contrôles qui permettent de vérifier que l'objet n'est pas déjà instancié?

  4. #4
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Quand tu utilises un pointeur normal, tu as deux catégories d'objets : Le pointeur, et l'objet pointé (alloué dynamiquement)

    Quand tu utilises un shared_pointer, tu as 3 catégories de choses : L'objet pointé (alloué dynamiquement), une structure de contrôle contenant entre autre le comptage de référence (allouée dynamiquement), et un certain nombre de pointeurs.

    Du coup, on se retrouve avec 2 allocations dynamiques là ou avant il y en avait une. make_shared va lors de la création de l'objet allouer un peu plus de place pour stocker la structure de contrôle dans le même bloc mémoire que l'objet pointé. On revient à une seule allocation dynamique (j'imagine qu'avoir les infos au même endroit peut aussi avoir un effet positif sur le cache, mais je n'ai jamais entendu parler de bench sur ce point).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2008
    Messages
    836
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2008
    Messages : 836
    Par défaut
    Résolu.
    La ligne qui remplace (je me suis pas encore embêté a utiliser make_shared, pour le moment, l'objectif, c'est que ça marche... quand un truc marche pas et le fait vite, ça m'intéresse moins qu'un truc qui marche lentement )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template <typename _Tp>
    __bitmap<_Tp>::__bitmap()
    :m_Surface((_Tp*)(0), std::bind1st(std::mem_fun(&__bitmap<_Tp>::Free),this))
    {
    }
    Bon, du coup ça m'a déclenché d'autres erreurs ailleurs dans le code, mais elles ne sont pas relatives à ça et vont être aisées a régler. MERCI

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

Discussions similaires

  1. utiliser un shared_ptr de template
    Par mazertys17 dans le forum C++
    Réponses: 3
    Dernier message: 27/02/2015, 11h55
  2. Réponses: 12
    Dernier message: 25/02/2008, 14h27
  3. Template & delete
    Par delire8 dans le forum MFC
    Réponses: 10
    Dernier message: 30/01/2004, 20h36
  4. template match="node() mais pas text()"
    Par Manu_Just dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 26/03/2003, 10h52
  5. [XSLT] template
    Par demo dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 09/09/2002, 11h31

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