Voir le flux RSS

Bousk

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

Noter ce billet
par , 09/09/2019 à 05h25 (232 Affichages)
Suite à mon précédent billet j'ai voulu pousser un peu pour arriver à une syntaxe telle que for (auto value : vec | [](...){...}) que je trouve plutôt attrayante. Et surtout ne pas avoir à taper le nom de la classe au complet, chose que je trouve des plus pénibles.

Ce fût étonnamment rapide puisque le simple ajout d'un opérateur libre permet d'y parvenir :
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
template<class Container>
ForRangedLoopFilter<Container> operator|(Container& container, std::function<bool(const typename Container::value_type& entry)> filter)
{
	return ForRangedLoopFilter<Container>(container, filter);
}
Ce code compile sous VS 2017 à partir de C++14 (et possiblement C++11) et est accepté par Clang et g++ en C++11.

Afin de réutiliser l'exemple du billet précédent, il convient d'ajouter le support pour un vector constant:
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
template<class Container>
ForRangedLoopFilter<Container> operator|(const Container& container, std::function<bool(const typename Container::value_type& entry)> filter)
{
	return ForRangedLoopFilter<Container>(container, filter);
}

Maintenant, il est assez simple d'étoffer cette base pour pouvoir chaîner les filtres en modifiant légèrement la classe de filtrage et en ajoutant un opérateur| interne:
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
template<class Container>
class ForRangedLoopFilter
{
	using value_type = typename Container::value_type;
	using iterator = typename Container::iterator;
 
public:
	friend class Iterator;
	class Iterator
	{
...
	private:
		iterator first()
		{
			mIterator = mOwner.mContainer.begin();
			if (!isValid(mIterator))
				mIterator = next();
			return mIterator;
		}
		iterator next()
		{
			do {
				mIterator++;
			} while (mIterator != last() && !isValid(mIterator));
			return mIterator;
		}
...
 
		bool isValid(const iterator& it) const
		{
			for (auto&& func : mOwner.mFilterFunctions)
			{
				if (!func(*it))
					return false;
			}
			return true;
		}
...
	};
 
public:
	template<class FilterFunc>
	ForRangedLoopFilter(Container& container, FilterFunc func)
		: mContainer(container)
	{
		mFilterFunctions.push_back(func);
	}
	template<class FilterFunc>
	ForRangedLoopFilter(const Container& container, FilterFunc func)
		: mContainer(const_cast<Container&>(container))
	{
		mFilterFunctions.push_back(func);
	}
...
	ForRangedLoopFilter<Container> operator|(std::function<bool(const value_type& entry)> filter)
	{
		mFilterFunctions.push_back(filter);
		return *this;
	}
 
private:
	Container & mContainer;
	std::vector<std::function<bool(const value_type& entry)>> mFilterFunctions;
};
Notez que je retourne une copie depuis l'opérateur | interne : c'est le seul moyen que j'ai trouvé pour faire fonctionner la syntaxe chaînée, sinon une copie est faîte et aucun filtre n'est appliqué...
Pour que le retour par référence fonctionne, il faudrait découper le code en 2 étapes :
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
	auto range = vec | [](int v) { return v % 2 == 0; } | [](int v) { return v % 3 == 0; };
	for (int v : range)
et alors les 2 filtres sont appliqués.
Vu le faible poids d'un std::function et la facilité à les copier, je préfère faire une copie et permettre la syntaxe chaînée directement dans le for.

Le code complet:
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
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
#include <functional>
#include <vector>
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 (!isValid(mIterator))
				mIterator = next();
			return mIterator;
		}
		iterator next()
		{
			do {
				mIterator++;
			} while (mIterator != last() && !isValid(mIterator));
			return mIterator;
		}
		iterator last() { return mOwner.mContainer.end(); }

		bool isValid(const iterator& it) const
		{
			for (auto&& func : mOwner.mFilterFunctions)
			{
				if (!func(*it))
					return false;
			}
			return true;
		}

	private:
		ForRangedLoopFilter & mOwner;
		iterator mIterator;
	};

public:
	template<class FilterFunc>
	ForRangedLoopFilter(Container& container, FilterFunc func)
		: mContainer(container)
	{
		mFilterFunctions.push_back(func);
	}
	template<class FilterFunc>
	ForRangedLoopFilter(const Container& container, FilterFunc func)
		: mContainer(const_cast<Container&>(container))
	{
		mFilterFunctions.push_back(func);
	}

	Iterator begin() { return Iterator(*this); }
	Iterator end() { return Iterator(*this, true); }

	ForRangedLoopFilter<Container> operator|(std::function<bool(const value_type& entry)> filter)
	{
		mFilterFunctions.push_back(filter);
		return *this;
	}

private:
	Container & mContainer;
	std::vector<std::function<bool(const value_type& entry)>> mFilterFunctions;
};

template<class Container>
ForRangedLoopFilter<Container> operator|(Container& container, std::function<bool(const typename Container::value_type& entry)> filter)
{
	return ForRangedLoopFilter<Container>(container, filter);
}
template<class Container>
ForRangedLoopFilter<Container> operator|(const Container& container, std::function<bool(const typename Container::value_type& entry)> filter)
{
	return ForRangedLoopFilter<Container>(const_cast<Container&>(container), filter);
}


#include <iostream>
#include <vector>
#include <string>
int main()
{
	std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	for (int v : vec | [](int v) { return v % 2 == 0; } | [](int v) { return v % 3 == 0; })
	{
		std::cout << v << std::endl;
	}
	struct Toto {
		int value;
		std::string nom;
	};
	const std::vector<Toto> vec2{ { 0, "toto" },{ 1, "tata" },{ 2, "titi" } };
	for (const Toto& v : vec2 | [](const Toto& v) { return v.value % 2 == 0; } | [](const Toto& v) { return v.nom.find('i') != std::string::npos; })
	{
		std::cout << v.value << ":" << v.nom << std::endl;
	}
	return 0;
}

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

Commentaires