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 :

double variadic template


Sujet :

Langage C++

  1. #1
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut double variadic template
    Un problème plus intéressant cette fois

    Problème simple mais solution moins simple :
    Comment disposer d'une classe comportant 2 boost::function<...> avec les templates de ces 2 boost function passés en template à ladite classe,
    c'est à dire est-ce possible d'utiliser 2 fois des template variadic dans une seule classe ou une seule fois en les "découpant" ?

    Ou du moins existe-t-il une solution le permettant ?

  2. #2
    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
    J'ai déjà eu à faire ça dans un de mes projets. Je m'en suis sorti en utilisant un type "séparateur". Voici un exemple de code.

    Le but est d'avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template<typename ... Args>
    struct test {
        typename func_types<Args...>::f1_type f1;
        typename func_types<Args...>::f2_type f2;
    };
     
    int main() {
        test<void, int, arg_separator, bool, const std::string&> t;
     
        // t.f1 est une std::function<void(int)>
        // t.f2 est une std::function<bool(const std::string&)>
    }
    D'abord quelques outils génériques :
    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
    #include <functional>
     
    // Le séparateur.
    struct arg_separator {};
     
    // Types utilitaires pour modifier une std::function :
    // Ajouter un argument à la fin de la liste
    template<typename T, typename Add> struct append;
    // Ajouter un argument au début de la liste
    template<typename T, typename Add> struct prepend;
    // Changer le type de retour
    template<typename T, typename Ret> struct set_ret;
     
    template<typename Add, typename Ret, typename ... Args>
    struct append<std::function<Ret(Args...)>, Add> {
        typedef std::function<Ret(Args..., Add)> type;
    };
    template<typename Add, typename Ret, typename ... Args>
    struct prepend<std::function<Ret(Args...)>, Add> {
        typedef std::function<Ret(Add, Args...)> type;
    };
     
    template<typename Ret, typename OldRet, typename ... Args>
    struct set_ret<std::function<OldRet(Args...)>, Ret> {
        typedef std::function<Ret(Args...)> type;
    };
    Puis le code à proprement parler :
    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
    // J'ai présenté l'algorithme ici comme si la lecture se faisait de gauche
    // à droite, mais en réalité ce sont les arguments de droite qui sont traités
    // en premier.
     
    // Déclarations anticipées :
    template<typename T, typename ... Args>
    struct func_types_impl_first_start;
    template<typename ... Args>
    struct func_types_impl_first;
    template<typename T, typename ... Args>
    struct func_types_impl_second_start;
    template<typename ... Args>
    struct func_types_impl_second;
     
    // On commence là, avec un simple typedef template pour faire joli,
    // par "func_types_impl_first_start".
    template<typename ... Args>
    using func_types = func_types_impl_first_start<Args...>;
     
    // On lit le type de retour de la première fonction,
    // puis on passe à "func_types_impl_first".
    template<typename T, typename ... Args>
    struct func_types_impl_first_start {
        typedef func_types_impl_first<Args...> base;
        typedef typename set_ret<typename base::f1_type, T>::type f1_type;
        typedef typename base::f2_type                            f2_type;
    };
     
    // Cas général : on ajoute un nouvel argument à la fonction 1.
    template<typename T, typename ... Args>
    struct func_types_impl_first<T, Args...> {
        typedef func_types_impl_first<Args...> base;
        typedef typename prepend<typename base::f1_type, T>::type f1_type;
        typedef typename base::f2_type                            f2_type;
    };
     
    // Cas final 1 : on a trouvé le séparateur.
    // On passe alors à "func_types_impl_second_start".
    template<typename ... Args>
    struct func_types_impl_first<arg_separator, Args...> {
        typedef func_types_impl_second_start<Args...> base;
        typedef typename base::f1_type f1_type;
        typedef typename base::f2_type f2_type;
    };
     
    // Cas final 2 : pas trouvé de séparateur... on s'arrête là.
    // La fonction 1 aura tous les arguments, et la fonction 2 aucun.
    template<>
    struct func_types_impl_first<> {
        typedef std::function<void()> f1_type;
        typedef std::function<void()> f2_type;
    };
     
    // On s'occupe maintenant de la fonction 2.
    // On lit son type de retour, et on passe à "func_types_impl_second".
    template<typename T, typename ... Args>
    struct func_types_impl_second_start {
        typedef func_types_impl_second<Args...> base;
        typedef typename base::f1_type                            f1_type;
        typedef typename set_ret<typename base::f2_type, T>::type f2_type;
    };
     
    // Cas général : on ajoute un argument à la fonction 2
    template<typename T, typename ... Args>
    struct func_types_impl_second<T, Args...> {
        typedef func_types_impl_second<Args...> base;
        typedef typename base::f1_type                            f1_type;
        typedef typename prepend<typename base::f2_type, T>::type f2_type;
    };
     
    // Cas final : les deux fonctions sont "vides" 
    // (c'est en réalité le point de départ ! question de point de vue...)
    template<>
    struct func_types_impl_second<> {
        typedef std::function<void()> f1_type;
        typedef std::function<void()> f2_type;
    };
    PS : Au passage : le premier code que je voulais montrer ici faisait planter gcc 4.7 (le compilateur, oui, oui) alors qu'il fonctionne bien dans le contexte où je l'utilise habituellement... Depuis que j'expérimente avec les templates variadiques, ce n'est pas la première fois que je me retrouve dans ce genre de situation, et ça montre bien que le support de ces fonctionnalités est encore expérimental.

  3. #3
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Merci pour ta réponse, je regarderai ça avec plus d'attention ce soir

  4. #4
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Technique intéressante (et je suis intéressé par le code qui fait planter gcc 4.7).

    Ceci dit, je me demande si - au niveau design - celà a vraiment un sens. Il me semble que ça permet principalement de résoudre un problème de design avec du code relativement complexe. Le fait est que ce cas pose un problème d'échelle. Deux template variadic à remplir avec une seule liste d'argument, passe encore, mais 3, 4, ... n ? Ca commence à devenir odieusement complexe.

    Ne vaut-t-il mieux pas passer par des objets intermédiaires - puisque de toute façon la position de arg_separator est définie par le programmeur, il peut tout autant utiliser un wrapper spécifique sans alourdir le code.

    Ou est-ce que je loupe quelque chose ?
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  5. #5
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    La méthode de Kalith m'a l'air assez novatrice en effet.
    Plus traditionnellement, la technique habituelle pour pouvoir passer deux ou plusieurs packs de paramètre vers une classe template consiste à wrapper les paramètres dans une structure intermédiaire, ce qui permet ensuite de récupérer les listes de paramètres séparément en spécialisant la classe. (difficile à expliquer, voir l'exemple plus bas)

    L'implémentation est à mon gout beaucoup plus simple que celle avec le séparateur, par contre l'utilisation est un peu plus lourde car il faut bien penser à wrapper les arguments. En clair au lieu d'écrire test<void, int, arg_separator, bool, const std::string&> t; il faudra écrire test<Wrapper<void, int>, Wrapper<bool, const std::string&>> t;
    Un exemple complet pour deux packs de paramètres :

    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 <functional>
    #include <string>
     
    using namespace std;
     
    template <typename... Fn>
    struct Args
    {
    };
     
     
    template <typename Args1, typename Args2>
    struct test
    {
    };
     
    template<typename... Fn1, typename... Fn2>
    struct test<Args<Fn1...>, Args<Fn2...>>
    {
        using f1_type = std::function<void(Fn1...)>;
        using f2_type = std::function<void(Fn2...)>;
     
        f1_type f1;
        f2_type f2;
     
        test(f1_type f1, f2_type f2):f1(f1), f2(f2){}
     
        void exe1(Fn1&&... args)
        {
            return f1(std::forward<Fn1>(args)...);
        }
        void exe2(Fn2&&... args)
        {
            return f2(std::forward<Fn2>(args)...);
        }
    };
     
     
    int main()
    {
        using type_t1 = test<Args<>, Args<>>;
        using type_t2 = test<Args<int>, Args<float>>;
     
        type_t1 t1{[]{cout << "f1\n";}, []{cout << "f2\n";}};
        t1.exe1();
        t1.exe2();
     
        type_t2 t2{[](int i){cout << to_string(i) << "\n";},[](float f){cout << to_string(f) << "\n";}};
        t2.exe1(4);
        t2.exe2(8.56f);
     
        return 0;
    }

  6. #6
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    @Kalith : Ta solution est sympathique à vrai dire tu clarifies mes idées, car je voulais justement quelque chose qui puisse "séparer" les arguments template, enfin tout du moins c'est ce à quoi je pensais, sans pour autant voir comment faire

    @Arzar : Merci pour cette solution plus simple mais tout aussi efficace, à vrai dire je ne sais pas laquelle des 2 je vais utiliser

    Enfin on peut dire que le sujet est résolu dans tous les cas, merci à tous

  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 Emmanuel Deloget Voir le message
    (et je suis intéressé par le code qui fait planter gcc 4.7).
    En voici une partie suffisante pour déclencher le crash (voir ci-dessous). À compiler avec g++ -std=c++11 test.cpp -o test.exe, on obtient alors :
    test.cpp:34:71: internal compiler error: Segmentation fault
    J'ai seulement testé avec MinGW, à voir sous linux. L'approche est la même dans le fond, mais la forme diffère un peu (je l'ai retravaillée et simplifiée plusieurs fois avant de poster ma première réponse) :
    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
    #include <functional>
     
    struct arg_separator {};
     
    template<typename Ret, typename ... Args>
    struct func_type {
        typedef std::function<Ret(Args...)> type;
     
        template<typename T>
        func_type<Ret, Args..., T> push_back();
        template<typename T>
        func_type<Ret, T, Args...> push_front();
    };
     
    template<typename ... Args>
    struct build_f1_type_;
     
    template<typename Type, typename T, typename ... Args>
    struct build_f1_type_<Type, T, Args...> {
        typedef typename build_f1_type_<decltype(((Type*)0)->template push_back<T>()), Args...>::type type;
    };
     
    template<typename Type, typename ... Args>
    struct build_f1_type_<Type, arg_separator, Args...> {
        typedef typename Type::type type;
    };
     
    template<typename Type, typename T>
    struct build_f1_type_<Type, T> {
        typedef typename decltype(((Type*)0)->template push_back<T>())::type type;
    };
     
    template<typename Ret, typename ... Args>
    using f1_type = typename build_f1_type_<func_type<Ret>, Args...>::type;
     
    template<typename ... Args>
    struct test {
        f1_type<Args...> f1;
    }
     
    int main() {
        test<void,int,arg_separator,bool,const std::string&> t;
    }
    Citation Envoyé par Emmanuel Deloget Voir le message
    Deux template variadic à remplir avec une seule liste d'argument, passe encore, mais 3, 4, ... n ? Ca commence à devenir odieusement complexe.
    Je suis d'accord, ça ne se scale pas bien pour de plus grands nombres de listes (O(n^2) en lignes de code). Mais dans mon cas au moins ça n'a jamais été l'objectif, je ne sais pas pour l'OP.

    L'approche d'Arzar est vraiment plus simple par contre... Pour la clarté du code, je pense que je préfèrerais la sienne

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

Discussions similaires

  1. Hierarchie et variadic template
    Par victor_gasgas dans le forum Langage
    Réponses: 5
    Dernier message: 22/09/2010, 17h24
  2. C++ 0x variadic template
    Par victor_gasgas dans le forum Langage
    Réponses: 6
    Dernier message: 21/09/2010, 07h26
  3. [C++0x] Variadic template : un constructeur par type
    Par Florian Goo dans le forum Langage
    Réponses: 2
    Dernier message: 08/04/2009, 18h33
  4. Réponses: 2
    Dernier message: 10/01/2009, 13h38

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