Developpez.com - Rubrique C++

Le Club des Développeurs et IT Pro

[Aller plus loin] C++ - Extraire des collections de données depuis une collection initiale

Un billet blog de Bousk

Le 11/11/2020, par Bousk, Rédacteur/Modérateur
Dans le billet précédent, nous avons vu comment extraire des collections de données depuis un vector sans aucune allocation supplémentaire.
La conclusion laissait entendre que ce système est applicable à d'autres collections que std::vector.

Ça l'est, et relativement simplement.

Pour y parvenir, il suffit d'ajouter un niveau de template, en modifiant le template pour pas être le type de l'élément mais de la collection.
Ainsi, VectorView devient donc ContainerView:
Code c++ :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <class Container> 
class ContainerView 
{ 
public: 
    using Type = Container; 
    using Element = typename Type::value_type; 
    using Iterator = typename Type::iterator; 
    using ConstIterator = typename Type::const_iterator; 
public: 
    ContainerView() 
    { 
        // Default values to prevent iterating over random memory 
        m_begin = m_end; 
    } 
    ContainerView(typename ConstIterator first, typename ConstIterator end) 
        : m_begin(first) 
        , m_end(end) 
    {} 
    ConstIterator begin() const { return m_begin; } 
    ConstIterator end() const { return m_end; } 
private: 
    typename ConstIterator m_begin, m_end; 
};
Et nous pouvons créer des allias pour continuer à utiliser VectorView aisément:
Code c++ :
1
2
3
4
5
  
template<class T> 
using VectorView = ContainerView<std::vector<T>>; 
template<class T> 
using ListView = ContainerView<std::list<T>>;

Il faut propager le changement aux fonctions.
Les plus attentifs auront remarqué que des allias sur le container interne, ses itérateurs et le type interne ont été ajouté à ContainerView. La syntaxe correspond aux types de std, mais peut être adaptée à vos propres collections.
Ceci permet de simplifier son utilisation, en passant un unique paramètre template qui sera la vue:
Code c++ :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  
template<class View> 
typename View::Iterator CreateLists(typename View::Iterator first, typename View::Iterator end, View& list) 
{ 
    list = View(first, end); 
    return end; 
} 
template<class View> 
typename View::Iterator CreateLists(typename View::Iterator first, typename View::Iterator end, View& list, const std::function<bool(typename View::Element)>& extracter) 
{ 
    auto firstNonExtracted = std::stable_partition(first, end, extracter); 
    list = View(first, firstNonExtracted); 
    return firstNonExtracted; 
} 
template<class View, class... Args> 
void CreateLists(typename View::Iterator first, typename View::Iterator end, View& list, const std::function<bool(typename View::Element)>& extracter, Args&&... args) 
{ 
    auto newFirst = CreateLists<View>(first, end, list, extracter); 
    CreateLists<View>(newFirst, end, std::forward<Args>(args)...); 
}
Enfin, petite mise à jour similaire pour notre petite fonction d'affichage de collection:
Code c++ :
1
2
3
4
5
6
7
8
template<class View> 
void PrintValues(const char* name, View values) 
{ 
    std::cout << name << " : "; 
    for (auto&& value : values) 
        std::cout << value << ", "; 
    std::cout << std::endl; 
}

L'utilisation est identique, voire simplifiée : le paramètre template à l'appel de CreateLists, que VS2019 ne semblait pas pouvoir déduire et était obligatoire dans le premier billet, ne l'est plus !
Code c++ :
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
int main() 
{ 
    { 
        std::vector<int> values; 
        for (int i = 0; i < 100; ++i) 
            values.push_back(i); 
  
        VectorView<int> pairs; 
        VectorView<int> mul3; 
        VectorView<int> mul5; 
        VectorView<int> mul7; 
        VectorView<int> others; 
  
        CreateLists(values.begin(), values.end() 
            , pairs, [](int v) { return v % 2 == 0; } 
        , mul3, [](int v) { return v % 3 == 0; } 
        , mul5, [](int v) { return v % 5 == 0; } 
        , mul7, [](int v) { return v % 7 == 0; } 
            , others 
            ); 
  
        PRINT_VALUES_WITH_NAME(pairs); 
        PRINT_VALUES_WITH_NAME(mul3); 
        PRINT_VALUES_WITH_NAME(mul5); 
        PRINT_VALUES_WITH_NAME(mul7); 
        PRINT_VALUES_WITH_NAME(others); 
    } 
    { 
        std::list<int> values; 
        for (int i = 0; i < 100; ++i) 
            values.push_back(i); 
  
        ListView<int> pairs; 
        ListView<int> mul3; 
        ListView<int> mul5; 
        ListView<int> mul7; 
        ListView<int> others; 
  
        CreateLists(values.begin(), values.end() 
            , pairs, [](int v) { return v % 2 == 0; } 
            , mul3, [](int v) { return v % 3 == 0; } 
            , mul5, [](int v) { return v % 5 == 0; } 
            , mul7, [](int v) { return v % 7 == 0; } 
            , others 
            ); 
  
        PRINT_VALUES_WITH_NAME(pairs); 
        PRINT_VALUES_WITH_NAME(mul3); 
        PRINT_VALUES_WITH_NAME(mul5); 
        PRINT_VALUES_WITH_NAME(mul7); 
        PRINT_VALUES_WITH_NAME(others); 
    } 
  
	return 0; 
}
  Billet blog