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

C++ Discussion :

vue "union" de conteneurs


Sujet :

C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut vue "union" de conteneurs
    Bonjour à tous,

    Je voulais savoir s'il existait une fonctionnalité dans boost ou si vous aviez une idée sur comment faire pour avoir une "vue union" sur plusieurs conteneurs.
    Un exemple de ce que je voudrai réaliser:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
    	std::vector<int> v1 = {1, 2, 3};
    	std::vector<int> v2 = {4, 5, 6};
    	for(int& i, make_union_view(v1, v2))
    		std::cout << i << " ";	//affiche 1, 2, 3, 4, 5, 6
    	std::cout << std::endl;
    	return 0;
    }
    make_union_view ne ferait pas de copie, ce serait une structure permettant d'itérer en lazy sur les conteneurs qui lui sont passés. Dans un premier temps, j'aimerai juste pouvoir le faire sur des conteneurs de même type, mais à terme ce qui serait bien c'est de pouvoir avoir un code comme celui-ci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int main()
    {
    	std::vector<int> 	v1 = {1, 2, 3};
    	std::list<int> 		v2 = {4, 5, 6};
    	std::set<int> 		v3 = {7, 8, 9};
    	std::map<int, int>	v4 = {{1, 10}, {2, 11}, {3, 12}};
    	for(int& i, make_union_view(v1, v2, v3, v4 | boost::adaptors::map_values))
    		std::cout << i << " ";	//affiche 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
    	std::cout << std::endl;
    	return 0;
    }

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Pas besoin d'aller chercher dans boost, la librairie standard possède déjà une fonction set_union. Pour reprendre ton premier exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    std::vector<int> v1 = {1, 2, 3};
    std::vector<int> v2 = {4, 5, 6};
    set_union( v1.begin(), v1.end(), v2.begin(), v2.end(), std::ostream_iterator<int>(std::cout) );
    Si tu veux passer un fonction quelconque, il doit être assez facile de créer un itérateur de sortie prenant en paramètre une fonction (ou un foncteur) et l’appelant à chaque appel de l’opérateur =.
    Par contre, avec cette méthode, le type des conteneur est quelconque, mais tu ne peux en avoir que 2.

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    Merci Joe Dralliam mais il y a deux défauts à cette méthode d'après la documentation de cplusplus.com:
    set_union : Union of two sorted ranges
    Constructs a sorted range beginning in the location pointed by result with the set union of the two sorted ranges
    Il faut donc que les ranges soient triés (ce n'est pas forcément mon cas) et la méthode "construit" un range alors que j'aimerai que ce soit réalisé en "lazy"

    C'est vrai que mon code d'exemple n'était pas très parlant voici un nouvel exemple (j'espère plus clair):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    std::vector<int> v1 = {3, 2, 1};
    std::vector<int> v2 = {4, 5, 6};
    for(int& i, make_union_view(v1, v2))
    		std::cout << i << " ";	//affiche 3, 2, 1, 4, 5, 6 et non pas 1, 2, 3, 4, 5, 6

  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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Sauf que set_union :
    - Demande que v1 et v2 soient triés
    - Va supprimer les doublons entre v1 et v2 (c'est son principal rôle)
    - A une interface qui fait que si on veut faire plus que stocker le résultat quelque part (y compris dans un flux), ce n'est pas fait pour, il faut redéfinir un itérateur

    Pour répondre à la question initiale, le plus proche que je connaisse dans boost est http://www.boost.org/doc/libs/1_49_0...doc/index.html qui permet de redéfinir plus simplement des itérateurs, et en défini même quelques uns (mais pas le cas que tu propose).
    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 émérite

    Inscrit en
    mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Boost.range contient *presque* ce qu'il faut avec <boost/range/join.hpp>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    std::vector<int> v1 = {3, 2, 1};
    std::vector<int> v2 = {4, 5, 6};
    for(int& i, boost::join(v1, v2))
       std::cout << i << " ";	//affiche 1, 2, 3, 4, 5, 6
    Malheureusement la fonction join est limité à deux arguments.
    Il me semble qu'il y a eu pas mal de discussion sur la mailing-list de boost pour étendre join() à plusieurs arguments en créant un range sous-jacent multi-vue. Dans mes souvenirs le problème majeur est qu'il faut donc implémenter un itérateur capable d'itérer sur des multi-segments et c'est un problème difficile qui s'accompagne toujours d'une perte de perf assez catastrophique. (Car ce sont des itérateurs lourds, qui doivent maintenir de nombreux états et de nombreuses informations, comme par exemple une sorte de liste des différents conteneurs à parcourir, leur taille respective, le conteneur courant etc., et qui oblige à chaque itération à faire de nombreux test pour toujours correctement se situer dans la liste des conteneurs à parcourir. )

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : avril 2009
    Messages : 199
    Points : 106
    Points
    106
    Par défaut
    Une classe prenant un nombre définie à la compilation de boost::range? (template variadic)?

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    avril 2009
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : avril 2009
    Messages : 199
    Points : 106
    Points
    106
    Par défaut
    Bon c'est un des premiers codes que je donne en réponse, donc soyez indulgent

    Je m'en suis sortis avec des variadics templates, unes range-based for-loop, et des lamdbas (sachant que c'est deux dernières ne sont pas obligatoire).
    Le code compile sous g++-4.7 avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    g++ -o m main.cpp -std=c++11 -W -Werror -Wall -Wfatal-errors -pedantic -pedantic-errors -O2

    Le code en question:
    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
    #include <iostream>
    #include <vector>
    #include <list>
     
    template<typename Func, typename... Args>
    struct DoIt;
     
    template<typename Func>
    struct DoIt<Func>
    {
        static void iteration(Func f) { };
    };
     
    template<typename Func, typename Head>
    struct DoIt<Func, Head>
    {
        static void iteration(Func f, Head arg) 
        {
            for (auto &x : arg)  f(x); 
        };
    };
     
    template<typename Func, typename Head, typename... Tail>
    struct DoIt<Func, Head, Tail...>
    {
        static void iteration(Func f, Head h, Tail... args) 
        {
            DoIt<Func, Head>::iteration(f, h);
     
            DoIt<Func, Tail...>::iteration(f, args...);
        };
    };
     
     
     
     
     
     
    template<typename Func, typename... Args>
    void make_union_view(Func f, Args... args)
    {
        DoIt<Func, Args...>::iteration(f, args...);
    }
     
     
    int main()
    {
        std::vector<int> vec(3, 4);
        std::list<int> list(6, 1);
     
        make_union_view( [](int a){std::cout << a << std::endl;}, vec, list );
     
        return 0;
    }
    J'itère sur des collections simple : comprendre std::vector, std::list.. Mais pas de std::map par exemple.. Mais je pense qu'avec boost::range on doit pouvoir s'en sortir!

    Je me doute que le code doit souffrir sur quelques points.. En effet je pense que les variadics sont "optimisables" avec des r-value et std::forward (si je dis pas une bétise) pour avoir une évaluation en lazy. Mais je suis très intéressé par tous les retours!!

    Je pense aussi qu'un défaut, c'est qu'on passe un foncteur en paramètre, et c'est pas tout à fait le design recherché par l'auteur du post.

    P.S. Ca fonctionne pas avec les std::set :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    In file included from /usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/set:60:0,
                     from main.cpp:4:
    /usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_tree.h: In instantiation of ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_II, _II) [with _InputIterator = int; _Key = int; _Val = int; _KeyOfValue = std::_Identity<int>; _Compare = std::less<int>; _Alloc = std::allocator<int>]’:
    /usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_set.h:166:4:   required from ‘std::set<_Key, _Compare, _Alloc>::set(_InputIterator, _InputIterator) [with _InputIterator = int; _Key = int; _Compare = std::less<int>; _Alloc = std::allocator<int>]’
    main.cpp:51:27:   required from here
    /usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_tree.h:1474:4: erreur: invalid type argument of unary ‘*’ (have ‘int)
    compilation terminated due to -Wfatal-errors.
    Ni avec les std::queue
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    main.cpp: In instantiation of ‘static void DoIt<Func, Head>::iteration(Func, Head) [with Func = main()::<lambda(int)>; Head = std::queue<int>]’:
    main.cpp:32:9:   recursively required from ‘static void DoIt<Func, Head, Tail ...>::iteration(Func, Head, Tail ...) [with Func = main()::<lambda(int)>; Head = std::list<int>; Tail = {std::queue<int, std::deque<int, std::allocator<int> > >}]’
    main.cpp:32:9:   required from ‘static void DoIt<Func, Head, Tail ...>::iteration(Func, Head, Tail ...) [with Func = main()::<lambda(int)>; Head = std::vector<int>; Tail = {std::list<int, std::allocator<int> >, std::queue<int, std::deque<int, std::allocator<int> > >}]’
    main.cpp:44:5:   required from ‘void make_union_view(Func, Args ...) [with Func = main()::<lambda(int)>; Args = {std::vector<int, std::allocator<int> >, std::list<int, std::allocator<int> >, std::queue<int, std::deque<int, std::allocator<int> > >}]’
    main.cpp:54:78:   required from here
    main.cpp:21:9: erreur: no matching function for call to ‘begin(std::queue<int>&)’
    compilation terminated due to -Wfatal-errors.

  8. #8
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Sinon, avec boost::join, ne pourrait-on pas faire ceci ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    for (int &i, boost::join(boost::join(v1, v2), boost::join(v3, v4))) { /* ... */ }

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    Merci Arzar, c'est exactement ce dont j'ai besoin (pour le moment je n'ai pas besoin de faire une vue sur plus de deux conteneurs). J'avais commencé à implémenter un itérateur maison et effectivement, ils sont bien lourds: dans mon ébauche d'implémentation j'avais par itérateur:
    - un pointeur sur le conteneur courant,
    - un itérateur sur le conteneur de conteneurs
    - un itérateur du conteneur courant
    Je pensais stocker le conteneur de conteneurs dans un union_range.
    Bref c'est nikel pour mon besoin actuel

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

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