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 :

Template, classe Bouton


Sujet :

C++

  1. #1
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut Template, classe Bouton
    Bonjour,

    J'ai récemment appris à faire des template et je suis légèrement bloqué.

    J'ai une classe bouton avec un template :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template<typename DONNEE1, typename DONNEE2, typename FONCTION1, typename FONCTION2>
    class Bouton
    DONNEE1 est le type de l'argument qui sera utilisée pour FONCTION1
    DONNEE2 est le type de l'argument qui sera utilisée pour FONCTION2

    Mais quand il n'y a pas d'argument, j'aimerais bien définir :
    Bouton<void, void, ma_fonction1, ma_fonction2>
    Ou quand il n'y pas une des deux fonction,
    Bouton<void, void, void, ma_fonction2>

    Mais quand je fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template <typename DONNEE2, typename FONCTION1, typename FONCTION2>
    void Bouton<int, DONNEE2, FONCTION1, FONCTION2>::setDonneeClique()
    {
        std::cerr << "Cette fonction ne prend pas d'argument" << std::endl;
    }
    il le compilateur me dit :
    C:\Users\Neckara\Desktop\Patcher (sources)\src\..\include\Bouton.h|107|error: invalid use of incomplete type 'class Bouton<int, DONNEE2, FONCTION1, FONCTION2>'|
    C:\Users\Neckara\Desktop\Patcher (sources)\src\..\include\Bouton.h|9|error: declaration of 'class Bouton<int, DONNEE2, FONCTION1, FONCTION2>'|
    ||=== Build finished: 2 errors, 0 warnings ===|
    NB : j'ai mis 'int' pour vérifié si ce n'est pas pas 'void' qui posait problème


    Le deuxième problème qui se pose, c'est que j'ai une classe Principale qui a deux sous-classes Bouton (jouer et desinstaller) ainsi qu'une sous-classe Serveur (gère les mises à jours).

    Mais il faut que Serveur ai un pointeur de type Bouton sur chacun des deux Bouton de Principal.
    Or la fonction a exécuter par le Bouton jouer est une méthode de Principal.
    J'ai donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Bouton <void *, void *,void (*)(void *), void (Principal::*)(void *)>Jouer;
    Dans Principal (les void * sont là parce que je n'ai pas encore réussi à mettre des void)

    Mais pour définir un pointeur Jouer il faut que je mettre dans Serveur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Bouton <void *, void *,void (*)(void *), void (Principal::*)(void *)>* Jouer;
    Mais Serveur ne connait pas Principal puisque Serveur est une sous-classe de Principal.
    Si je met l'en-tête de Principal au début de celle de Serveur, ceci ne marchera pas puisque l'en-tête de Principal aura besoin de celle de Serveur avant, c'est un peu le serpent qui se mort la queue.
    J'ai tenté de faire un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    extern class Principal;
    Mais comme attendu ceci n'a pas marché...

    Auriez-vous des idées?

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 476
    Par défaut
    Hello,

    Le moins que l'on puisse dire, c'est que c'est TRÈS tordu. Tu te poses ces cas de figure pour l'exercice ou tu as réellement architecturé ton programme de la sorte ?

    Pour la question 1 : Quand tu ajoutes « <> » à la suite d'un identifiant, c'est pour définir un cas d'usage ou pour l'instancier. Pas pour le déclarer. Ce que tu veux faire, en fait, c'est utiliser un nom de type par défaut si celui-ci n'est pas explicitement spécifié. Dans ce cas, ça se fait dans la déclaration de « template <…> »., comme pour les fonctions :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template <typename DONNEE1=int>
    … par contre, il y a des chances pour que tu sois obligé de mettre ces paramètres par défaut à la fin de ta déclaration.

    Pour la question 2 : si tu veux faire deux classes qui se référencent mutuellement, tu peux les déclarer comme telles sans définir leur contenu :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class A;
    class B;
     
    class A
    {
        public:
            B * b;
    };
     
    class B
    {
        public:
            A * a;
    };
    C'est parfois nécessaire, mais c'est généralement aller au devant d'ennuis pour pas grand chose.

  3. #3
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Merci pour ta réponse, maintenant ça marche très bien^^

    Le problème n'était pas de mettre une valeur par défaut, mais de faire en sorte qu'il puisse ne pas y avoir de valeur.

    Par exemple si FONCTION1 est : void (*)(void) je vais avoir un problème lors de la compilation car j'ai dans une fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    DONNEES1 d1
    FONCTION1 f1
    CLASSE1 c1 (ici (Principal *))
    (c1->*f1)(d1);
    De même si la fonction f1 n'existe pas (pas de fonction lancée lors du clique).
    Ou si la classe c1 n'existe pas (idem, pas de fonction lancée lors du clique).

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 476
    Par défaut
    Il y a deux cas à distinguer :

    1. Les paramètres template par défaut

    Malgré ce que tu es en train de faire, c'est le cas le plus approprié dans le contexte qui t'intéresse :

    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
    #include <iostream>
     
    using namespace std;
     
    template <class A, class B, class C=char>
    class Toto
    {
        public:
            void affiche ();
    };
     
    template <class A, class B, class C>
    void Toto<A,B,C>::affiche ()
    {
        cout <<    "A: " << sizeof (A)
             << " - B: " << sizeof (B)
             << " - C: " << sizeof (C)
             << endl;
    }
     
    int main (void)
    {
        Toto<int,int>      x;
        Toto<int,int,int>  y;
     
        x.affiche();
        y.affiche();
     
        return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $ ./programme
    A: 4 - B: 4 - C: 1
    A: 4 - B: 4 - C: 4

    Dans cet exemple, on voit bien que l'on a réussi à instancier deux variantes de la même classe, avec une seule et même définition. Dans le corps de main(), je passe la première fois deux paramètres template et, la seconde fois, trois paramètres template.

    Dans le résultat, quand on examine la taille de C, on voit bien que dans le premier cas, c'est un char qui a été choisi par défaut, comme spécifié et que, dans le second cas, c'est bien la largeur d'un int qui apparaît.

    Une valeur « par défaut » n'est pas simplement une valeur initiale. C'est bien la valeur à utiliser lorsque le paramètre « fait défaut ».



    2 La spécialisation partielle

    L'idée est de faire une spécialisation, c'est-à-dire une écriture de code spécifique à un type donné, mais en spécifiant certains paramètres seulement, et en laissant les autres génériques.

    Cela dit, si tu veux faire une spécialisation partielle d'une classe, tu ne pourras pas spécialiser directement ces fonctions-membres : il te faudra ré-écrire une définition de classe spécialisée. Voici un exemple qui mêle paramètre par défaut et spécialisation partielle :

    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
    #include <iostream>
     
    using namespace std;
     
    template <class A,class B,class C,class D=char>
    class Exemple
    {
        public:
            void id();
    };
     
    template <class B,class C,class D>
    class Exemple<float,B,C,D>
    {
        public:
            void id();
    };
     
    template <class B,class C,class D>
    void Exemple<float,B,C,D>::id()
    {
        cout << "Classe Exemple spécial float" << endl;
     
        cout << "  A: " << "f"
             << "  B: " << sizeof (B)
             << "  C: " << sizeof (C)
             << "  D: " << sizeof (D)
             << endl;
    }
     
    template <class A,class B,class C,class D>
    void Exemple<A,B,C,D>::id()
    {
        cout << "Classe Exemple général" << endl;
     
        cout << "  A: " << sizeof (A)
             << "  B: " << sizeof (B)
             << "  C: " << sizeof (C)
             << "  D: " << sizeof (D)
             << endl;
    }
     
    int main (void)
    {
        Exemple<float,float,float,float>       x;
        Exemple<float,float,float>             y;
        Exemple<char,char,char>                z;
        Exemple<char,char,char,int>            i;
        Exemple<char,char,char,float>          j;
        Exemple<double,double,double>          a;
        Exemple<double,double,double,double>   b;
     
        x.id();
        y.id();
        z.id();
        i.id();
        j.id();
        a.id();
        b.id();
     
        return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $ ./programme
    Classe Exemple spéciale float
      A: f  B: 4  C: 4  D: 4
    Classe Exemple spéciale float
      A: f  B: 4  C: 4  D: 1
    Classe Exemple générale
      A: 1  B: 1  C: 1  D: 1
    Classe Exemple générale
      A: 1  B: 1  C: 1  D: 4
    Classe Exemple générale
      A: 1  B: 1  C: 1  D: 4
    Classe Exemple générale
      A: 8  B: 8  C: 8  D: 1
    Classe Exemple générale
      A: 8  B: 8  C: 8  D: 8

    On constate plusieurs choses :
    • On a pu, dans main(), instancier « Exemple » avec trois ou quatre paramètres, mais uniquement grâce au paramètre par défaut. Si tu retires le « =char » de la ligne 5, le programme ne compile plus ;
    • La spécialisation partielle m'a permis de définir, en plus du code général pour tous les cas non explicitement pris en charge, de définir du code pour tous les cas où le premier paramètre template est float, quels que soient les autres ;
    • Pour cela, il m'a fallu spécialiser la fonction-membre id() mais, celle-ci faisant partie d'une classe qui, ELLE, est template, il fallait d'abord que je spécialise la classe entière, pour pouvoir ensuite définir une fonction-membre spéciale, appartenant à cette classe spécialisé. J'ai donc deux définitions de la même classe et deux définitions de la même fonction-membre ;
    • « template <> » n'est pas une signature. C'est juste un prélude à une définition quelconque qui va enrichir le langage nécessaire pour la décrire ;
    • Quelque soit les spécialisations que l'on fait et le nombre de paramètres template restant ou non à spécifier, c'est toujours la classe originale qui fait foi et le nombre de paramètres à lui passer reste le même.


    Pour résumer tout cela :

    • Ce sont les paramètres par défaut qui vont te permettre d'en omettre par la suite si tu le souhaites ;
    • Si tu fais une spécialisation de classe, il faut en fait spécialiser la classe elle-même, puis écrire les fonctions-membres de chacune de ces spécialités, mais spécialiser directement les fonctions-membres.

  5. #5
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Je vois, merci pour ton aide.

    Sinon, une personne m'a conseillée de créer une classe "nothing" qui servira au bouton d'appeler des fonctions ne faisant rien.

    J'ai tout de même 2 questions :

    Pour un template<typename A=int, typename B=int, typename C=int>, comment fait-on pour mettre le type B par défaut tout en spécifiant un type A et C?



    Dans le cas d'argument vide, quel type X utiliser?

    Car il faudra que je spécialise une classe pour dire que l'argument est vide quand le type sera X.

    Mais il ne faudrait pas que l'utilisateur, en utilisant le type X appelle sa fonction sans argument alors qu'elle en nécessite.

    Un type "void" est-il possible (au pire je testerais ce soir)

    EDIT : je suis en train de me demander si les typedef int Mon_type sont considéré comme des types à part entière par les templates (je testerais ce soir)

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 476
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Sinon, une personne m'a conseillée de créer une classe "nothing" qui servira au bouton d'appeler des fonctions ne faisant rien.
    Ça me paraît douteux comme principe. Pourrais-tu nous ré-expliquer au propre ce que tu comptes faire exactement et la chemin que tu comptes suivre ? Parce que présenté comme ça, il semble que tu aies posé tes fondations sur du sable et que tu essaies d'utiliser des artifices a posteriori pour pallier les uns après les autres les problèmes qui surgissent.

    Ce n'est absolument pas une critique, c'est très fréquent en programmation et c'est critique en C++ parce qu'il est très facile de tomber dans ces travers avec ce langage. Je dirais même qu'architecturer proprement un programme dès le départ avec un outil aussi puissant demande tant de savoir-faire que c'est devenu un métier en soi.

    J'ai tout de même 2 questions : Pour un template<typename A=int, typename B=int, typename C=int>, comment fait-on pour mettre le type B par défaut tout en spécifiant un type A et C?
    On ne peut pas. Lorsque tu spécifies un type par défaut, tous les arguments suivants, s'ils existent, doivent également disposer du leur. Présenté autrement : tous les paramètres disposant d'un argument par défaut doivent être tassés sur la droite.

    Ce principe est aussi valable pour les arguments de toutes les fonctions, qu'elles soient template ou pas.

    De la même façon, lorsque tu invoques ta fonction, si tu spécifes un argument, tous les autres avant lui doivent l'être aussi, et si tu l'ignores, tous les autres après lui doivent l'être aussi.

    C'est nécessaire car tu ne peux pas laisser un trou dans une liste d'argument : « a,b,,,e,f ». C'était possible en BASIC, pas en C ou C++, ne serait-ce que parce que ces arguments sont censés aller dans la pile et pouvoir être récupérés à emplacement fixe. Sinon, il faudrait utiliser des méta-données à côté pour savoir si un argument a été effectivement transmis ou pas.

    Autrement, si tu as plusieurs arguments « par défaut » et que ne spécifie que certains d'entre eux, le compilateur ne peut pas savoir quels arguments ont été spécifiés et quels arguments ont été laissés en blanc dans ta liste.

    Dans le cas d'argument vide, quel type X utiliser?
    La réponse est ci-dessus : il n'y a pas d'argument vide.
    Pour les arguments non spécifiés, tu as le droit de spécifier un type void par défaut, pour peu que cela ait du sens syntaxiquement : ton type template va être substitué à ton code comme si tu l'avais toi-même tapé en toutes lettres à cet endroit, puis compilé.

    Car il faudra que je spécialise une classe pour dire que l'argument est vide quand le type sera X.

    Mais il ne faudrait pas que l'utilisateur, en utilisant le type X appelle sa fonction sans argument alors qu'elle en nécessite.
    Si tu spécialises une classe et que tu redéfinis une fonction existante mais sans argument, tu changes de signature et ce sera considéré comme une surcharge : la fonction avec argument sera disponible dans toutes les versions de ta classe et la version sans argument sera utilisable, en plus, dans ta spécialité X.

    Mais je maintiens que ce n'est probablement pas la bonne approche à ton problème.

    EDIT : je suis en train de me demander si les typedef int Mon_type sont considéré comme des types à part entière par les templates (je testerais ce soir)
    Oui. Un typedef est un alias de nom de type. Tu as le droit de l'utiliser si le type original utilisé dans ton typedef passerait lui-aussi.

  7. #7
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Voici ce que je pense faire :

    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
     
    typedef char Sans_donnees;
    class Rien
    {
           public :
               Nothing();
               ~Nothing();
               void Rien(){}
    }
     
    template<typename CLASSE1 = Rien *, typename FONCTION1 = void (Rien::*)(void), typename DONNEE1 = Sans_donnees>
    class Bouton
    {
     
    };
    //si la fonction n'admet pas d'arguments fonction(void)
    template<>
    <CLASSE1, FONCTION1, Sans_donnees>
    class Bouton
    {
             Rien rien
             public :
                    Bouton(CLASSE1 = rien, FONCTION1 = &(rien::rien));//je suis pas sûr de la syntaxe)
    };
    Appels :
    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
     
    class P
    {
         public :
                P();
     
    };
    P::P()
    {
    Bouton <P *, void (P::*)(void)>bouton(this,&ma_méthode);
    //appel d'une fonction sans argument
    Bouton <P *, void (P::*)(int), int>bouton(this,&ma_méthode, data);
    //appel d'une fonction avec un argument data
    Bouton <>bouton();
    //on ne veut pas déclencher d'action quand on clique.
    }
    Le but est de créer un bouton qui déclenche une action (= lance une méthode) lorsqu'on clique dessus (et de lancer une autre action lorsqu'on passe le curseur dessus, mais je ne l'ai pas mis pour simplifier).

    Déjà, avec les absences d'arguments, il faut redéfinir 3 fois la classe (pas d'argument pour la fonction1, pas d'argument pour la fonction2, pas d'argument pour les deux)
    Si je décide en plus d'utiliser un autre typedef pour l'absence de fonction, il faudra que je redéfinisse la classe 8 fois (les 3 ci-dessus plus :
    pas de fonction 1, pas de fonction1 et pas d'argument2, pas de fonction2, pas de fonction2 et pas d'argument1, pas des deux fonctions)

    EDIT : en fait, je pense avoir trouvé une solution plus simple pour définir l'absence de fonction : une simple surcharge de la fonction qui pose les fonctions ainsi qu'un booléen pour dire si une fonction est activée ou non.

  8. #8
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 476
    Par défaut
    Bonjour,

    Quand tu écris cela :

    Citation Envoyé par Neckara Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    P::P()
    {
    Bouton <P *, void (P::*)(void)>            bouton  (this,&ma_méthode);
    Bouton <P *, void (P::*)(int), int>        bouton  (this,&ma_méthode, data);
    Bouton <>                                  bouton  ();
    }
    … tu ne déclares pas des fonctions-membres mais trois instances de différentes spécialités de la classe Bouton, et toutes nommées « bouton » ce qui conduira à une erreur de compilation.

    Ensuite, ce ne sont pas les templates qu'il faudrait utiliser mais l'héritage avec fonctions membres virtuelles. Tu crées une classe « Bouton » avec tout un tas de fonctions membres virtuelles formant une interface qui sera reconnue par ton framework.

    Ensuite, tu étends cette classe « Bouton » en une classe « BoutonJouer » ou bouton « BoutonQuitter » et, plutôt que de définir une fonction de callback quelque part et de la faire appeler, tu redéfinis la fonction-membre « Cliquer() » et tu mets le code de ta callback dedans.

  9. #9
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Bonjour,

    Quand tu écris cela :
    … tu ne déclares pas des fonctions-membres mais trois instances de différentes spécialités de la classe Bouton, et toutes nommées « bouton » ce qui conduira à une erreur de compilation.
    Je sais, c'était pour présenter les différentes façon de créer un bouton.

    Je problème c'est que si j'utilise un héritage, il faudra que je refasse une classe à chaque bouton, là 2 bouton ça vas, mais quand il y en aura plus de 200... ça ne sera pas viable...

    L'objectif est de créer un bouton générique qui pourra être utilisé à chaque fois.

    Mais peut être qu'il vaudrait mieux que je créé une classe par type de bouton :
    bouton sans effet
    bouton avec fonction lorsqu'on clique
    bouton avec fonction lorsqu'on le survole
    bouton avec fonction lorsqu'on clique et lorsqu'on le survole.
    ...

    Je pense que ça sera beaucoup plus facile.

  10. #10
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 476
    Par défaut
    Je suis assez d'accord mais dans ce cas, tes fonctions template ne te seront d'aucune utilité : le code qui appelle tes différentes callbacks doit être compatible avec leur signature, sinon ça n'a pas intérêt.

    Même choses pour les caractéristiques de ton bouton : tu as souligné toi-même que chaque possibilité (clic, survol, etc.) pouvait ou non être pris en charge par ton bouton. Si, à cela, tu ajoutes l'ensemble des signatures de fonctions de ton programme, tu devras le produit de cet ensemble avec l'ensemble des parties des caractéristiques de ton bouton. Soit des milliers de combinaisons.

    À noter que beaucoup de frameworks ont rencontré le même problème. En général, soit ils définissent un type « fonction callback » commun à tout le framework, soit ils contournent le problème en définissant un paramètre « void * » à transtyper en fonction du contexte.

    Par contre, tu peux faire en sorte que le prototype d'une fonction callback utilisable par un bouton contienne justement un argument « Bouton & ». De cette façon, le bouton transmet directement à la fonction une référence vers lui-même à la fonction concernée qui, elle, peut ensuite remonter le fil pour retrouver les informations qui l'intéressent.

  11. #11
    Membre émérite
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Par défaut
    Bonsoir,

    A mon humble avis, ta première erreur consiste à faire un "bouton" à tout faire plutôt que de faire un "bouton" pour matérialiser quelques chose de cliquable (qui appelle une fonction sans paramètre), tout comme une zone d'édition qui appelle une fonction prenant en paramètre la nouvelle valeur (d'où tes problèmes avec les différentes signatures). La seconde erreur (induite par la première) consiste à passer en paramètre template la signature, alors que la classe template est incapable de faire quoi que ce soit de générique avec ce paramètre template.

    Sans jugement, pour ne pas partir dans des solutions "farfelues", c'est jamais un mal de se basé sur ce qui existe en terme d'architecture. Le mécanisme le plus proche de ce que tu essais de faire est un mécanisme de callback (on définit une fonction onclick, une fonction onchange, etc.). D'une manière un peu plus évoluée, on trouve le mécanisme de signaux et slots de Qt (plusieurs fonctions de callback en gros)


    Techniquement, tu peux peut-être partir sur du boost::bind/boost::function pour éviter de manipuler des infâmes pointeurs de fonctions.

    A cette adresse, tu trouveras un exemple trivial pour un bouton (onclick):
    (http://www.boost.org/doc/libs/1_49_0...boost_function).

    Je te l'adapte ci-après pour une zone d'édition avec un événement onChange passant un paramètre (j'ai un peu compliqué pour te montrer que certains éléments pouvait demeuré templaté).

    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
     
    #include <iostream>
    #include <string>
     
    #include <boost/bind.hpp>
    #include <boost/function.hpp>
     
    template < typename T >
    class editbox {
    public:
    	editbox( T const& value ):
    		_value(value)
    	{
     
    	}
    	void setValue( T const& value ){
    		_value = value ;
    		//on ne fait rien si aucune fonction est définie (sinon, erreur d'exécution)
    		if ( onChange != 0 ){
    			//on appelle la fonction sinon
    			onChange(value);
    		}
    	}
    	T const& value() const { return _value ; }
     
        boost::function<void(T const&)> onChange;
    private:
        T _value ;
    };
     
    template < typename T >
    void printNewValue( T const& value ){
    	std::cout << value << std::endl ;
    }
     
    int main( int argc, char* argv[] ){
    	{
    		editbox< int > box(0);
    		box.onChange = boost::bind( &printNewValue< int >, _1 );
    		box.setValue(10);
    		box.setValue(15);
    		box.setValue(20);
    	}
    	{
    		editbox< std::string > box("null");
    		box.onChange = boost::bind( &printNewValue< std::string >, _1 );
    		box.setValue("titi");
    		box.setValue("toto");
    		box.setValue("tutu");
    	}
     
    	return 0;
    }

    En espérant que ça t'inspire...

  12. #12
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Bonjour, merci pour vos réponses.

    J'aurais quelques dernières petites questions :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    playButton.onClick = boost::bind(&player::play, &thePlayer);
    Comment fait-il? Il utilise des void * ?
    Je ne comprend pas vraiment comment fonctionne ceci.

    Sinon est-il possible de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template<T>
    void ma_fonction(T * ma_classe, void(T::*)(Bouton *, int etat) ma_fonction);
    ?

    Pour un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    template<typename T>
    class Bouton
    {
    };
    Un pointeur sur bouton peut-il s'écrire Bouton * ou faut-il à chaque fois préciser le template?

    Je pense ne faire qu'un appel de fonction qui donnera à chaque fois en argument un pointeur sur bouton ainsi que l'état (cliqué, dessus, onchange, ...)

  13. #13
    Membre émérite
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    playButton.onClick = boost::bind(&player::play, &thePlayer);
    Comment fait-il? Il utilise des void * ?
    Je ne comprend pas vraiment comment fonctionne ceci.
    Je vois le genre d'utilisateur indiscret qui veut tout savoir sur le fonctionnement . Même réponse qu'aux autres "Comment fait-il?" : "Ca ne te regarde pas, tu ne maintiens pas cette bibliothèque, tu dois juste comprendre l'interface et son comportement. Tu te demandes comment fonctionne une std::map en interne?"

    Après, si tu es passionné, tu peux jeter un oeil ici pour un début d'explication si ça te chante, mais tu n'en as pas besoin (http://stackoverflow.com/questions/5...boostbind-work).


    Citation Envoyé par Neckara Voir le message
    Sinon est-il possible de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template<T>
    void ma_fonction(T * ma_classe, void(T::*)(Bouton *, int etat) ma_fonction);
    ?
    Aucune idée et aucune envie de me replonger dans la syntaxe pour faire ces horreurs . Si tu veux continuer d'essuyer des plâtres avec tes pointeurs de fonction, libre à toi, mais je veux pas être mêlé à ça

    PS :
    - Si je ne dis pas de connerie, la nouvelle norme reprend boost::function et boost::bind (http://www2.research.att.com/~bs/C++...l#std-function).
    - A mon avis, la seule bonne raison de faire des pointeurs de fonction nu qui demeure à haut niveau est de faire une interface en C et là on ne met pas de template mais un void* pour promener des données arbitraires.

  14. #14
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Actuellement, je suis surtout autodidacte et il est évident que je ne sais pas tout sur le C++.
    J'essaye donc de faire au maximum avec ce que je sais faire et j’apprends lorsque je ne peux pas trouver de solution (ex : utilisation de la SFML pour l'interface graphique).
    Je sais que Boost est très pratique, mais actuellement, je n'ai pas envie de l'utiliser (c'est un choix et ce n'est pas le sujet ici).

    Si je demande comment cette fonction marche, c'est que je ne comprend pas comment le prototype peut être valide :
    En apparence, on utilise deux templates pour les arguments.
    Mais je ne vois pas d'initialisation du type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    playButton.onClick = boost::bind<Type1,Type2>(&player::play, &thePlayer);
    L'autre possibilité serait d'avoir une valeur par défaut pour Type2 et Type1 mais il est très improbable qu'un template soit initialisé avec les types de &player::play et &thePlayer.

    Ceci veut donc dire que soit je n'ai pas tout à fait compris l'utilisation des templates, soit il existe un concept en C++ que je ne connais pas.
    Il est donc très intéressant que je connaisse ces concepts afin d'avoir plus de possibilités lorsque je coderais et ainsi je pourrais simplifier certains algo avec ce que j'ai vu tout comme les template m'ouvrent d'autres possibilités jusqu'alors inaccessibles.

    Ensuite ce ne sont pas des pointeur de fonction nu mais des pointeurs de méthode.

    J'ai commencé à utiliser des void * puis on ma dit d'utiliser plutôt des template et vous me dîtes d'utiliser des void * pour des pointeurs de fonctions.

    Alors je fais quoi? Que dois-je faire? Que choisir? Pourquoi l'un et pas l'autre? Qui croire?

    Si vous me dîtes de ne pas faire cela mais de plutôt faire ceci, qu'aurais-je appris?
    Donnez-moi des raisons claires et approfondies que je puisse me faire ma propre opinion.


    Le but n'est pas tellement d'avoir un programme propre et bien écris, mais plutôt d'avoir appris.
    Après, ce n'est qu'une question d'expérience. On ne peut pas exiger de moi de coder aussi proprement qu'un professionnel.

    Donc si vous parlez d'horreur et d'essuyer des plâtres, expliquez-moi pourquoi c'est si horrible pour que je puisse comprendre les raisons pour lesquelles on ne fait pas comme cela.


    NB : j'ai trouvé une solution satisfaisante :
    Pour définir un bouton je n'aurais besoin qu'un template pour un type de classe.
    Je stockerais un pointeur de fonction du type void (T: (Bouton *)
    Je proposerais des get/setter pour modifier la fonction mise (ou pour ne plus mettre de fonctions)
    Ainsi que quelques booléens pour savoir quand appeler cette fonction (on click, onblur, ...)
    Le pointeur sur le bouton permettra à la fonction de savoir :
    - Pourquoi elle a été appelée
    - Quel est la position de l'évènement

    Si la fonction a besoin d'autre chose, c'est à la classe qui la contient de lui donner ce dont elle a besoin grâce à des attributs.

  15. #15
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    en fait il te faut un pointeur sur fonction membre.
    En général le prototype, est comme ceci, en template:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template<class T>
    class CallbackMember
    {
      typedef void(T::*ptrFunc)(); // une fonction membre de T sans argument qui ne retourne rien
      public:
        void subscribe(ptrFunc* fct, T* object);
    };
    Tu peux aussi avoir une méthode template au lieu de la classe, qui aura cette signature
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template<class T>
    void subscribe(void(T::*ptrFunc)(), T* object);
    Pour rester dans le principe de la classe callback, il faut que tu enregistres la fonction (membre) à utiliser, mais aussi l'objet, l'instance, qui l'utilise.
    Ensuite pour l'utiliser, l'écriture est (en gros, je ne suis pas sur)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    my_object->(*my_function)(args);
    Je pourrai te fournir plus d'exemples fonctionnels quand je serai chez moi ce soir .
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  16. #16
    Membre émérite
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Je sais que Boost est très pratique, mais actuellement, je n'ai pas envie de l'utiliser (c'est un choix et ce n'est pas le sujet ici).
    C'est ton choix, tu es libre. Perso, je m'autorise l'usage que de certains modules et je n'hésite pas que je les vois entrer dans la norme...

    Citation Envoyé par Neckara Voir le message
    Si je demande comment cette fonction marche, c'est que je ne comprend pas comment le prototype peut être valide :
    En apparence, on utilise deux templates pour les arguments.
    Mais je ne vois pas d'initialisation du type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    playButton.onClick = boost::bind<Type1,Type2>(&player::play, &thePlayer);
    Désolé, mais je n'avais pas compris ce qui te bloquait. Tu n'as pas besoin d'expliciter les types d'une fonction template s'ils peuvent être déduit par l'appel (je crois que ça s'appelle l'instanciation implicite). C'est pas la même chose que les paramètres template avec des valeurs par défaut.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    template < typename T >
    T squared_norm( T const& x, T const& y ){
    	return a + b ;
    }
     
    int main( int argc, char* argv[] ){
    	double d = squared_norm(2.0,3.0);
    	return 0 ;
    }
    Tu retrouves la même chose avec std::min, std::max etc... Après, pour les profondeurs de boost::bind, tu te douteras que les explications sur le fonctionnement ne sont pas si simples (Je vois grosso modo deux principes derrière boost::bind et boost::function : Celui de foncteur (objet fonction) et le type-erasure.)

    C'est bien ce qui te gênait?

    Citation Envoyé par Neckara Voir le message
    Donnez-moi des raisons claires et approfondies que je puisse me faire ma propre opinion.

    Des raisons claires (du moins je l'espère) :

    - Tu obliges l'utilisateur à créer une classe pour écouter ton bouton (il ne peut pas faire appel à void exit() par exemple, il doit faire une classe intermédiaire pour appeler la fonction libre "exit" en ignorant le paramètre "bouton" qui lui sert à rien). Avec ce que je te proposais, il avait le choix entre appeler une méthode d'une classe, une fonction libre, une fonction statique, etc.

    - Les pointeurs de fonction étaient le seul moyen en C (à ce que je sache) pour permettre à une bibliothèque d'appeler une fonction définie par l'utilisateur. On retrouve ces pointeurs de fonctions en OpenGL, dans pthread, dans curl, dans les pilotes SQL,... En C++, on n'en a plus besoin car des outils de plus haut niveau sont disponibles.

    - La syntaxe n'est pas triviale, ton utilisateur doit décortiquer la déclaration du pointeur de fonction pour savoir comment utiliser ton bouton. Bousk à l'air bien parti pour te montrer comment encapsuler ça.

    ++

  17. #17
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Merci pour vos réponses.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    - Tu obliges l'utilisateur à créer une classe pour écouter ton bouton (il ne peut pas faire appel à void exit() par exemple, il doit faire une classe intermédiaire pour appeler la fonction libre "exit" en ignorant le paramètre "bouton" qui lui sert à rien). Avec ce que je te proposais, il avait le choix entre appeler une méthode d'une classe, une fonction libre, une fonction statique, etc.
    En fait, sur le projet que je mène, nous avons un gestionnaire de fenêtres/évènements.

    Il y a une boucle de lecture des évènements claviers/souris.
    A chaque évènements, on recherche la fenêtre qui dois recevoir cet évènement.
    On envoie donc l'évènement à cette fenêtre.
    Une fenêtre contient divers objets... dont des boutons.
    Dans la fenêtre, on va donc déclarer un ensemble de bouton (liste/file/pile/autre?) et à chaque évènement souris, on va envoyer cet évènement à tous les boutons concernés (plusieurs possibilités).
    La fonction appelée par un bouton sera donc soit une méthode de la classe fenêtre, soit une méthode d'une de ses sous-classes.

  18. #18
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    Voilà un code de callback que j'ai écrit il y a quelques mois (n'hésitez pas à indiquer les erreurs/améliorations que vous voyez dessus - je ne suis pas repassé dessus depuis, trop heureux qu'il fonctionne^^)

    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
    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
    class CCallback
    	{
    		public:
    			CCallback() {}
    			CCallback(const CCallback&){}
    			virtual ~CCallback() {}
    			virtual void Call()=0;
    	};
    	class CStaticCallback : public CCallback
    	{
    		typedef void(*ptrfct)();
    		public:
    			CStaticCallback(uint32 _uiEventId, ptrfct _pCallback)
    				: m_uiEventId(_uiEventId)
    				, m_pFct(_pCallback)
    			{
     
    			}
    			CStaticCallback(const CStaticCallback& _kCallback)
    				: m_uiEventId(_kCallback.m_uiEventId)
    				, m_pFct(_kCallback.m_pFct)
    			{
     
    			}
    			virtual ~CStaticCallback() {}
     
    			virtual void Call()
    			{
    				(*m_pFct)();
    			}
     
    			bool operator==(const CStaticCallback& _kCallback) const
    			{
    				return m_uiEventId == _kCallback.m_uiEventId
    					&& m_pFct == _kCallback.m_pFct;
    			}
    			CStaticCallback& operator=(const CStaticCallback& _kCallback)
    			{
    				m_uiEventId = _kCallback.m_uiEventId;
    				m_pFct = _kCallback.m_pFct;
    				return *this;
    			}
     
    		protected:
    			uint32	m_uiEventId;
    			ptrfct	m_pFct;
    	};
    	template< class T >
    	class CMemberCallback : public CCallback
    	{
    		typedef void(T::*ptrfct)();
    		public:
    			CMemberCallback(uint32 _uiEventId, T* _pObj, ptrfct _pCallback)
    				: m_uiEventId(_uiEventId)
    				, m_pObj(_pObj)
    				, m_pFct(_pCallback)
    			{
     
    			}
    			virtual ~CEventCallback() {}
     
    			virtual void Call()
    			{
    				(m_pObj->*m_pFct)();
    			}
     
    			template< class O >
    			bool operator==(const CMemberCallback<O>& _kCallback) const
    			{
    				return _kCallback.m_uiEventId == m_uiEventId
    					&& _kCallback.m_pObj == m_pObj
    					&& _kCallback.m_pFct == m_pFct;
    			}
     
    		protected:
    			uint32	m_uiEventId;
    			T*		m_pObj;
    			ptrfct	m_pFct;
    	};
    	class CEvent
    	{
    		public:
    			CEvent(uint32 _uiEventId);
    			virtual ~CEvent();
     
    			uint32	GetId	() const { return m_uiEventId; }
     
    			void	Suscribe	(void(*ptrfct)())
    			{
    				Suscribe(new BOUSDK_HEAP CStaticCallback(m_uiEventId, ptrfct));
    			}
    			template< class T >
    			void	Suscribe	(T* _pObj, void(T::*ptrfct)())
    			{
    				Suscribe(new BOUSDK_HEAP CMemberCallback<T>(m_uiEventId, _pObj, ptrfct));
    			}
    			void	Suscribe	(CCallback* _pCallback);
     
    			void	Unsuscribe	(CCallback* _pCallback);
     
    			void	Notify	();
     
    		protected:
    			uint32					m_uiEventId;
    			std::list<CCallback*>	m_listCallback;
    	};
    De cette manière, je peux créer un Event avec une callback sur une fonction globale/static ou membre.
    CCallback est l'interface d'une callback, ensuite je dérive 2 class pour les callback globales/static, et les callback membres. Tu devrais remarquer que la syntaxe est très proche de cette que l'on te donne plus haut.
    Après les callback, il y a CEvent, qui possède une callback à appeler.
    Le polymorphisme s'occupe du reste.

    Dans mon cas, les callback retournent un void et ne prennent pas de paramètre, mais ceci peut être changé.

    Dans mon projet, il est lié à un event manager, parce que le but était de broadcaster un évènement.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class CEventManager
    	{
                    public:
    			CEventManager() {}
    			virtual ~CEventManager();
     
    		public:
    			CEvent*	GetEvent	(uint32 _uiEventId);
     
    		protected:
    			std::map<uint32, CEvent*>	m_mapEvents;
    	};
    Quant à l'utilisation, voilà ce que ça donne:

    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
    #include <iostream>
    void Test()
    {
    	std::cout<<"omg it works !"<<std::endl;
    }
     
    struct STest {
    	void Test() {
    		std::cout<<"omg it works with member ! "<<std::endl;
    	}
    };
    	STest kTest;
    	CEventManager::Get().GetEvent(1)->Suscribe(Test);
    	CEventManager::Get().GetEvent(1)->Suscribe<STest>(&kTest, &STest::Test);
    	CEventManager::Get().GetEvent(1)->Notify();
    Tu devrais parvenir aisément à l'adapter à ce qui te convient. Dans ton cas, je pense que seul les class XXCallback t'intéresseront, et éventuellement la classe Event, mais à priori tu en as déjà une ou assimilée, donc tu pourras la modifier pour accepter les Callback.

    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
    	///////////////////////////////////////////////////////////////////////////
    	//	CEvent
    	///////////////////////////////////////////////////////////////////////////
    	CEvent::CEvent(uint32 _uiEventId)
    		: m_uiEventId(_uiEventId)
    	{
     
    	}
    	CEvent::~CEvent()
    	{
    		std::list<CCallback*>::iterator itCallback = m_listCallback.begin();
    		while (itCallback != m_listCallback.end())
    		{
    			CCallback* pCallback = *itCallback;
    			SafeDelete(pCallback);
    			itCallback = m_listCallback.erase(itCallback);
    		}
    	}
    	void CEvent::Suscribe(CCallback* _pCallback)
    	{
    		m_listCallback.push_back(_pCallback);
    	}
    	void CEvent::Unsuscribe(CCallback* _pCallback)
    	{
    		m_listCallback.remove(_pCallback);
    		SafeDelete(_pCallback);
    	}
    	void CEvent::Notify()
    	{
    		std::list<CCallback*>::iterator itCallback = m_listCallback.begin();
    		while (itCallback != m_listCallback.end())
    		{
    			CCallback* pCallback = *itCallback;
    			pCallback->Call();
    			++itCallback;
    		}
    	}
    	///////////////////////////////////////////////////////////////////////////
    	//	CEventManager
    	///////////////////////////////////////////////////////////////////////////
    	CEventManager::~CEventManager()
    	{
    		std::map<uint32, CEvent*>::iterator itEvt = m_mapEvents.begin();
    		while (itEvt != m_mapEvents.end())
    		{
    			CEvent* pEvt = itEvt->second;
    			++itEvt;
    			SafeDelete(pEvt);
    		}
    	}
    	CEvent* CEventManager::GetEvent(uint32 _uiEventId)
    	{
    		std::map<uint32, CEvent*>::iterator itEvt = m_mapEvents.find(_uiEventId);
    		if (itEvt != m_mapEvents.end())
    			return itEvt->second;
     
    		CEvent* pEvent  = new BOUSDK_HEAP CEvent(_uiEventId);
    		m_mapEvents[_uiEventId] = pEvent;
    		return pEvent;
    	}

    Désolé pour le pavé de code, j'avais la flegme de le découper pour extraire les parties suffisantes.

    N'hésite pas à revenir ici si tu as des questions.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

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

Discussions similaires

  1. Réponses: 19
    Dernier message: 23/12/2009, 19h22
  2. Réponses: 3
    Dernier message: 09/04/2009, 11h30
  3. Template / Classe Latex
    Par zifox dans le forum Débuter
    Réponses: 3
    Dernier message: 26/03/2009, 18h04
  4. template<class> et template<typename>
    Par mister3957 dans le forum C++
    Réponses: 10
    Dernier message: 01/11/2007, 09h32
  5. Héritage classe template->classe template
    Par zabibof dans le forum Langage
    Réponses: 5
    Dernier message: 11/08/2007, 11h05

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