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 :

Collection & callbacks


Sujet :

Langage C++

  1. #41
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    - quand tu renvoies un pointeur, préfère toujours un pointeur intelligent à un pointeur nu. Là se pose la question de la responsabilité de la requête ? Qui doit se charger de la libérér ? (oui, la question ne se posait pas en javascript, mais ici tu n’as pas le choix)
    Je sais, c'est bien prévu d'utiliser les pointeurs intelligents mais j'ai pas encore réussi à prendre le temps de fouiller à fond la dessus.
    La requête peut être rejouée autant de fois que nécessaire. Du coup je pense que c'est à l'utilisateur du client de la supprimer.

    - ton système est hybride entre une couche bas niveau (transport) et une couche service. Quel est le sens, dans une requête publish, de tripatouiller les en-têtes ? Personnellement, j’empilerai trois couches (parce que c’est comme ça que ça se passe dans la vraie vie) :
    - une première couche purement HTTP. En fait, pour celle-ci il est probable que tu aies déjà une librairie pour ça.
    - une deuxième couche qui s’occupe uniquement du json, et qui utilise la première.
    - une troisième couche applicative, qui mappe les services exposés par ton json.
    Lorsqu'on fait du REST, les en-têtes doivent être manipulées autant que le contenu. Sinon on est tenté de mettre ce qui doit normalement aller dans les en-têtes dans les arguments de l'URL ou dans le corps de la requête.

    La couche 1 c'est HTTPRequest. La classe se charge d'accepter comme paramètres une URL, un contenu et des en-têtes.
    La couche 2 c'est le client, qui se charge de mettre en forme l'entrée de l'utilisateur (dans le cas de publish justement) et de mettre en forme la sortie (ce que j'espère faire avec des signaux).
    La couche 3 c'est l'utilisateur qui doit être à même de soumettre ses données sous forme structurée et d'accéder à d'autres en provenance du serveur suivant la même structure.

    Si mes méthodes de client (donc entre la couche 2 & 3) proposent de modifier les en-têtes, c'est surtout à cause de l'extension potentielle.
    Le client tel qu'il est là est finalement bête et méchant et ne prend en charge aucune règle ni structure particulière. D'autres classes sont sensées l'étendre pour y ajouter du métier. Du coup je ne réinvente pas la roue à chaque fois et fais régulièrement appel au niveau supérieur de ma hiérarchie de classe en proposant de nouvelles en-têtes.
    A noter également que ces classes découlant de HTPPClient ne proposent pas ces arguments. Je devrait peut-être rendre HTTPClient privé pour éviter son instanciation mais je me dit que j'aurais peut-être un jour besoin d'un client "bête et méchant".

    À chacune de ces couches, il y a des signaux émis, et des handlers exécutés. Chaque handler va à son tour émettre un nouveau signal qui sera géré par la couche supérieure.

    Au final, le client du service n’a même pas à savoir que le service est en http / json : ce n’est pas son soucis. Son soucis est que le service se conforme à l’interface.
    Justement et c'est bien mon but.
    Si je n'avais pas à transformer mon client HTTP en factory de HTTPRequest, l'utilisateur du client ne saurai pas du tout qu'il a à faire avec un service HTTP.

    Je ne suis pas sûr que ça réponde à ta question . Mais ça me semble plus sain de segmenter les responsabilités.
    Normalement elles le sont déjà, suivant les explications ci-dessus.

    J'ai toujours autant de mal à positionner de manière convenable le traitement de retour de la requête dans mon client HTTP.

    Devrais-je utiliser les slots ordonnés en plaçant un handler en 0, correspondant aux traitements de retour de la requête, déclaré dans la factory?
    Après l'utilisateur déclare les siens en 1, 2... à l'extérieur de la factory mais ça me semble fort bancal car je ne vois rien pour forcer la déclaration des handlers utilisateur en 1, 2... Comment être alors sur que l'utilisateur accède à des données post-traitées plutôt qu'aux données brutes issues de la requête?

    Merci pour ton aide, cette discussion est fort enrichissante.

  2. #42
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    La requête peut être rejouée autant de fois que nécessaire. Du coup je pense que c'est à l'utilisateur du client de la supprimer.
    Aucun soucis avec ça conceptuellement parlant, par contre, il faut l’indiquer. Dans un premier temps, dans la doc de ta fonction. Ensuite, quand tu auras pris le temps de regarder les smart_pointer, tu verras qu’un unique_ptr est probablement approprié .

    Sinon,
    Devrais-je utiliser les slots ordonnés en plaçant un handler en 0, correspondant aux traitements de retour de la requête, déclaré dans la factory?
    Après l'utilisateur déclare les siens en 1, 2... à l'extérieur de la factory mais ça me semble fort bancal car je ne vois rien pour forcer la déclaration des handlers utilisateur en 1, 2... Comment être alors sur que l'utilisateur accède à des données post-traitées plutôt qu'aux données brutes issues de la requête?
    L’utilisateur va accéder à… ce à quoi tu lui donnes accès . J’ai l’impression que tu veux faire deux choses contradictoires en même temps, à savoir :
    - fournir à l’utilisateur toutes les données, au cas où il en ait besoin
    - t’assurer que l’utilisateur ne va pas utiliser les données brutes et utiliser de préférence les données décodées.

    En clair, tu veux que l’utilisateur intervienne à la fois sur la couche 2 et la 3, tout en t’assurant qu’il le fasse correctement. Difficile

    Par contre, ça me fait penser à quelque chose que j’aurais dû évoquer bien avant, et qui me paraît à la réflexion une meilleure solution : les classes de politique. Je t’invite du coup à lire le tutoriel de alp, très bien fait :

    http://alp.developpez.com/tutoriels/traitspolicies/

    C’est un peu la même idée que ce que je te proposais avec les signaux/slots, à la différence qu’au lieu d’émettre un signal, tu vas ici appeler le comportement défini par ta politique. En c++, l’implémentation classique passe par l’utilisation de templates.

    Par rapport à ton système de callbacks, il y a une certaine segmentation qui est effectuée. Par rapport à des signaux, tu vérifies statiquement que l’utilisateur va fournir une politique pour chacun des évènements (quitte à utiliser une politique par défaut). Je vois au moins trois politiques dans ton sytème :
    - une pour altérer les en-têtes
    - une pour gérer le retour http (décoder la réponse).
    - une pour gérer le retour « data ». En fait, celle-ci n’est pas une politique de ton client http, mais… une politique de la précédente .

    Dans l’idée, les signaux/slots vont être utiles si tu dois observer le comportement au run-time, sur des objets de même type. Les politiques vont au contraire être utiles si tu dois modifier des comportements, et que tu es certain à la compilation de tes comportements. Du coup, à la réflexion le deuxième me semble plus approprié dans ton cas.

  3. #43
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Bonsoir,

    Citation Envoyé par white_tentacle Voir le message
    J’ai l’impression que tu veux faire deux choses contradictoires en même temps, à savoir :
    - fournir à l’utilisateur toutes les données, au cas où il en ait besoin
    - t’assurer que l’utilisateur ne va pas utiliser les données brutes et utiliser de préférence les données décodées.

    En clair, tu veux que l’utilisateur intervienne à la fois sur la couche 2 et la 3, tout en t’assurant qu’il le fasse correctement. Difficile
    Non, ca n'est pas ce que j'ai dit.
    Je ne veux pas que l'utilisateur accède aux données brutes mais justement aux données pré-traitées.

    Ca n'est pas moi qui ai lancé l'idée d'utiliser le client comme une factory de requêtes HTTP... Au départ de l'action la requête restait dans le périmètre du client qui ne faisait qu'appeler les callbacks de l'utilisateur.

    Maintenant on me dit que faire une factory est plus sensé donc je fait des factories et l'utilisateur se voit confier l’instance de la requête pour qu'il y connecte des signaux. Ca n'est cependant pas une entrée de mon cahier des charges actuel.

    Ce que je souhaite c'est que le client fasse un pré-traitement des données avant que l'utilisateur y accède, qu'on utilise une factory ou non. Le pré-traitement du client peut très bien être la fonction identité et on accède aux données non modifiées toujours à travers le client. Donner l'instance HTTPRequest à l'utilisateur ca me semble clairement connecter couche 1 & 3.

    Visiblement la factory complique beaucoup les choses de ce côté là. Dois-je poursuivre dans ce sens?

    Par contre, ça me fait penser à quelque chose que j’aurais dû évoquer bien avant, et qui me paraît à la réflexion une meilleure solution : les classes de politique. Je t’invite du coup à lire le tutoriel de alp, très bien fait :

    http://alp.developpez.com/tutoriels/traitspolicies/
    C'est super intéressant mais je dérive vraiment trop de ce qu'il y avait à l'origine.
    Je suis volontaire pour implémenter des principes plus adaptés au langage mais il faut aussi que je termine cette partie de mon projet...

  4. #44
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Ca n'est pas moi qui ai lancé l'idée d'utiliser le client comme une factory de requêtes HTTP... Au départ de l'action la requête restait dans le périmètre du client qui ne faisait qu'appeler les callbacks de l'utilisateur.
    Pas faux. Je vais éviter de te reprocher des choix que je t’ai amenés à faire .

    Maintenant on me dit que faire une factory est plus sensé donc je fait des factories et l'utilisateur se voit confier l’instance de la requête pour qu'il y connecte des signaux. Ca n'est cependant pas une entrée de mon cahier des charges actuel.

    Ce que je souhaite c'est que le client fasse un pré-traitement des données avant que l'utilisateur y accède, qu'on utilise une factory ou non. Le pré-traitement du client peut très bien être la fonction identité et on accède aux données non modifiées toujours à travers le client. Donner l'instance HTTPRequest à l'utilisateur ca me semble clairement connecter couche 1 & 3.
    Oui. L’objet HTTPRequest ne devrait être connu que par la couche 2. En fait, j’avais compris que le client HTTP était dans la couche 1, en fait il est dans la couche 2. J’aurais surtout dû mieux lire ton message, c’était écrit

    Visiblement la factory complique beaucoup les choses de ce côté là. Dois-je poursuivre dans ce sens?
    Oui, mais il ne faudrait pas renvoyer la requête HTTP. Ce qu’il faudrait renvoyer, c’est un objet (en fonction du type de requête exécutée) GetRecordRequest, PublishRequest… En fonction de l’objet renvoyé, les signaux émis vont donc être différents (en particulier dans les données contenues dans ces signaux). Et c’est le HTTPClient qui va se charger de connecter aux signaux de l’objet HTTPRequest ses handlers à lui, qui permettront d’effectuer le prétraitement de la requête (et émettre les nouveaux signaux, qui eux seront traités par l’utilisateur). Si je ne me plante pas, tu as prévu que HTTPClient soit héritable, et donc ces traitements de signaux peuvent tout à fait être surchargés dans une classe fille pour personnaliser ce traitement.

    C'est super intéressant mais je dérive vraiment trop de ce qu'il y avait à l'origine.
    Je suis volontaire pour implémenter des principes plus adaptés au langage mais il faut aussi que je termine cette partie de mon projet...
    Yep, je comprends. D’autant que les signaux/slots fournissent déjà une bonne abstraction, il n’y a pas nécessité de tout remettre en question.

  5. #45
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Oui, mais il ne faudrait pas renvoyer la requête HTTP. Ce qu’il faudrait renvoyer, c’est un objet (en fonction du type de requête exécutée) GetRecordRequest, PublishRequest… En fonction de l’objet renvoyé, les signaux émis vont donc être différents (en particulier dans les données contenues dans ces signaux). Et c’est le HTTPClient qui va se charger de connecter aux signaux de l’objet HTTPRequest ses handlers à lui, qui permettront d’effectuer le prétraitement de la requête (et émettre les nouveaux signaux, qui eux seront traités par l’utilisateur). Si je ne me plante pas, tu as prévu que HTTPClient soit héritable, et donc ces traitements de signaux peuvent tout à fait être surchargés dans une classe fille pour personnaliser ce traitement.
    Ca c'est habile oui.
    Donc je créerais une sorte de wrapper de ma requête HTTP comme le prolongement de ma couche 2 en dehors de mon client HTTP.
    Pour moi nul besoin de le spécialiser suivant le type de requete demandé, get, publish... Le client continue de produire une instance de HTTPRequest puis le file à mon wrapper qu'il retourne.
    Il peut également déclarer des slots sur un signal de pré-traitement qui serait diffusé avant le signal de succès par le wrapper.
    De cette façon, le client déclare ses propres fonctions comme métier de pré-traitement puis l'utilisateur déclare ses callbacks aux signaux de succès et d'echec diffusés par le wrapper en réponse aux signaux de la requête elle-meme.

    J'ai malheureusement pas les moyens de saisir facilement du code pour illustrer mes propos

    Lorsque la requete diffuse son évenement de succès, le wrapper va enregistrer en privé les données de retour (private: std::string _data_raw) et diffuser à son tour le signal de pré-traitement.
    Les slots de pré-traitement vont potentiellement accepter en entrée les données brutes de la requête.
    Une fois modifiées, les données post-traitées sont retournées au wrapper (private: std::string _data).
    Le wrapper diffuse ensuite à son tour un signal de succès pour appeler les callbacks de l'utilisateur en fournissant les données post-traitées.

    Il reste un problème : Au cours du pré-traitrement, les données peuvent changer de type. Au dessus, _data_raw et _data ont tous les deux le type std::string. Dans le cas du JSON dé-sérialisé on va avoir un std::string avant et une structure plus complexe après. C'est le client qui a la responsabilité du protocole et qui fixe ce genre de détail. Le wrapper pourrait donc être une classe template.

    Si tu es d'accord avec ca, j'y vois l'avantage de ne devoir modifier/étendre que mes classes de client et non de wrapper. Si je dois créer des classes différentes pour tous mes cas de pré-traitement, ca va faire trop alors que des lambdas peuvent faire l'affaire.

    Yep, je comprends. D’autant que les signaux/slots fournissent déjà une bonne abstraction, il n’y a pas nécessité de tout remettre en question.
    Ca me donne quelques sujets de réflexion dans le métro oui en effet

    Bonne nuit & bon week end!

  6. #46
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Il reste un problème : Au cours du pré-traitrement, les données peuvent changer de type. Au dessus, _data_raw et _data ont tous les deux le type std::string. Dans le cas du JSON dé-sérialisé on va avoir un std::string avant et une structure plus complexe après. C'est le client qui a la responsabilité du protocole et qui fixe ce genre de détail. Le wrapper pourrait donc être une classe template.
    C’est pour cette raison que je te suggérais un wrapper différent selon le type de requête. Mais si c’est l’utilisateur qui détermine si la donnée change de type, alors oui, il te faut une classe template (et tu es en plein dans le design pattern politique ).

  7. #47
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    C’est pour cette raison que je te suggérais un wrapper différent selon le type de requête. Mais si c’est l’utilisateur qui détermine si la donnée change de type, alors oui, il te faut une classe template (et tu es en plein dans le design pattern politique ).
    En fait le type de requête n'est pas un critère suffisant. Ca depend du protocole et de l'API visé. Un HTTP GET sur deux services différents va impliquer une préparation puis un post-traitement différents.

    Ce métier là est de la responsabilité du client HTTP et pas de l'utilisateur.

    Je pourrais ecrire en effet un wrapper différent pour chaque cas, ca va néanmoins faire trop sachant que je peux très bien écrire des lambdas et les déclarer en slots. Le code sera découpé pour une éventuelle création de classe à l'avenir.

    Je serai en mesure de coder ça demain soir, je posterai le code et les erreurs de compil dans la foulée

  8. #48
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Je suis entrain de faire l'implémentation et ça à l'air de fonctionner.

    Une question de bonne pratique me laisse dubitatif:
    Lorsque je défini mes slots, je suis tenté de créer des membres privés et de les lier au signal avec connect() ou bien de définir des lambdas.
    Quelle est la meilleure solution?

    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
     
    class A {
       private:
       boost::signal<void ()> _sig;
     
       void handler () {
          // Actions
       }
     
       public:
       A (){
           this->_sig.connect (&A::handler);
       }
     
       void fire(){
           this->_sig();
       }
    }
    ou bien...
    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 A {
       private:
       boost::signal<void ()> _sig;
     
       public:
       void autre_methode (){
           void handler = [this]() { // Actions };
     
           this->_sig.connect (handler);
       }
     
       void fire(){
           this->_sig();
       }
    }
    Est-ce équivalent, est-ce juste?

    [EDIT]
    Il semblerait que ni Signal ni Signal2 de boost ne supportent les lambdas à capture comme ceux que j'ai écrit.
    Est-ce surmontable?
    [/EDIT]

    Merci par avance.

  9. #49
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Je suis entrain de faire l'implémentation et ça à l'air de fonctionner.

    Une question de bonne pratique me laisse dubitatif:
    Lorsque je défini mes slots, je suis tenté de créer des membres privés et de les lier au signal avec connect() ou bien de définir des lambdas.
    Quelle est la meilleure solution?
    Ça dépend.

    Mais, de mon point de vue, une lambda ne devrait pas faire plus de 3-4 instructions au maximum. Il y a plusieurs avantages à nommer la fonction :
    - le code est plus clair, mieux indenté (l’indentation de la lambda, c’est assez folklorique)
    - le nommage de la fonction permet d’expliquer ce qu’elle fait, là où pour comprendre l’intention d’une lambda, il faut lire le code

    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
     
    class A {
       private:
       boost::signal<void ()> _sig;
     
       void handler () {
          // Actions
       }
     
       public:
       A (){
           this->_sig.connect (&A::handler);
       }
     
       void fire(){
           this->_sig();
       }
    }
    ou bien...
    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 A {
       private:
       boost::signal<void ()> _sig;
     
       public:
       void autre_methode (){
           void handler = [this]() { // Actions };
     
           this->_sig.connect (handler);
       }
     
       void fire(){
           this->_sig();
       }
    }
    Est-ce équivalent, est-ce juste?
    J’écrirai plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->_sig.connect ([this]() { // Actions };);
    D’ailleurs, le type n’est pas void .

    Il semblerait que ni Signal ni Signal2 de boost ne supportent les lambdas à capture comme ceux que j'ai écrit.
    Est-ce surmontable?
    Quelle version de boost / du compilateur ? Chez moi (gcc 4.7 et boost 1.49):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    void connect(Document& doc)
    {
        doc.connect([this](bool){ this->f(); });
    }
    fonctionne et a le comportement attendu.

  10. #50
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Ça dépend.

    Mais, de mon point de vue, une lambda ne devrait pas faire plus de 3-4 instructions au maximum. Il y a plusieurs avantages à nommer la fonction :
    - le code est plus clair, mieux indenté (l’indentation de la lambda, c’est assez folklorique)
    - le nommage de la fonction permet d’expliquer ce qu’elle fait, là où pour comprendre l’intention d’une lambda, il faut lire le code
    D'accord, j'en tiendrai compte.

    J’écrirai plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->_sig.connect ([this]() { // Actions };);
    D’ailleurs, le type n’est pas void .
    Quel est-il? Probablement, je dois passer à côté de quelque chose

    Quelle version de boost / du compilateur ? Chez moi (gcc 4.7 et boost 1.49):
    Je suis avec Boost 1.51.0 et Visual Studio 2012 (avec l'update de 11/2012).

    J'ai trouvé mon erreur : un oubli de déclaration du slot en const comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    void HTTPRequest::onSuccess (const sig_success_t::slot_type &clbk){
    	this->_sig_success.connect(clbk);
    }

  11. #51
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this->_sig.connect ([this]() { // Actions };);
    D’ailleurs, le type n’est pas void .
    Quel est-il? Probablement, je dois passer à côté de quelque chose
    Le type de la lambda, c’est un pointeur vers une fonction qui renvoie le même type et prends les mêmes arguments.

    Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void (*handler)() = [this]() { // Actions };
    Mais comme tu es en c++11, tu peux aussi utiliser auto :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    auto handler = [this]() {}
    .

    Ou sinon, utiliser le type défini dans le signal :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    typedef boost::signal<void()> sig;
    …
    sig::slot_type handler = [this]() {}
    J'ai trouvé mon erreur : un oubli de déclaration du slot en const comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    void HTTPRequest::onSuccess (const sig_success_t::slot_type &clbk){
    	this->_sig_success.connect(clbk);
    }


    N’hésite pas si tu as d’autres soucis.

  12. #52
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Mais, de mon point de vue, une lambda ne devrait pas faire plus de 3-4 instructions au maximum. Il y a plusieurs avantages à nommer la fonction :
    J'utilise pour ma part une règle plus lâche :
    - Soit la lambda ne fait que 3/4 lignes
    - Soit le code utilisant la lambda ne fait que 3/4 lignes.

    Si j'ai une fonction ne contenant rien d'autre qu'un appel à std::async(), je m'autoriserai à avoir une lambda plus grosse.

    Citation Envoyé par white_tentacle Voir le message
    Le type de la lambda, c’est un pointeur vers une fonction qui renvoie le même type et prends les mêmes arguments.
    Le type d'une lambda est une classe dont on ne peut pas connaitre le nom (mais dont on peut créer une instance, à l'aide d'auto). Ce type est compatible avec un std::function ou autre, ce dernier servant de type erasure (mais avec un coup en perfs). Mais il peut être intéressant dans certains cas d'utilsier directement le vrai type.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  13. #53
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Le type de la lambda, c’est un pointeur vers une fonction qui renvoie le même type et prends les mêmes arguments.
    Oui pardon, ca m'avait échappé

    Donc tout fonctionne de ce point de vue, j'ai par contre une masse d'erreur de compil impressionnantes avant de valider la solution par le test.

    N’hésite pas si tu as d’autres soucis.
    Ce topic est allé beaucoup plus loin que les attentes initiales, merci

    Il doit rester un problème avec le type de retour du signal lors de son appel.
    Voici le code de mon wrapper de HTTPRequest avec un seul signal de preprocess
    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
     
    template <typename out_t>
    class HTTPRW {
     
    	typedef boost::signals2::signal <out_t (std::string &)> sig_preProcess_t;
     
    	// --------------------------------------------------------------------------------
    	// Attributs
    	// --------------------------------------------------------------------------------
     
    	private:
    	HTTPRequest *_request;
    	std::string _data_raw;
    	out_t _data;
     
    	sig_preProcess_t _sig_preProcess;
     
    	// --------------------------------------------------------------------------------
    	// Getters / Setters
    	// --------------------------------------------------------------------------------
     
     
    	// --------------------------------------------------------------------------------
    	// Methodes
    	// --------------------------------------------------------------------------------
    	private: HTTPRW (HTTPRW const &); // Pas de copie
    	private: HTTPRW & operator = (HTTPRW const &); // Pas d'affectation
    	public: virtual ~HTTPRW();
     
    	// --------------------------------------------------------------------------------
    	// Function : HTTPRW () // CONSTRUCTEUR
    	// Description :
    	//	Instancie la classe
    	// Input:
    	//	@req : Pointeur vers l'objet HTTPRequest à wrapper
    	// --------------------------------------------------------------------------------
    	public: HTTPRW (HTTPRequest *req) {
    		this->_request = req;
     
    		auto success_handler = [this] (std::string &data, unsigned int status) {
    			this->_data_raw = data;
    			this->_data = this->_sig_preProcess(data);
     
    			// On poursuivra avec le déclenchement d'un signal de succès à destination de l'utilisateur
    		};
    		auto error_handler = [this] (std::string &response, unsigned int status, std::string &error) {
    			// On poursuivra avec la diffusion d'un signal d'erreur à destination de l'utilisateur
    		}
     
    		req->onSuccess(success_handler);
    		req->onError(error_handler);
    	}
    	// --------------------------------------------------------------------------------
     
    	// --------------------------------------------------------------------------------
    	// Function : send()
    	// Description :
    	//	Lance la requete HTTP wrappée
    	// --------------------------------------------------------------------------------
    	public: bool send (bool async = false) {
    		return this->_request->send(async);
    	}
    	// --------------------------------------------------------------------------------
     
    	// --------------------------------------------------------------------------------
    	// Function : onPreProcess()
    	// Description :
    	//	Enregistrement un slot sur le signal de pré-processing
    	// --------------------------------------------------------------------------------
    	public: void onPreProcess (typename sig_preProcess_t::slot_type &clbk) {
    		this->_sig_preProcess.connect(clbk);
    	}
    	// --------------------------------------------------------------------------------
     
    	// Deux autres futures méthodes pour les signaux de succès et d'erreur.
    };
    A la ligne this->_data = this->_sig_preProcess(data); j'ai l'erreur suivante:
    error C2679: binary '=' : no operator found which takes a right-hand operand of type 'boost::optional<T>' (or there is no acceptable conversion)
    1> with
    1> [
    1> T=std::basic_string<char,std::char_traits<char>,std::allocator<char>>
    1> ]
    1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstring(992): could be 'std::basic_string<char,std::char_traits<char>,std::allocator<char>> &std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator =(_Elem)'
    1> with
    1> [
    1> _Elem=char
    1> ]
    1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstring(987): or 'std::basic_string<char,std::char_traits<char>,std::allocator<char>> &std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator =(const _Elem *)'
    1> with
    1> [
    1> _Elem=char
    1> ]
    1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstring(969): or 'std::basic_string<char,std::char_traits<char>,std::allocator<char>> &std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator =(const std::basic_string<char,std::char_traits<char>,std::allocator<char>> &)'
    1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstring(912): or 'std::basic_string<char,std::char_traits<char>,std::allocator<char>> &std::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator =(std::basic_string<char,std::char_traits<char>,std::allocator<char>> &&) throw()'
    1> while trying to match the argument list '(std::string, boost::optional<T>)'
    1> with
    1> [
    1> T=std::basic_string<char,std::char_traits<char>,std::allocator<char>>
    1> ]
    Je ne comprends pas car je pense avoir fait les bonnes correspondances de types...

  14. #54
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    A priori le signal te renvois un boost::optional<std::string>, regardes les fonctions get() et get_value_or(T const& default) pour récupérer ton std::string

    doc ici

  15. #55
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Le résultat ne s'est pas fait attendre :
    ========== Génération*: 1 a réussi, 0 a échoué, 0 mis à jour, 0 a été ignoré ==========
    Merci beaucoup!

  16. #56
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Bonsoir,

    A la suite des tests que j'ai pu faire, il semble que l'approche proposée ici à base de signaux/slots sous Boost donne pleinement satisfaction.

    Je vais donc mettre à jour mon code pour supprimer les classes maisons que j'avais fait pour gérer mes callbacks.
    L'approche des signaux a bien pour avantage de gérer les types au moment de la compilation et non à l’exécution. On s'évite ainsi moult problèmes ensuite...
    A part ces évolutions fonctionnelles, le code Boost étant maintenu, je m'épargne le support de cette solution qui sort du périmètre de ma librairie.

    L'utilisation de wrapper permettant la connexion des slots avec un objet issu d'une factory est également adapté.


    Merci à l'aide apportée par les participants de ce topic

    A bientôt sur le forum

+ Répondre à la discussion
Cette discussion est résolue.
Page 3 sur 3 PremièrePremière 123

Discussions similaires

  1. [VB6] Sauvegarder une collection d'objets
    Par Sayagh dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 19/09/2003, 11h58
  2. [VB6] la collection controls
    Par tomnie dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 30/04/2003, 17h03
  3. Comment créér une collection sous Delphi
    Par PsyKroPack dans le forum Langage
    Réponses: 6
    Dernier message: 11/02/2003, 13h20
  4. [VB6] Modifier la clé d'un élément d'une collection
    Par Ricou13 dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 21/11/2002, 14h49

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