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 :

contourner les fonctionnalités manquantes au C++11 de façon standard


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Par défaut contourner les fonctionnalités manquantes au C++11 de façon standard
    Bonjour,
    Je suis tombé hier sur un problème qui me turlupine depuis.

    Voyez-vous, j'ai dans mon code besoin de passer une lambda prenant en paramètre un type inconnu à la compilation, mais qui ait comme caractéristique d'être un flux sur lequel divers operator>> sont disponibles.

    M'étant renseigné, j'ai appris qu'il s'agissait là des lambdas polymorphiques, qui apparemment n'existent pas en C++11, pour une obscure raison de concepts, tout aussi inexistants également.

    J'ai essayé de résoudre ce problème (parce que oui, faut bien avancer) avec du type-erasure. Mal m'en a pris ! Je suis tombé fatalement sur une autre fonctionnalité manquante du C++ : les templates virtuels.

    Ne pouvant les utiliser, j'en suis réduit au code suivant, pas du tout flexible puisqu'il faut le modifier pour chaque type de donnée, et une contrainte de plus est que la donnée doit être disponible sur tous les types de flux que j'envisage d'utiliser (pour l'instant sf:: Packet et std::istream, mais rien ne me dis que je ne vais pas en ajouter).

    (je n'ai mis en exemple que la version pour les int)
    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
    class istr
    {
    private:
        class streamBase
        {
        public:
            streamBase() {};
            virtual ~streamBase() {};
            template <class D>
            void extract(D &d)
            {
                get(d);
            }
        protected:
            virtual void get(int &d) = 0;
        };
     
        template <class S>
        class streamObject: public streamBase
        {
        public:
            streamObject(S &iss): m_iss(iss) {};
            virtual ~streamObject() {};
     
        protected:
            virtual void get(int &d){extract(d);}
     
            template <class D>
            void extract(D &d)
            {
                m_iss >> d;
            }
        private:
            S &m_iss;
        };
     
        std::unique_ptr<streamBase> stream;
     
    public:
        template <class R>
        istr(R &iss): stream(new streamObject<R>(iss)) {}
        virtual ~istr() {};
     
        template <class T>
        inline istr& operator>>(T& var)
        {
            return *this;
        }
    };
    Y-a-t-il des solutions connues pour résoudre ce problème de façon standard au C++11 ? Avez-vous des pistes pour résoudre le problème des templates virtuels ou des lambdas polymorphiques ?

    D'avance merci pour vos réponses.

  2. #2
    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,

    pour des lambdas polymorphiques, c'est possible grâce à Boost:: Phenix.
    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.

  3. #3
    Membre Expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Par défaut
    Malheureusement je n'ai pas accès à boost, et je ne peux pas changer le type que je dois fournir, c'est obligatoirement une std::function (avec des lambda standard).

  4. #4
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Bonjour,

    Tu as le droit à boost ? Si oui alors boost::phoenix résoudra ton problème de lambda polymorphique.

    L'absence des lambdas polymorphiques est du à un problème de "cohabitation" avec les concepts, or en effet les concepts ont été retiré du C++11, mais ils devraient revenir, et intégrer d'office les lambdas polymorphiques aurait été ajouter des problèmes pour les concepts alors qu'il y en a déjà d'autre à résoudre ...

    Pour les template virtuel, je ne crois pas qu'il soit question que ca existe un jour (quand on réfléchit à la facon dont la compilation se passe, à l'implémentation des tables virtuelles et des instanciations des templates, on se rend compte que ca doit vraiment poser des problèmes d'autoriser des fonctions membres templates virtuelles).

    Edit: A la place du code que tu as écri pour contourner les problèmes, tu pourrais nous donner un exemple de code que tu voudrais écrire à l'utilisation ?

  5. #5
    Membre Expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Par défaut
    Le système en question est un module de ligne de commande.
    Je vous passe les détails de l'implémentation, le code ci-dessous est fonctionnel, mais a le défaut de ne pouvoir utiliser que les std::istream alors que j'aimerais pouvoir utiliser des sf:acket depuis le réseau.
    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 LineCommander: public ThreadedModule, sf::NonCopyable
    {
    public:
        LineCommander();
        virtual ~LineCommander();
     
        void registerCommand(const std::string &com, const std::function<void(std::istream&)> &f);
        void unregisterCommand(const std::string &com);
     
        void exec(const std::string &com, std::istream &istr);
    protected:
    private:
        virtual void frame();
     
        std::map<std::string, std::function<void(std::istream&)>> m_coms;
    };
    voilà un exemple d'utilisation de la classe (situé dans un constructeur) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    m_lc.registerCommand("accept", [this](std::istream& iss)
    {
        sf::IpAddress ip;
        unsigned int port(0);
        iss >> ip >> port;
        accept(ip, port);
    });
    avec évidemment le code complémentaire dans le destructeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    m_lc.unregisterCommand("accept");
    EDIT: quelque chose qui résoudrait le problème à la racine serait évidemment une fonction permettant de transformer un sf:: Packet en std::istream, mais ça c'est pas gagné vu que les modèles de buffer n'ont rien de comparable, et que de toute façon ce n'est pas beau.

  6. #6
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Mais en réalité tu connais forcément l'ensemble des types de flux utilisés au moment de la compilation.

    Dans ce cas tu pourrais rajouter en paramètre template de ta classe l'ensemble des types qu'elle utilise et définir un typedef sur un variant (cf boost, c'est pas hors de portée à recoder, AMA), et un opérateur >> qui travail sur ce variant (il va forwarder le bon appel, cf static_visitor).

    Ainsi pour écrire les foncteurs, ton utilisateur utilisateur ton typedef comme type.

  7. #7
    Membre expérimenté Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Par défaut
    Bonsoir,
    Le variant me semble difficile a coder soit même, a cause de l’opérateur = difficile a rendre exception-safe. Même si c'est vrais que çà peut se tenter.
    En l'absence de lambda polymorphique, la meilleur solution me semble être le type erasure cepandant, plutôt que de wrapper le flux, j'ai penser wrapper la commande. Cela n'est pas aussi concis qu'avec les lambda, mais ça me semble acceptable.
    Je vous donne un exemple:
    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
    #include <string>
    #include <map>
    #include <iostream>
    #include <sstream>
    #include <memory>
     
    class CmdFunction //! Class abstraite contenant une commande
    {
    	//Non copyable
    	CmdFunction(CmdFunction const&);
    	CmdFunction& operator =(CmdFunction const&);
     
    public:
    	CmdFunction(){}
    	virtual ~CmdFunction() = 0 {};
    };
     
    template<typename S>
    class CmdFunctionForType : public CmdFunction //! Class abstraite contenant est specialisé pour un type de flux
    {
    public:
    	virtual void exec(S& iss) = 0;
    };
     
     
    class LineCommander
    {
    public:
    	void registerCommand(const std::string &com, std::shared_ptr<CmdFunction> handler)
    	{
    		m_coms[com] = handler;
    	}
     
    	void unregisterCommand(const std::string &com)
    	{
    		m_coms.erase(com);
    	}
     
    	template<typename S>
    	void exec(const std::string &com, S &istr)
    	{
    		//! On recupere l'iterateur
    		auto iter = m_coms.find(com);
    		if(iter == m_coms.end())
    			return; //retour ou erreur
     
    		CmdFunction& handler = *iter->second;
    		//! On downcast dans le type template CmdFunctionForType aproprié
    		//! Si ca echoue, c'est que c'est le mauvais type de flux.
    		//! Ca lancera un bad_cast.
    		CmdFunctionForType<S>& downcastedHandler = 
    			dynamic_cast<CmdFunctionForType<S>&>(handler);
    		downcastedHandler.exec(istr);
    	}
     
    private: 
    	std::map<std::string, std::shared_ptr<CmdFunction> > m_coms;
    };
     
     
    int main()
    {
    	LineCommander m_lc;
     
    	//! Specialisation pour le comportement voulue.
    	//! Ok c'est un peut plus compliqué qu'un lambda, mais ca va encore.
    	//! Remarque, on peut passer des argument dans le constructeur, équivalent a 
    	//! la liste de capture dans les lambda.
    	class LocalCmdFunctionForIstream : public CmdFunctionForType<std::istream>
    	{
    	public:
    		void exec(std::istream& iss)
    		{
    			unsigned int ip = 0;
    			unsigned int port = 0;
    			iss >> ip;
    			iss >> port;
    			//accept(ip, port);
    			std::cout << ip << " " << port << std::endl;
    		}
    	};
    	m_lc.registerCommand("accept", std::make_shared<LocalCmdFunctionForIstream>());
     
    	std::stringstream iss("859212563 48496");
    	m_lc.exec<std::istream>("accept", iss); //Attention, ici, il faut specifier le bon type
     
    	return EXIT_SUCCESS;
    }
    Voila, c'est juste un exemple hein, mais ça vous donnera peut-être une piste.

  8. #8
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    @Nogane: Oui mais non, si j'ai bien compris le besoin de l'op, il veut pouvoir exécuter sa commande sur n'importe quel type de flux (hors de toute hiérarchie). Dans ton code, la commande LocalCmdFunctionForIstream ne fonctionne que pour un seul type de flux (si on considère que les flux héritant de std::istream, alors pas besoin de tout ça, le polymorphisme fait son boulot).

    L'idée de wrapper de commande ça correspond à la définition de foncteur. Or dans le cas de l'op, il a besoin de foncteur polymorphique, on peut les réaliser assez facilement à la main ou en utilisant les lambda polymorphiques, mais dans les deux cas les stocker est problématique (j'ai essayé avant de répondre tout à l'heure, et j'ai pas trouvé de solution sur le moment).

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

Discussions similaires

  1. [phpBB] Quelles sont les fonctionnalités d'administration
    Par psychoBob dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 11
    Dernier message: 22/06/2006, 13h16
  2. Bloquer les fonctionnalités word
    Par chauve59 dans le forum API, COM et SDKs
    Réponses: 5
    Dernier message: 28/04/2006, 19h22
  3. [INSTALL] où trouver les packages manquant ?
    Par cdu dans le forum Oracle
    Réponses: 4
    Dernier message: 01/04/2006, 12h03
  4. Réponses: 7
    Dernier message: 20/02/2006, 16h18
  5. PostGreSQL et les fonctionnalités spatiales
    Par luta dans le forum PostgreSQL
    Réponses: 1
    Dernier message: 27/05/2004, 17h29

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