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 :

Convertir un std::vector de std::unique_ptr en un std::vector de pointeurs nus.


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité
    Invité(e)
    Par défaut Convertir un std::vector de std::unique_ptr en un std::vector de pointeurs nus.
    Salut!

    Je voudrais n'utiliser les pointeurs intelligent que en interne à ma bibliothèque, le problème c'est que j'ai des std::vector de std::unique_ptr, et je dois alors, les convertir en std::vector de pointeurs nu avant de les renvoyer à l'utilisateur comme ceci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    std::vector<Entity*> getEntities() {       
           std::vector<Entity*> entities;
           for (unsigned int i = 0; i < m_entities.size(); i++) {
                 entities.push_back(m_entities[i].get());
           }
           return entities;
    }

    Ce que je trouve assez lourd, n'y a t'il pas moyen de faire ceci directement ?

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    entities = m_entities;

    Sinon, je pense que je vais coder ma propre classe pour faire cela, d'ailleurs, j'ai déjà commencé à en code une car, au niveau syntaxe ça devient, assez, lourd...

    Pour les pointeurs simple ça va encore mais pour les tableaux de pointeurs ça devient vraiment, overkill. :/

  2. #2
    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
    non

    mais une simple fonction template à réutiliser partout ferait l'affaire plutôt que
    je vais coder ma propre classe pour faire cela
    m'enfin, comme tu aimes bien réinventer des roues ovales, ne t'en prive pas !
    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.

  3. #3
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Au fond ce que tu souhaites (si j'ai bien compris), c'est permettre à l'utilisateur de parcourir (sans pouvoir la modifier) une collection de Entity. Avec ta solution actuelle, tu imposes trop de choses à l'utilisateur : travailler avec std::vector, et sur des Entity*. Si tes entités étaient stockées comme valeurs dans un std::vector<Entity>, la solution serait simple : il suffit de déclarer un paire de fonctions begin() et end() dans ta classe qui retournent simplement le résultat de m_entities.begin() et m_entities.end(), respectivement. De fait, l'utilisateur manipule directement des Entity&, et sans avoir à se soucier du type exact de la collection (std::vector ou autre).

    Mais comme tu as un std::vector<std::unique_ptr<Entity>>, le problème est un peu plus compliqué, car tu ne dois pas exposer à l'utilisateurs les std::unique_ptr. La solution est de passer par des itérateurs personnalisés qui vont "cacher" ce détail. Le code est assez pénible à écrire car il faut grosso modo refaire à la main le code d'un itérateur standard en ne changeant que l'opérateur de dé-référencement. Je te mets ci-dessous le code générique que j'utilise (C++11, source: github). Il a l'avantage de fonctionner avec n'importe quel conteneur, et n'importe quel type de pointeur (pointeur nu, std::unique_ptr, ...).

    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
    161
    162
    163
    164
     
    // Factorisation du code commun dans une classe de base.
    // Que ce soit un iterator, const_iterator, reverse_iterator, ...
    // on a toujours le même comportement, donc inutile de se
    // répéter.
    template<typename ChildIter, typename BaseIter>
    struct ptr_iterator_base_impl {
        BaseIter i;
     
        ptr_iterator_base_impl(const BaseIter& j) : i(j) {}
     
        ChildIter operator ++ (int) {
            ChildIter iter = static_cast<ChildIter&>(*this);
            ++i; return iter;
        }
     
        ChildIter& operator ++ () {
            ++i; return static_cast<ChildIter&>(*this);
        }
     
        ChildIter operator -- (int) {
            ChildIter iter = static_cast<ChildIter&>(*this);
            --i; return iter;
        }
     
        ChildIter& operator -- () {
            --i; return static_cast<ChildIter&>(*this);
        }
     
        template<typename U>
        ChildIter operator + (U d) const {
            return i + d;
        }
     
        auto operator - (const ChildIter& iter) const -> decltype(i - i) {
            return i - iter.i;
        }
     
        template<typename U, typename enable = typename std::enable_if<std::is_arithmetic<U>::value>::type>
        ChildIter operator - (U d) const {
            return i - d;
        }
     
        template<typename U, typename enable = typename std::enable_if<std::is_arithmetic<U>::value>::type>
        ChildIter& operator += (U d) {
            i += d; return static_cast<ChildIter&>(*this);
        }
     
        template<typename U, typename enable = typename std::enable_if<std::is_arithmetic<U>::value>::type>
        ChildIter& operator -= (U d) {
            i -= d; return static_cast<ChildIter&>(*this);
        }
     
        bool operator == (const ChildIter& iter) const {
            return i == iter.i;
        }
     
        bool operator != (const ChildIter& iter) const {
            return i != iter.i;
        }
     
        bool operator < (const ChildIter& iter) const {
            return i < iter.i;
        }
     
        bool operator <= (const ChildIter& iter) const {
            return i <= iter.i;
        }
     
        bool operator > (const ChildIter& iter) const {
            return i > iter.i;
        }
     
        bool operator >= (const ChildIter& iter) const {
            return i >= iter.i;
        }
     
        auto operator * () const -> decltype(**i) {
            return **i;
        }
     
        auto operator -> () const -> decltype(*i) {
            return *i;
        }
     
        BaseIter stdbase() const {
            return i;
        }
    };
     
    template<typename BaseIter>
    class ptr_iterator_base;
     
    // Le const_iterator
    template<typename BaseIter>
    class const_ptr_iterator_base :
        private ptr_iterator_base_impl<const_ptr_iterator_base<BaseIter>, BaseIter> {
     
        using impl = ptr_iterator_base_impl<const_ptr_iterator_base, BaseIter>;
        friend impl;
     
    public :
        const_ptr_iterator_base() = default;
        const_ptr_iterator_base(const BaseIter& j) : impl(j) {}
        const_ptr_iterator_base(const ptr_iterator_base<BaseIter>& iter) : impl(iter.i) {}
        const_ptr_iterator_base(const const_ptr_iterator_base&) = default;
        const_ptr_iterator_base(const_ptr_iterator_base&&) = default;
        const_ptr_iterator_base& operator = (const const_ptr_iterator_base&) = default;
        const_ptr_iterator_base& operator = (const_ptr_iterator_base&&) = default;
     
        using impl::operator*;
        using impl::operator->;
        using impl::operator+;
        using impl::operator-;
        using impl::operator++;
        using impl::operator--;
        using impl::operator+=;
        using impl::operator-=;
        using impl::operator==;
        using impl::operator!=;
        using impl::operator<;
        using impl::operator<=;
        using impl::operator>;
        using impl::operator>=;
        using impl::stdbase;
    };
     
    // L'iterator de base
    template<typename BaseIter>
    class ptr_iterator_base :
        private ptr_iterator_base_impl<ptr_iterator_base<BaseIter>, BaseIter> {
     
        template<typename N>
        friend class const_ptr_iterator_base;
        using impl = ptr_iterator_base_impl<ptr_iterator_base, BaseIter>;
        friend impl;
     
    public :
        ptr_iterator_base() = default;
        ptr_iterator_base(const BaseIter& j) : impl(j) {}
        ptr_iterator_base(const ptr_iterator_base&) = default;
        ptr_iterator_base(ptr_iterator_base&&) = default;
        ptr_iterator_base& operator = (const ptr_iterator_base&) = default;
        ptr_iterator_base& operator = (ptr_iterator_base&&) = default;
     
        using impl::operator*;
        using impl::operator->;
        using impl::operator+;
        using impl::operator-;
        using impl::operator++;
        using impl::operator--;
        using impl::operator+=;
        using impl::operator-=;
        using impl::operator==;
        using impl::operator!=;
        using impl::operator<;
        using impl::operator<=;
        using impl::operator>;
        using impl::operator>=;
        using impl::stdbase;
    };
     
    // NB: tu peux facilement rajouter les reverse_iterator et const_reverse_iterator si besoin
    // Cf. le code sur github.
    Pour rendre les choses plus propres et éviter de polluer ta classe, tu peux utiliser un wrapper qui n'expose que le strict minimum :
    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
    template<typename ValueType>
    struct vector_unique_ptr_wrapper {
    private :
        using base = std::vector<std::unique_ptr<ValueType>>;
        using base_iterator = typename base::iterator;
        using base_const_iterator = typename base::const_iterator;
     
        base& m_vector;
     
    public :
        using iterator = ptr_iterator_base<base_iterator>;
        using const_iterator = const_ptr_iterator_base<base_const_iterator>;
     
        explicit vector_unique_ptr_wrapper(base& vec) : m_vector(vec) {}
     
        iterator begin() {
            return m_vector.begin();
        }
     
        const_iterator begin() const {
            return m_vector.begin();
        }
     
        iterator end() {
            return m_vector.end();
        }
     
        const_iterator end() const {
            return m_vector.end();
        }
     
        // Tu peux, si tu veux, rajouter m_vector.size() aussi
    };
    Avec ce wrapper, le code dans ta classe est simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class MyClass {
        // Le conteneur que tu manipules en interne
        std::vector<std::unique_ptr<Entity>> m_entities;
     
        // Le reste de ton code
     
    public :
        MyClass() : entities(m_entities) { /* ... */ }
     
        // Tu n'exposes que le wrapper à l'utilisateur
        vector_unique_ptr_wrapper<Entity> entities;
     
        // Le reste de ton code
    };
    Et ça s'utilise comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for (/* const */ Entity& e : obj.entities) {
        // traitement sur 'e' ...
    }
    Du point de vue de l'utilisateur, on peut difficilement faire plus simple.

  4. #4
    Membre Expert

    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 : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Sinon il y a boost qui propose déjà ça (boost.iterator et boost.range).

  5. #5
    Invité
    Invité(e)
    Par défaut
    Ok c'est ce que je cherchais mais une question, ça fonctionne aussi si ma classe Entity est polymorphique ? (Au fait c'est pour ça que j'utilise un pointeur, sinon j'aurai un effet de slicing)

    J'avais pensé faire une classe qui hérite de std::vector<std::unique_ptr<T>> et une fonction de conversion mais, je sais pas si c'est la meilleure solution en plus cela ne fonctionnerait qu'avec un seul type de conteneur.

  6. #6
    Membre Expert Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Par défaut
    Pourquoi renvoyer à l'utilisateur un vecteur de pointeur nus ? Même si on est pas toujours d'accord pour utiliser, ou pas, des pointeurs nus à l'intérieur d'une classe ou d'un fonction ; on est généralement assez d'accord pour dire : « pas de pointeurs nus dans l'interface publique ».
    Donc pour moi tu devrais probablement renvoyer une référence sur le vecteur d'unique_ptr. Si tu veux un truc qui se rapproche plus d'un simple vecteur, tu peux utiliser boost::ptr_vector.

  7. #7
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par Lolilolight Voir le message
    ça fonctionne aussi si ma classe Entity est polymorphique ?
    Oui, tes objets sont toujours stockés sous forme de std::unique_ptr<Entity>, donc pas de slicing.

    Citation Envoyé par Lolilolight Voir le message
    J'avais pensé faire une classe qui hérite de std::vector<std::unique_ptr<T>> et une fonction de conversion mais, je sais pas si c'est la meilleure solution en plus cela ne fonctionnerait qu'avec un seul type de conteneur.
    La conversion va être coûteuse, j'éviterais cette approche. Tu peux envisager d'hériter de std::vector<std::unique_ptr<T>>, mais uniquement par héritage privé. Il est probable que tu arrives à quelque chose qui soit proche de boost::ptr_vector (merci Flob pour le lien vers boost, je ne l'utilise pas et du coup je l'oublie toujours) ou plutôt du ptr_vector que j'utilise (source: github). Ma version est taillée pour l'utilisation avec std::unique_ptr et ne permet pas de stocker de pointeur invalide (nullptr ou pointeur vers de la mémoire non allouée) car les pointeurs ne sont jamais exposés à l'interface publique : la construction se fait "inplace" par copie ou mouvement et les éléments sont retournés directement par référence. En utilisant cette classe, tu peux avoir un comportement identique au code que j'ai cité ci-dessus en écrivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class MyClass {
        // Le conteneur que tu manipules en interne
        ptr_vector<Entity> m_entities;
     
        // Le reste de ton code
     
    public :
        MyClass() : entities(m_entities) { /* ... */ }
     
        // Tu n'exposes qu'une référence constante à l'utilisateur
        const ptr_vector<Entity>& entities;
     
        // Le reste de ton code
    };

Discussions similaires

  1. std::vector : dynamique ou statique, pile et tas
    Par salseropom dans le forum SL & STL
    Réponses: 7
    Dernier message: 24/01/2005, 13h22
  2. std::sort() sur std::vector()
    Par tut dans le forum SL & STL
    Réponses: 20
    Dernier message: 05/01/2005, 19h15
  3. char[50] et std::vector<>
    Par tut dans le forum SL & STL
    Réponses: 9
    Dernier message: 12/10/2004, 13h26
  4. Réponses: 8
    Dernier message: 26/08/2004, 18h59
  5. Sauvegarde std::vector dans un .ini
    Par mick74 dans le forum MFC
    Réponses: 2
    Dernier message: 12/05/2004, 13h30

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