Voir le flux RSS

Bousk

[Actualité] Filtrer les données d'un conteneur pendant une itération via ranged-based for loop

Noter ce billet
par , 05/08/2019 à 18h55 (1370 Affichages)
Les ranged-base for loop apparues en C++11 sont un excellent moyen d'itérer sur l'ensemble des données d'un conteneur.
Mais si l'on veut ne traiter que certaines entrées selon un critère/filtre, il faut alors ajouter le filtre dans la boucle afin de ne pas exécuter l'opération sur cette entrée, ce qui n'est pas très élégant.
En attendant une solution du langage, possiblement via les ranged, dans une version future, voici un moyen relativement simple de filtrer dans la boucle, en C++11 uniquement :
Code c++ : 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
#include <functional>
template<class Container>
class ForRangedLoopFilter
{
    using value_type = typename Container::value_type;
    using iterator = typename Container::iterator;
 
public:
    friend class Iterator;
    class Iterator
    {
    public:
        Iterator(ForRangedLoopFilter& owner)
            : mOwner(owner)
        {
            mIterator = first();
        }
        Iterator(ForRangedLoopFilter& owner, bool)
            : mOwner(owner)
        {
            mIterator = last();
        }
 
        value_type& operator*() { return *mIterator; }
        value_type& operator->() { return *mIterator; }
 
        Iterator& operator++() { mIterator = next(); return *this; }
 
        bool operator !=(const Iterator& other) const { return mIterator != other.mIterator; }
 
    private:
        iterator first()
        {
            mIterator = mOwner.mContainer.begin();
            if (!mOwner.mFilterFunction(*mIterator))
                mIterator = next();
            return mIterator;
        }
        iterator next()
        {
            do {
                mIterator++;
            } while (mIterator != last() && !mOwner.mFilterFunction(*mIterator));
            return mIterator;
        }
        iterator last() { return mOwner.mContainer.end(); }
 
    private:
        ForRangedLoopFilter& mOwner;
        iterator mIterator;
    };
 
public:
    template<class FilterFunc>
    ForRangedLoopFilter(Container& container, FilterFunc func)
        : mContainer(container)
        , mFilterFunction(func)
    {}
 
    Iterator begin() { return Iterator(*this); }
    Iterator end() { return Iterator(*this, true); }
 
private:
    Container& mContainer;
    std::function<bool(const value_type& entry)> mFilterFunction;
};
Le code n'est certainement pas parfait mais vous donne une idée des possibilités. Prenez ceci comme une base.

Voici un exemple d'utilisation, pour afficher les nombres pairs sur [0,9] :
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <vector>
int main()
{
    std::vector<int> vec{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (int v : ForRangedLoopFilter<std::vector<int>>(vec, [](int v) { return v%2 == 0;}))
    {
        std::cout << v << std::endl;
    }
    return 0;
}
Pouvoir indiquer le filtre dans la déclaration de la boucle rend le code plus clair sur son but et utilisation selon moi.

Afin d'être utilisé avec un conteneur constant, il suffit d'ajouter un constructeur compatible :
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <functional>
template<class Container>
class ForRangedLoopFilter
{
...
public:
...
    template<class FilterFunc>
    ForRangedLoopFilter(const Container& container, FilterFunc func)
        : mContainer(const_cast<Container&>(container))
        , mFilterFunction(func)
    {}
...
};
Je garde la référence interne non constante afin de simplifier le code, je ne fais aucune modification du conteneur donc il n'y a pas de soucis.

Code c++ : 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
#include <iostream>
#include <string>
#include <vector>
int main()
{
    struct Toto {
        int value;
        std::string nom;
    };
    const std::vector<Toto> vec{ {0, "toto"}, {1, "tata"}, {2, "titi"} };
    for (const Toto& v : ForRangedLoopFilter< std::vector<Toto>>(vec, [](const Toto& v) { return v.value % 2 == 0; }))
    {
        std::cout << v.value << ":" << v.nom << std::endl;
    }
    return 0;
}

Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog Viadeo Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog Twitter Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog Google Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog Facebook Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog Digg Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog Delicious Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog MySpace Envoyer le billet « Filtrer les données d'un conteneur pendant une itération via ranged-based for loop » dans le blog Yahoo

Mis à jour 23/08/2019 à 15h42 par Bousk

Catégories
Programmation , C++

Commentaires