Méthode d'une classe virtuelle pure appelant une méthode virtuelle pure => Warning.

Version imprimable

Je ne comprend pas exactement ce que tu veux faire.


Citation:

Envoyé par Kalith Voir le message
Pour savoir s'il vaut mieux s'orienter vers ce design là, il faudrait connaitre le contexte dans lequel tu cherches à faire tout ça, c'est à dire quelles sont les différences possibles entre tes classes dérivées.

J'ai deux classes listener, l'une pour écouter le port admin, l'autre pour écouter celui du port client.
Ces deux classes sont très similaires à quelques petites exceptions :
- on a une zone critique pour le port client et on en a pas pour le port admin.
- dans les deux cas, on va logger des messages différents ou ne pas logger.

Ceci permettra aussi aux personnes voulant reprendre les sources d'adapter plus facilement les messages de logs.



Citation:

Envoyé par ÉJLS. Voir le message
Juste pour information (je n'arrive pas à déterminer avec vos messages si vous le savez ou non), il est possible de définir une fonction virtuelle pure (ce qui explique le warning).

Oui en mettant = 0 pour lui dire qu'il n'existe pas d'implémentation pour cette méthode et que la classe fille, si elle ne veut pas être virtuelle pure devra implémenter cette méthode.



Citation:

Envoyé par ÉJLS. Voir le message
Si je comprends bien, vous utilisez des class templates et de l'héritage pour essayer d'inliner l'appel à VirtualListener::logStartServeur (et overrides) dans VirtualListener::maFonction ?

Non, VirtualListener n'aura que des méthodes virtuelles pures. On essaye d'inliner l'appel de T::logStartServeur() dans ListenerFille<T>::maMethode()


Citation:

Envoyé par ÉJLS. Voir le message
Déjà, une fonction virtuelle et inline, j'ai le souvenir d'une implémentation que l'on pourrait appeler ainsi, c'était dans cfront (pas tout jeune donc) : au lieu de mettre un pointeur sur la fonction dans la vtable, le code de la fonction y été directement mis.

J'imagine que ce n'est pas une bonne idée ?

Citation:

Envoyé par ÉJLS. Voir le message
Hormis cela, ce que tu cherches à faire est tout simplement impossible… comment le compilateur pourrait il inliner un code qu'il ne connait pas encore ?

Si lors de la ligne d'appel le compilateur sait de façon sûre quelle méthode est appelée alors il inline.
Comme je vais avoir T a; a.logStartServeur(); Le compilateur va écrire la classe template pour tous les types possibles donc il va inliner non?


Citation:

Envoyé par ÉJLS. Voir le message
Si tu veux faire ça, tu peux tout simplement utiliser un CRTP, mais c'est tordu et ça ne t'avancera à rien.

Je vais me renseigner à ce sujet.

EDIT : en gros je ferais :
Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename T>
class VirtualListener
{
      public :
        void methode(void)
        {
               //debut
               T::monInline();
               //fin
        }
}
 
class ListenerClient : VirtualListener<T>
{
        private :
              void monInline(void){}
}

Et dans ce cas là, je pourrais aussi me servir de VirtualListener pour donner les prototypes à définir dans les classes filles avec des virtuelles pures ?
Mais pourquoi est-ce que ceci ne m'avancerait à rien ?


Citation:

Envoyé par ÉJLS. Voir le message
mais l'idée de Kalith d'utiliser des policies est peut être ce que tu cherchais ? Par contre, c'est une solution compile-time seulement…

compile-time ? ie ?
EDIT : oui c'est le but recherché.



Citation:

Envoyé par ÉJLS. Voir le message
Je ne sais pas si tu travailles pour de l'embarqué où quelque chose comme cela, mais non, un appel de fonction ne sera pas si lourd que ça, surtout que là, tu as l'air de faire de l'IO, ce qui sera certainement beaucoup plus lent.

C'est un projet perso.
Dans le cas du listener pour les admins, il va écouter un port et il va à chaque tour de boucle appeler une fonction de traitement pour traiter le paquet reçu.
Mais dans le cas du listener pour les clients, il va lire en boucle et inscrire les paquets reçus dans une file qui sera traitée par un ensemble de thread consommateur. C'est donc une zone qui doit être traitée le plus rapidement possible. Or utiliser des fonctions non inline reviendrait à faire plus de 10 appels/retours de fonctions pour parfois une seule ligne de code.


Bon après ça me permet aussi d'apprendre et d'approfondir mes connaissances.
  • 08/07/2012, 12h06
    Kalith
    Citation:

    Envoyé par ÉJLS. Voir le message
    Juste pour information (je n'arrive pas à déterminer avec vos messages si vous le savez ou non), il est possible de définir une fonction virtuelle pure (ce qui explique le warning).

    J'avais oublié :) Effectivement, le warning prend plus de sens quand on prend ça en considération.

    Citation:

    Envoyé par ÉJLS. Voir le message
    En fait, Test::foo() est inline ici aussi…

    Oui, ce n'est pas forcément très malin de l'avoir écrit comme ça. Je voulais éviter le code à rallonge, et préciser explicitement quelles méthodes on déclarait comme inline (étant sous entendu que, comme dans le problème initial, on ne définit aucune méthode directement dans le code de la classe).

    Citation:

    Envoyé par ÉJLS. Voir le message
    En fait, c'est surtout utile quand une définition par défaut disparait quand on déclare une variante, par exemple si tu veux un constructeur par défaut ET un constructeur à deux paramètres.

    C'est l'une des autres utilités de ce mot clé, mais je le trouve clairement plus utile dans le cas d'un constructeur de copie, où il y a clairement beaucoup de choses à écrire si l'on veut reproduire le comportement par défaut (contrairement au constructeur par défaut et au destructeur, qui ont un corps vide).

    Citation:

    Envoyé par Neckara Voir le message
    Une référence prend un objet pour se construire mais c'est bien l'adresse de l'objet qu'il regarde. Ce code compile bel et bien.
    Après on a pas forcément de fuites de mémoires, on peut toujours faire un delete &referance certes ce n'est pas propres mais ça fonctionne parfaitement :aie:

    Eh ben ça ne compile pas chez moi :
    Code:

    int& r = int(10);
    Citation:

    error : invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
    Et pour le second, oui tu peux faire ça mais... :calim2:


    Citation:

    Envoyé par Neckara Voir le message
    Je ne comprend pas exactement ce que tu veux faire.

    Oui je n'ai pas beaucoup développé puisque je pense qu'avec les politiques le code est beaucoup plus simple. Mais si tu veux, dans ton code j'aurai ajouté :
    Code:

    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
    class Listener
    {
    public :
            virtual void action() = 0;
    }
     
    template<typename T>
    class newListener<T> : Listener
    {
    public :
             void action();
    }
     
    template<typename T>
    void newListener<T>::action()
    {
             //debut
             T::methodInline();
             //fin
    }
     
    class BaseInline
    {
            virtual void methodInline() = 0;
    }
     
    class X : BaseInline
    {
           inline void methodInline();
    }
     
    inline void X::methodInline()
    {
    }
     
    // Ajouter un typedef pour clarifier les choses
    typedef newListener<X> XListener;
     
    // Utilisation (l'utilisateur ne voit pas l'étape intermédiaire avec newListener) :
    Listener * a = new XListener();
    a->action();

    Citation:

    Envoyé par Neckara Voir le message
    J'ai deux classes listener, l'une pour écouter le port admin, l'autre pour écouter celui du port client.
    Ces deux classes sont très similaires à quelques petites exceptions :
    - on a une zone critique pour le port client et on en a pas pour le port admin.
    - dans les deux cas, on va logger des messages différents ou ne pas logger.

    D'accord, donc ça peut effectivement être intéressant d'utiliser les politiques :
    Code:

    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
    class Listener
    {
    public :
     
        virtual void action() = 0;
    };
     
    template<typename LogPolicy, typename CriticalSectionPolicy>
    class ListenerPolicies : public Listener
    {
        CriticalSectionPolicy section;
        LogPolicy             log;
     
    public :
     
        void action() {
            section.begin();
            // début
            log.print();
            // fin
            section.end();
        }
    };
     
    struct LogClientMessage
    {
        void print() {
            // ...
        }
    };
     
    struct LogAdminMessage
    {
        void print() {
            // ...
        }
    };
     
    class CriticalSectionMutex
    {
        std::mutex m;
     
    public :
     
        void begin() {
            m.lock();
        }
     
        void end() {
            m.unlock();
        }
    }
     
    struct CriticalSectionNone
    {
        void begin() {}
        void end() {}
    }
     
    // Raccourcis d'écriture :
    typedef ListenerPolicies<LogClientMessage, CriticalSectionMutex> ClientListener;
    typedef ListenerPolicies<LogAdminMessage,  CriticalSectionNone>  AdminListener;
     
    // utilisation :
    Listener* p = new ClientListener();
    p->action();

    Tout l'intérêt de ce design c'est que tu n'auras pratiquement rien à ré-écrire le jour où tu auras besoin d'un troisième listener qui aurait le même output que le listener client mais sans section critique, ou l'output admin avec une section critique, etc.
  • 08/07/2012, 12h58
    Neckara
    Citation:

    Envoyé par Kalith Voir le message
    Eh ben ça ne compile pas chez moi :
    Code:

    int& r = int(10);

    Je croyais que tu parlais de la méthode avec le new. Donc j'avais raison de passer par une méthode statique.

    Citation:

    Envoyé par Kalith Voir le message
    Et pour le second, oui tu peux faire ça mais... :calim2:

    Bon c'était plus de la fainéantise qu'autre chose, autant utiliser un pointeur plutôt qu'une référence.


    Citation:

    Envoyé par Kalith Voir le message
    Oui je n'ai pas beaucoup développé puisque je pense qu'avec les politiques le code est beaucoup plus simple. Mais si tu veux, dans ton code j'aurai ajouté :
    Code:

    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
    class Listener
    {
    public :
            virtual void action() = 0;
    }
     
    template<typename T>
    class newListener<T> : Listener
    {
    public :
             void action();
    }
     
    template<typename T>
    void newListener<T>::action()
    {
             //debut
             T::methodInline();
             //fin
    }
     
    class BaseInline
    {
            virtual void methodInline() = 0;
    }
     
    class X : BaseInline
    {
           inline void methodInline();
    }
     
    inline void X::methodInline()
    {
    }
     
    // Ajouter un typedef pour clarifier les choses
    typedef newListener<X> XListener;
     
    // Utilisation (l'utilisateur ne voit pas l'étape intermédiaire avec newListener) :
    Listener * a = new XListener();
    a->action();


    D'accord je comprend mieux.


    Citation:

    Envoyé par Kalith Voir le message
    D'accord, donc ça peut effectivement être intéressant d'utiliser les politiques :
    Code:

    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
    class Listener
    {
    public :
     
        virtual void action() = 0;
    };
     
    template<typename LogPolicy, typename CriticalSectionPolicy>
    class ListenerPolicies : public Listener
    {
        CriticalSectionPolicy section;
        LogPolicy             log;
     
    public :
     
        void action() {
            section.begin();
            // début
            log.print();
            // fin
            section.end();
        }
    };
     
    struct LogClientMessage
    {
        void print() {
            // ...
        }
    };
     
    struct LogAdminMessage
    {
        void print() {
            // ...
        }
    };
     
    class CriticalSectionMutex
    {
        std::mutex m;
     
    public :
     
        void begin() {
            m.lock();
        }
     
        void end() {
            m.unlock();
        }
    }
     
    struct CriticalSectionNone
    {
        void begin() {}
        void end() {}
    }
     
    // Raccourcis d'écriture :
    typedef ListenerPolicies<LogClientMessage, CriticalSectionMutex> ClientListener;
    typedef ListenerPolicies<LogAdminMessage,  CriticalSectionNone>  AdminListener;
     
    // utilisation :
    Listener* p = new ClientListener();
    p->action();

    Tout l'intérêt de ce design c'est que tu n'auras pratiquement rien à ré-écrire le jour où tu auras besoin d'un troisième listener qui aurait le même output que le listener client mais sans section critique, ou l'output admin avec une section critique, etc.

    Très intéressant, merci beaucoup.
    Je pense que c'est la solution que je vais retenir.

    Les designs pattern sont très utiles et j'aimerais bien apprendre à utiliser d'autres designs pattern mais lorsque j'avais fait des recherches j'étais tombé sur des résultats indigestes.

    Comme avec une description du design pattern, un exemple d'implémentation mais aucun mot sur l'utilité/avantage/utilisation du design pattern :aie:
    On se retrouvait à la fin du tutoriel avec un code contenant 5-6 design pattern sans même comprendre pourquoi on les avaient utilisés...

    Est-ce que vous auriez des liens intéressants à ce sujet ?
  • 08/07/2012, 23h49
    gl
    Citation:

    Envoyé par Neckara Voir le message
    Oui en mettant = 0 pour lui dire qu'il n'existe pas d'implémentation pour cette méthode et que la classe fille, si elle ne veut pas être virtuelle pure devra implémenter cette méthode.

    Je pense que tu n'as pas compris la remarque de ÉJLS., il signale justement qu'une méthode virtuelle pure peut avoir une implémentation.

    Citation:

    Envoyé par Neckara Voir le message
    Non, VirtualListener n'aura que des méthodes virtuelles pures. On essaye d'inliner l'appel de T::logStartServeur() dans ListenerFille<T>::maMethode()

    Mais c'est donc T::logStartServeur() qui doit être inline, pas VirtualListener::logStartServeur().