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é.0 votes
@ildjarn mais une règle n'est pas un analyseur concret :D le contenu d'une règle est une expression, l'équivalent d'un arbre d'analyse.
0 votes
@Hassan : quelle version de spirit/compilateur utilisez-vous. Il semble au moins compiler pour moi (la partie en commentaires, t1 = t2 ;) avec msvc10 et spirit de boost 1.45.
1 votes
Je ne peux pas évaluer si le numéro 1 est sûr pour les fils ou non, mais je peux donner un conseil qui est facile à oublier. Une affectation statique n'est évaluée par le compilateur qu'une seule fois. Imaginez un petit contrôle dans le code (if (!evaluated_yet) evaluate() else noop()). La première fois qu'un objet membre pertinent de rbl_int4_parser_rule_definition est appelé quelque part, il sera construit une seule fois. c'est presque absolument équivalent à l'utilisation d'un singleton global. Pourriez-vous utiliser un singleton global de ce type pour résoudre le même problème ? (en ignorant l'ordre inti. etc.) si c'est le cas, cela devrait être thread-safe.
0 votes
Et si vous choisissiez un meilleur outil pour ce travail ?
0 votes
@zvrba Je suppose que je pourrais écrire mon propre analyseur descendant récursif, spécifique au domaine (ce que je finirai probablement par faire) :D J'ai posé cette question il y a longtemps. Spirit est un framework assez intéressant, si le TMP C++ était plus facile à utiliser, je le recommanderais à tous ceux qui font de l'analyse syntaxique.
0 votes
Je viens d'essayer de compiler votre code de "distillation", et il a compilé sans erreur (boost 1.39, gcc 4.3.2).