78 votes

plus de folie d'esprit - types d'analyseurs (règles vs int_parser<>) et techniques de méta-programmation

La question est en gras en bas, le problème est également résumé par le fragment de code de distillation vers la fin.

J'essaye d'unifier mon système de type (le système de type fait à et de type à chaîne) en un seul composant (comme défini par Lakos). J'utilise boost::array , boost::variant y boost::mpl afin d'y parvenir. Je veux que les règles de l'analyseur et du générateur pour mes types soient unifiées dans une variante. Il y a un type indéfini, un type int4 (voir ci-dessous) et un type int8. La variante se lit comme suit variant<undefined, int4,int8> .

int4 traits :

    struct rbl_int4_parser_rule_definition
    {
      typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

      boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

      rule_type rule;

      rbl_int4_parser_rule_definition()
      {
        rule.name("rbl int4 rule");
        rule = parser_int32_t;  
      }
    };

    template<>
    struct rbl_type_parser_rule<rbl_int4>
    {
      typedef rbl_int4_parser_rule_definition string_parser;
    };

la variante ci-dessus commence par être indéfinie, et ensuite j'initialise les règles. J'ai eu un problème, qui a provoqué 50 pages d'erreurs, et j'ai finalement réussi à le retrouver, la variante utilise operator= pendant l'affectation et un boost::spirit::qi::int_parser<> ne peut être affecté à un autre (opérateur=).

En revanche, je n'ai pas de problème avec mon type indéfini :

    struct rbl_undefined_parser_rule_definition
    {
      typedef boost::spirit::qi::rule<std::string::iterator, void()> rule_type;
      rule_type rule;

      rbl_undefined_parser_rule_definition()
      {
        rule.name("undefined parse rule");
        rule = boost::spirit::qi::eps;
      }
    };

    template<>
    struct rbl_type_parser_rule<rbl_undefined>
    {
      typedef rbl_undefined_parser_rule_definition string_parser;
    };

Distillation du problème :

    #include <string>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/variant.hpp>
    #include <boost/cstdint.hpp>

    typedef boost::spirit::qi::rule<std::string::iterator,void()> r1;
    typedef boost::spirit::qi::rule<std::string::iterator,int()> r2;

    typedef boost::variant<r1,r2> v;

    int main()
    {
      /*
      problematic
      boost::spirit::qi::int_parser<int32_t> t2;
      boost::spirit::qi::int_parser<int32_t> t1;

      t1 = t2;
      */

      //unproblematic
      r1 r1_;
      r2 r2_;
      r1_ = r2_;

      v v_;
      // THIS is what I need to do.
      v_ = r2();
    }

Il existe un fossé sémantique entre les analyseurs concrets et les règles. Mon cerveau fume en ce moment, je ne vais donc pas réfléchir au pramatisme, Ma question est la suivante : comment puis-je résoudre ce problème ? Je peux penser à trois approches pour résoudre le problème.

un : Membres de fonctions statiques :

    struct rbl_int4_parser_rule_definition
    {
      typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

      //boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

      rule_type rule;

      rbl_int4_parser_rule_definition()
      {
        static boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

        rule.name("rbl int4 rule");
        rule = parser_int32_t;  
      }
    };

Je suppose que l'approche 1 empêche le code thread safe ? ?

deux : Le parseur intégral est enveloppé dans un shared_ptr. Il y a deux raisons pour lesquelles je m'embête avec le TMP pour le système de typage : 1 efficacité, 2 centralisation des préoccupations dans les composants. l'utilisation de pointeurs va à l'encontre de la première raison.

trois : L'opérateur= est défini comme un no-op. La variante garantit que l'opérateur lhs est construit par défaut avant l'affectation.

Editar: Je pense que l'option 3 est la plus logique (operator= est un no-op). Une fois que le conteneur de règles est créé, il ne changera pas, et je n'assigne que pour forcer le trait de règle d'un type dans son offset.

1 votes

L'option 1 n'est pas sûre pour les threads que si : parser_int32_t a l'état y une référence est prise. Si c'est sans état ou si une copie est faite, alors c'est sûr. D'après la sémantique, je dirais qu'une copie est faite.

0 votes

C'est un problème assez déroutant, je ne peux pas être certain que l'objet analyseur n'a pas d'état. De plus, il existe une sémantique de référence et une sémantique concrète avec la mécanique des règles - c'est-à-dire qu'une règle peut contenir des références à d'autres règles, mais elles peuvent aussi être des analyseurs concrets eux-mêmes (je pense), et je ne sais pas comment cette sémantique s'applique aux analyseurs concrets.

0 votes

@MatthieuM : Exact, une copie est faite à moins que .alias() est utilisé.

11voto

sehe Points 123151

Je ne suis pas sûr d'avoir saisi toute l'étendue de la question, mais voici quelques indices

  • La ligne commentée avec // THIS is what I need to do. compile bien avec moi (problème résolu ? Je suppose que tu voulais en fait dire assigner un analyseur, pas une règle).

  • Initialisation de la fonction-local static a été défini comme étant thread safe dans la dernière norme (C++11). Vérifiez le support de votre compilateur pour le threading C++0x. (Si l'initialisation échoue, un passage de l'instruction d'initialisation tentera de l'initialiser à nouveau, d'ailleurs).

  • règles alias()

    Comme décrit dans http://boost-spirit.com/home/articles/doc-addendum/faq/#aliases

    Vous pouvez créer des "copies logiques" de règles sans avoir à copier la valeur de l'expression proto. Comme le dit la FAQ, c'est principalement pour permettre une liaison paresseuse.

  • El Le truc de Nabialek pourrait être précisément ce dont vous avez besoin, car il sélectionne paresseusement un analyseur syntaxique pour l'analyse ultérieure.

    one = id;
    two = id >> ',' >> id;
    
    keyword.add
        ("one", &one)
        ("two", &two)
        ;
    
    start = *(keyword[_a = _1] >> lazy(*_a));

    Dans votre contexte, je pourrais voir keyword défini comme suit

    qi::symbols<char, qi::rule<Iterator>*> keyword;

    faire tout le travail avec les attributs des actions sémantiques. C'est une alternative,

    qi::symbols<char, qi::rule<Iterator, std::variant<std::string,int>() >*> keyword;
  • Mettre les règles sous le même type (comme indiqué dans la ligne précédente, en gros)

    C'est la partie où je suis confus : Vous dites que vous voulez unifier votre système de type. Il se peut qu'il n'y ait pas besoin de parsers strongtypés (signatures d'attributs distinctes).

    typedef boost::variant<std::string,int> unified_type;
    typedef qi::rule<std::string::iterator, unified_type() > unified_rule;
    
    unified_rule rstring = +(qi::char_ - '.');
    unified_rule rint    = qi::int_;
    
    unified_rule combine = rstring | rint;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X