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

Boost C++ Discussion :

Boost::Spirit : Encapsuler le parseur et ses résultats.


Sujet :

Boost C++

  1. #1
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut Boost::Spirit : Encapsuler le parseur et ses résultats.
    Plop !

    Je cherche désespérément le moyen d'encapsuler mes parsers.
    Je m'explique : je souhaiterais que les callback de mes parseurs soient des méthodes d'une certaine classe (appartenant au parser ou pas) et non des fonctions globales.

    Est-ce possible ?

    Pour l'instant j'utilises ce style: source mais ça ne me convient pas du tout dans l'optique de parser des échanges TCP, et la misère que ça peut faire si c'est en plus multi-thread :\

    Chacune de mes tentatives de syntaxe échouent lamentablement avec 14 pages d'erreur

  2. #2
    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
    Peux-tu montrer un exemple de ce que tu tente de faire et qui échoue ?

    Par ailleurs, je ne vois pas le lien entre fonctions libres et problèmes de multi-thread.
    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.

  3. #3
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    Ouaip bien sûr

    Voici ce que je tente de 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
    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
     
    struct IRCParser : public grammar<IRCParser>
    {
     
        template<typename ScannerT>
        struct definition
        {
     
            definition( IRCParser const& self)
            {
                message
                    =   ch_p(':')
                        >> nick
                        >> host
                        >> space_p
                        >> type
                        >> space_p
                        >> !argument
                        >> ch_p(':')
                        >> info
                    ;
                nick
                    =
                        (+alnum_p)[&this->nick_f] <==== Erreur
                        >> ch_p('!')
                    ;
                host
                    =
                        (
                            +alnum_p
                            >> ch_p('@')
                            >> +alnum_p
                            >> ch_p('.')
                            >> +alnum_p
                        )[&this->host_f]                  <==== Erreur
                    ;
                type
                    =
                        (+upper_p)[&this->type_f] <==== Erreur
                    ;
                argument
                    =
                        (+(anychar_p - ':'))[&this->arg_f] <==== Erreur
                    ;
                info
                    =   (*anychar_p)[&this->info_f] <==== Erreur
                    ;
            }
     
            rule<ScannerT> message, nick, host, type, argument, info;
     
            rule<ScannerT> const& start() const
            {
                return message;
            }
     
            string snick;
            string shost;
            string stype;
            string sarg;
            string sinfo;
     
            void nick_f( char const * first, char const * last ) { snick = string(first, last); }
            void host_f( char const * first, char const * last ) { shost = string(first, last); }
            void type_f( char const * first, char const * last ) { stype = string(first, last); }
            void arg_f ( char const * first, char const * last ) { sarg  = string(first, last); }
            void info_f( char const * first, char const * last ) { sinfo = string(first, last); }
     
        };
     
    };
     
    int main()
    {
        IRCParser r;
     
        string str = ":Nick!Host@name.here JOIN :#channel";
        parse( str.c_str(), r );
     
     
        return 0;
    }
    En gros, je souhaiterais après chaque parse réussi pouvoir récupérer les infos directement dans les membres du parser.

    Et le soucis du multi-thread c'est que admettons que j'ai 2 thread étant chacun connecté sur un serveur différent et que je reçois des messages des 2 serveurs. Soit je dois "recopier" les fonctions pour qu'ils ne s'appellent pas entre eux, soit les variables de résultats vont être complètement insignifiant.

    Un autre soucis serait que si j'ai 1 thread qui travaille sur un message et qu'un autre parse un autre message pendant que le 1er copie, le process du 1er thread sera foutu en l'air.

  4. #4
    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 JulienDuSud Voir le message
    En gros, je souhaiterais après chaque parse réussi pouvoir récupérer les infos directement dans les membres du parser.
    Je n'ai pas trop d'expérience pratique de spirit, mais je pense voir le problème maintenant. En fait, tes différentes fonctions modifient un état global (ce qui effectivement posera des problèmes du multithread), état que tu tente de rendre moins global, mais qui reste en dehors du parseur lui même.

    Je pense que le plus simple serait de modifier la sémantique de ton parseur pour qu'au lieu de modifier un état global, il retourne simplement un objet, et que toutes les fonctions associées au parsing soient sans "side effects" (sauf éventuellement du log).

    Autrement, si tu veux continuer sur cette voie, je te conseille la lecture de http://spirit.sourceforge.net/distri...c/grammar.html et de l'exemple associé.
    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.

  5. #5
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Je pense que le plus simple serait de modifier la sémantique de ton parseur pour qu'au lieu de modifier un état global, il retourne simplement un objet, et que toutes les fonctions associées au parsing soient sans "side effects" (sauf éventuellement du log).
    C'est exactement ce que je souhaite faire, malheureusement soit je lis de travers soit la documentation de spirit ne mentionne pas d'exemple concret sur comment le faire, et me lancer dans une étude complète des classes de spirit me prendrait trop de temps que pour faire une chose aussi triviale que ceci.

    Donc la question finale: est-il possible avec Spirit de séparer de manière cohérente et safe la sémantique de la grammaire ? Si oui, comment ? :\

  6. #6
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Je m'en suis sorti en utilisant phoenix, closure et lazy_parser.
    J'essaie d'en extraire un exemple simple et je le poste.

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Rectification : pas besoin de lazy_parser (chez moi, c'était pour un autre besoin...).

    L'idée est d'associée une variable état (par closure) à la règle et d'utiliser phoenix::bind pour l'appel de fonction.
    Le résultat (boost 1.38) :
    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
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    #include <string>
    #include <iostream>
     
    #include <boost/spirit/include/classic_core.hpp>
       // la base de spirit
    #include <boost/spirit/include/classic_attribute.hpp>
       // pour permettre les closures
    #include <boost/spirit/include/phoenix1.hpp>
       // pour permettre le bind
     
     
    using namespace BOOST_SPIRIT_CLASSIC_NS;
    using namespace phoenix;
    using namespace std;
     
    // cette structure va nous servir à recevoir le résultat du parse 
    // j'ai repris ce que tu avais fait et l'ai juste sorti de la structure :
    struct stIRCResultat
    {
       string snick;
       string shost;
       string stype;
       string sarg;
       string sinfo;
     
       void nick_f( std::string const & val_) { snick = val_; }
       void host_f( std::string const & val_) { shost = val_; }
       void type_f( std::string const & val_) { stype = val_; }
       void arg_f( std::string const & val_) { sarg = val_; }
       void info_f( std::string const & val_) { sinfo = val_; }
    };
     
    // la structure qui permet d'associer un paramètre à une règle :
    // le second template est le type du paramètre :
    struct IRCParser_closure : boost::spirit::classic::closure<IRCParser_closure, stIRCResultat>
    {
       member1 variable_1;
    };
     
    // ta grammaire :
    struct IRCParser : public grammar<IRCParser, IRCParser_closure::context_t>
    {
     // j'ai mis le resultat dans la grammaire au vu de ce que j'ai compris de ton fil
    // mais, il aurait été plus logique de le passer en parametre (cf ci dessous)
       stIRCResultat m_resultat;
     
        template<typename ScannerT>
        struct definition
        {
     
            definition( IRCParser const& self)
            {
                message
                    =   ch_p(':')
                        >> nick
                        >> host
                        >> space_p
                        >> type
                        >> space_p
                        >> !argument
                        >> ch_p(':')
                        >> info
                    ;
                nick
                    =
                        (+alnum_p)[
                         phoenix::bind(&stIRCResultat::nick_f)(self.variable_1,phoenix::construct_<std::string>(arg1, arg2))
                        ]// l'action déclanchée consiste à appeler la méthode avec le résultat du parser (+alnum_p) sur l'objet parametre de la règle
                        >> ch_p('!')
                    ;
                host
                    =
                        (
                            +alnum_p
                            >> ch_p('@')
                            >> +alnum_p
                            >> ch_p('.')
                            >> +alnum_p
                        )[
                         phoenix::bind(&stIRCResultat::host_f)(self.variable_1,phoenix::construct_<std::string>(arg1, arg2))
                        ]
                        //[&this->host_f]                  <==== Erreur
                    ;
                type
                    =
                        (+upper_p)[
                         phoenix::bind(&stIRCResultat::type_f)(self.variable_1,phoenix::construct_<std::string>(arg1, arg2))
                        ]//[&this->type_f] <==== Erreur
                    ;
                argument
                    =
                        (+(anychar_p - ':'))[
                         phoenix::bind(&stIRCResultat::arg_f)(self.variable_1,phoenix::construct_<std::string>(arg1, arg2))
                        ]//[&this->arg_f] <==== Erreur
                    ;
                info
                    =   (*anychar_p)[
                         phoenix::bind(&stIRCResultat::info_f)(self.variable_1,phoenix::construct_<std::string>(arg1, arg2))
                        ]//[&this->info_f] <==== Erreur
                    ;
            }
     
            rule<ScannerT> message, nick, host, type, argument, info;
     
            rule<ScannerT> const& start() const
            {
                return message;
            } 
        };
     
    };
     
    int main()
    {
        IRCParser r;
     
        string str = ":Nick!Host@name.here JOIN :#channel";
        parse( str.c_str(), r[var(r.m_resultat) = arg1]);
        std::cout
          <<r.m_resultat.snick<<std::endl
          <<r.m_resultat.shost<<std::endl
          <<r.m_resultat.stype<<std::endl
          <<r.m_resultat.sarg<<std::endl
          <<r.m_resultat.sinfo<<std::endl
       ;
     
       // en fait, il est certainement plus approprie de sortir le resultat
       // de la grammaire (suppression de la variable membre m_resultat)
       // et d'utiliser une variable locale comme résultat du parse : 
        stIRCResultat resultat;
        parse( str.c_str(), r[var(resultat) = arg1]);
        std::cout
          <<resultat.snick<<std::endl
          <<resultat.shost<<std::endl
          <<resultat.stype<<std::endl
          <<resultat.sarg<<std::endl
          <<resultat.sinfo<<std::endl
       ;
     
        return 0;
    }

  8. #8
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    Merci beaucoup ! C'est exactement pile poil ce que je cherchais

    J'avais finalement décidé de me tourner vers les regex mais finalement, grâce à toi, non

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Encore mieux avec

  10. #10
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Encore mieux avec
    Oups

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

Discussions similaires

  1. Parseur de multiple affectation (avec Boost.Spirit)
    Par Trademark dans le forum Langage
    Réponses: 5
    Dernier message: 16/07/2012, 19h20
  2. [BOOST.Spirit] Absence génante
    Par Invité2 dans le forum Boost
    Réponses: 27
    Dernier message: 10/10/2008, 12h45
  3. [BOOST.Spirit] Types de bases inéxistant
    Par Invité2 dans le forum Boost
    Réponses: 4
    Dernier message: 14/09/2008, 21h31
  4. Comment utiliser Boost::spirit ?
    Par kimels dans le forum Boost
    Réponses: 8
    Dernier message: 11/06/2008, 19h04
  5. find encapsulé afin d'exclure certains résultats
    Par soveste dans le forum Shell et commandes GNU
    Réponses: 3
    Dernier message: 29/01/2008, 09h39

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