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 :

Polymorphisme, templates et variadic


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 165
    Points : 62
    Points
    62
    Par défaut Polymorphisme, templates et variadic
    Mesdames, Messieurs,

    Je me permets de venir vers vous avec une petite question... Après un petit moment d'arrêt de la programmation en C++, je m'y remets et je ne vous cache pas que j'ai beaucoup perdu... (mais à ce qu'il paraît c'est comme le vélo ^^)
    Pour une question pratique, je cherche à avoir une classe me permettant de gérer mes envois d'alertes. Je pensais en grossier à une classe du genre
    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
    class                            AlerterManager
    {
        enum TYPE { MAIL, TCHAT, SMS };
     
    private:
        std::vector<IAlerter *>        _alerters;
     
    public:
        AlerterManager()  {}
        template <typename ...Args>
        void addNewAlerter(TYPE alerterType, Args &&...args);
        void sendMessage(const std::string &format, Args &&...args) {
            vector_iteration {
                iterator->sendMessage(format, std::forward<Args>(args)...);
            }
        }
    };
    Mon but ici est de pouvoir instancier un Alerter de chaque type (donc potentiellement avec un nombre de paramètre variable) et de pouvoir envoyer un message à tous mes alerters.

    Afin de rester générique autant que possible et d'essayer de garder un code clair, je voulais utiliser le polymorphisme en commençant par une classe abstraite ressemblant à qqch du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class        iAlerter
    {
    public:
        template <typename ...Args>
        virtual void    sendMessage(const std::string &format, Args &&...args) = 0;
    };
    et du coup, implémenter mes différents alerters dans le genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class        TchatAlerter : public iAlerter
    {
    private:
        SOCKET_TYPE    _sock;
        std::string        _host, _login, _passwd;
     
    public:
        TchatAlerter(const std::string &host, const std::string &login, const std::string &passwd) : _host(host), _login(login), _passwd(passwd) { // INITIATE SOCKET HERE }
        ~TchatAlerter() {}
        virtual void    sendMessage(const std::string &format, Args &&...args) { // SEND MESSAGE HERE }
    };
    Le problème est que je ne peux pas utiliser les variadics dans une classe abstraite et ... je ne vois pas du tout comment contourner mon problème.
    Ça fait plusieurs jours que je tourne en rond et je me permets donc de faire appel à des experts
    Si jamais vous aviez ne serait-ce qu'une petite piste...

    Merci d'avance
    Cordialement,
    Julien BAUMGARTEN

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Ce n'est pas que tu ne peux pas utiliser les variadics dans une classe abstraite, c'est que tu ne peux tout simplement pas déclarer une fonction qui soit à la fois template et virtuelle.

    Par contre, tu peux parfaitement déclarer une fonction virtuelle (voire meme virtuelle pure) dans une classe template

    Et donc pour résoudre ton problème, peut être devrais tu te rappeler de ce que David Wheeler disait :
    Citation Envoyé par David Wheeler
    Tous les problèmes peuvent être résolus par un niveau d'indirection supplémentaire, à part, bien sur, le problème qui consiste à avoir trop de niveau d'indirection
    L'idée, ici, serait sans doute de créer une classe de base iAlerter qui ne soit pas template, et d'en faire hériter une classe template (utilisant les variadic template) pour définir l'ensemble des informations que tu souhaites envoyer.

    Tu définirais alors les valeurs de ces éléments dans le constructeur de cette classe dérivée, et tu pourrait alors implémenter la fonction sendMessage(sans arguments) de manière à ce qu'elle aille pêcher toutes les informations que tu veux envoyer

    Maintenant, cela pourra peut être nécessiter un peu de travail en plus pour générer le message en lui-même. Mais peut être pourrais tu (même si le code est en "ancien C++") t'intéresser aux idées développées par alexandrescu dans sa bibliothèque Loki...

    Tu pourrais peut être y trouver quelques idées sympa
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 165
    Points : 62
    Points
    62
    Par défaut
    Merci Koala01 d'avoir pris du temps pour me répondre.
    Je m'excuse si je me suis mal exprimé, je pensais bel et bien à l'incompatibilité d'une fonction template/virtuelle.

    Je crains de ne pas avoir compris ta proposition parce que mon but est de pouvoir utiliser et réutiliser mon alerter N fois du coup, je ne vois pas vraiment comment passer mes éléments dans le constructeur de mon alerter
    Je pourrais utiliser les va_args mais je trouve ça un peu moche

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Oh, ben, il y a des trucs sympa à faire...

    Un exemple tiré d'un code écrit y a pas très longtemps :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template <typename ... Args>
    class Visitor;
    template <typename T>
    class Visitor<T>{
    public:
        virtual ~Visitor(){}
        virtual void visit(T &) = 0; 
    };
    template <typename T, typename ... Args>
    class Visitor<T, Args ... >: public Visitor<Args ... >{
    public:
        virtual void visit( T &) = 0;
        using Visitor<Args ... >::visit:
    }:
    (en fait, mon code était un peu plus complexe que cela )

    Ces quelques lignes te permettent de définir l'interface d'un visiteur, qui pourrait être utilisé sous une forme proche de
    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
    class Base{
    public:
        virtual ~Base(){}
        /* ... */
    }; 
    class D1:public Base{
        /* ... */
    }; 
    class D2:public Base{
        /* ... */
    }; 
    class D3:public Base{
        /* ... */
    }; 
    class D4:public Base{
        /* ... */
    }; 
    class D5:public Base{
        /* ... */
    };
    /* l'interface de base spécifique à cette hiérarchie de classes */
    using VBase = Visitor<D1, D2, D3, D4, D5>;
    class V1 : public VBase{
    public:
        /* il faut définir le comportement de visit pour chacune des classes dérivées ici */
    };
    /* Et deux visiteurs concrèts */
    class V2 : public VBase{
    public:
        /* il faut définir le comportement de visit pour chacune des classes dérivées ici */
    };
    En partant sur un principe sensiblement similaire, on pourrait avoir quelque chose proche de (non testé)
    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
    template <typename ... Args>
    struct MessageData;
    /* la "classe de base" (on peut avoir un std::unique_ptr qui contient autorise la représentation 
     * de toutes les autres représentation */
    template <>
    struct MessageData<> {
    public:
        virtual ~MessageData(){}
    }
    /* le dernier type de donnée, met fin à la récursion */
    template <typename T>
    struct MessageData<T>: public MessageData<>{
        MessageData<T const & t>:v{t}{}
        auto make_tuple(){
            return std::make_tuple(v);
        }
        T v;
    };
    /* tous les autres types */
    template <typename T, typename ... Arg>
    struct MessageData<T, Args ... >: public MessageData<T, Args ... >{
    public:
        MessageData(T const & v, Args ... args):MessageDatas<Args ... >(args),v{v}{}
        auto make_tuple(){
            return std::make_tuple(v);
        }
        T v;
    };
    Et ta classe message pourrait alors ressembler à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Message{
    public:
       void sendMessage(datas.get()->make_tuple(););
    std::unique_ptr<MessageData<>> datas;
    };
    Je tiens à préciser que je n'ai pas testé ce code, et qu'il y a peut être un gros paquet de trucs auxquels je n'ai pas songé, mais l'idée est là, et elle devrait pouvoir te mener à une solution sympa (n'oublie pas David Wheeler )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 165
    Points : 62
    Points
    62
    Par défaut
    Une fois de plus, merci koala01 pour le temps passé à me répondre

    En attendant ta réponse, j'ai trouvé un contournement qui va me permettre de t'expliquer clairement ce que j'ai en tête afin d'être sûr de comprendre ton bout de code.
    Tout d'abord, j'ai une classe abstraite qui va être utilisé pour le polymorphisme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #ifndef __IALERTER_HPP__                                                                                                                                                                                                                                                                     
    # define __IALERTER_HPP__                                                                                                                                                                                                                                                                    
     
    class                   IAlerter                                                                                                                                                                                                                                                             
    {                                                                                                                                                                                                                                                                                            
    /* Methods */                                                                                                                                                                                                                                                                                
    public:                                                                                                                                                                                                                                                                                      
        virtual void        sendMessage(const std::string &format, ...) const = 0;                                                                                                                                                                                                               
    };                                                                                                                                                                                                                                                                                           
     
    #endif // !__IALERTER_HPP__
    De cette classe, j'ai N classe qui en découle me permettant de définir des Alerteurs (SMS, Tchat, ...)
    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
     
    #ifndef __SLACKALERTER_HPP__
    # define __SLACKALERTER_HPP__
     
    #  include <string>
    #  include <stdarg.h>
    #  include "ialerter.hpp"
    #  include "values.hpp"
     
    class                       SlackAlerter : public IAlerter
    {
    /* Variables */
    private:
        slack::Slacking         &_handle;
        std::string             _channel, _emoji, _username;
     
    /* Methods */
    public:
        SlackAlerter()  = delete;
     
        //-----------------------------------------------------
        SlackAlerter(const std::string &token, const std::string &channel = "", const std::string &username = "", const std::string &emoji = "") : _handle(slack::create(token)), _channel(channel), _emoji(emoji), _username(username)
        //-----------------------------------------------------
        {
        }
     
        //-----------------------------------------------------
        ~SlackAlerter()
        //-----------------------------------------------------
        {
        }
     
     
        //-----------------------------------------------------
        void                sendMessage(const std::string &format, ...) const
        //-----------------------------------------------------
        {
            va_list         args;
            char            message[MAX_BUFFER_LENGTH];
     
            va_start(args, format);
            vsnprintf(message, MAX_BUFFER_LENGTH - 1, format.c_str(), args);
            try {
                this->_handle.post("chat.postMessage",
                                    {
                                        { "channel",    this->_channel },
                                        { "username",   this->_username },
                                        { "icon_emoji", this->_emoji },
                                        { "text",       message }
                                    });
            } catch (const std::runtime_error &err) {
                va_end(args);
                throw;
            }
            va_end(args);
        }
    };
    #endif // !__SLACKALERTER_HPP__
    ou encore
    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
    #ifndef __SMSALERTER_HPP__
    # define __SMSALERTER_HPP__
     
    #  include <string>
    #  include <stdarg.h>
    #  include "ialerter.hpp"
    #  include "values.hpp"
     
    class                       SMSAlerter : public IAlerter
    {
    /* Variables */
    private:
        // SMS SENDER
        std::string             _phoneNumber;
     
    /* Methods */
    public:
        SMSAlerter()  = delete;
     
        //-----------------------------------------------------
        SMSAlerter(const std::string &phoneNumber) : _phoneNumber(phoneNumber)
        //-----------------------------------------------------
        {
        }
     
        //-----------------------------------------------------
        ~SMSAlerter()
        //-----------------------------------------------------
        {
        }
     
     
        //-----------------------------------------------------
        void                sendMessage(const std::string &format, ...) const
        //-----------------------------------------------------
        {
            va_list         args;
            char            message[MAX_BUFFER_LENGTH];
     
            va_start(args, format);
            vsnprintf(message, MAX_BUFFER_LENGTH - 1, format.c_str(), args);
            try {
                        // SEND SMS
            } catch (const std::runtime_error &err) {
                va_end(args);
                throw;
            }
            va_end(args);
        }
    };
     
    #endif // !__SMSALERTER_HPP__
    Le code fonctionne mais j'aimerai autant que possible éviter de mélanger C (va_args) et C++ sachant que ce que je comprends des Variadics, ça correspond totalement à ce que je veux. Le soucis est que j'ai méchamment rouillé en CPP et donc les templates se sont un petit peu obscurcit...

    En relisant ton code Message/MessageData, est-ce qu'il faut que je comprenne que j'ai intérêt à exporter le formatage du message ou il faut que je fasse la transition Message -> iAlerter, MessageData -> SlackAlerter ?

    Bonne journée à toi

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    Ce que tu veux, c'est pas juste un vector de string? (std::vector< std::string > ?)

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    165
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2007
    Messages : 165
    Points : 62
    Points
    62
    Par défaut
    Non non. Je veux un std::vector<IAlerter *> où IAlerter représente ma classe abstraite, classe base de mes Alerter
    En gros, que j'envoie un SMS ou un Mail ou un Tchat, mon destinataire sera toujours le même et donc j'ai uniquement besoin d'une méthode me permettant de formatter/envoyer un message

  8. #8
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Dans ce cas pourquoi un Message n'est pas qu'un std::string ?
    Son formatage n'a rien à voir et devrait être externe.
    sprintf etc sont là pour ça depuis toujours.
    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.

Discussions similaires

  1. Variadic template policy et polymorphisme
    Par D-j-O dans le forum C++
    Réponses: 6
    Dernier message: 28/07/2014, 14h43
  2. Polymorphisme, templates et heritages
    Par Swaraj dans le forum Langage
    Réponses: 2
    Dernier message: 25/06/2009, 18h04
  3. Réponses: 14
    Dernier message: 09/05/2006, 15h23
  4. Réponses: 8
    Dernier message: 28/03/2006, 15h53
  5. Template et polymorphisme
    Par Pierre_IPROS dans le forum Langage
    Réponses: 19
    Dernier message: 09/10/2005, 23h04

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