Caso general: no puedo entender por qué mis acciones de gramática / semántica de Espíritu no se están comstackndo.
A veces, el comstackdor se quejará de la asignación o del tipo de incompatibilidades y no tengo idea de qué ocurre. El problema ocurre en dos áreas principales:
El error del comstackdor no es exactamente tratable, y la documentación está equivocada, o lo malentendí.
¿Hay alguna manera de descubrir exactamente qué es lo que el Espíritu pasa a mi acción semántica?
struct mybase { int a,b; }; struct myderived : mybase { int c,d; }; BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b)); BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d)); auto base_expr = int_ >> int_; // avoids assigning to struct attribute rule base_ = int_ >> int_; rule derived_ = base_ >> int_ >> int_; myderived data; bool ok = phrase_parse(f,l,derived_,space,data);
Este código no se comstackrá, con una gran cantidad de errores impenetrables.
( ligeramente adaptado de una publicación en la lista general del espíritu )
Para mayor claridad, el error aquí es que se base_ >> int_ >> int_
como la expresión para una regla que crea un myderived
, y como base_
está fijo para escribir mybase
, tendríamos que crear un myderrived
desde un mybase
y dos int
s, pero no hay nada que decirle a Spirit cómo hacerlo.
Puede obtener impulso para imprimir el tipo de valor que boost crea al analizar base_ >> int_ >> int_
definiendo un functor que tomará cualquier parámetro y le dirá cuáles son (el siguiente código se ha adaptado de algún código sehe poner en SO chat):
struct what_is_the_attr { template struct result { typedef bool type; }; template static void print_the_type() { std::cout << " "; std::cout << typeid(T).name(); if(std::is_const ::type>::value) std::cout << " const"; if(std::is_rvalue_reference::value) std::cout << " &&"; else if(std::is_lvalue_reference ::value) std::cout << " &"; } template static void print_the_type() { print_the_type(); std::cout << ",\n"; print_the_type(); } template void operator()(Ts&&...) const { std::cout << "what_is_the_attr(\n"; print_the_type(); std::cout << ")" << std::endl; } };
Luego, para usarlo, use el actor anterior en una acción semántica en el inicializador para su regla defectuosa:
std::string input = "1 2 3 4"; auto f(std::begin(input)), l(std::end(input)); rule base_ = int_ >> int_; rule derived_ = (base_ >> int_ >> int_)[what_is_the_attr()]; myderived data; bool ok = phrase_parse(f,l,derived_,space,data);
Tenga en cuenta que no puede usar la propagación automática de atributos con %=
(a menos que elimine el tipo de atributo expuesto del tipo declarado de la regla).
Ejecutar esto debería producir un tipo codificado, que se puede decodificar con c++filt -t
: Live On Coliru
$ g++ 9404189.cpp -std=c++0x $ ./a.out |c++filt -t what_is_the_attr( boost::fusion::vector3 &, boost::spirit::context, boost::fusion::vector0 > &, bool &)
La primera línea, boost::fusion::vector3
, al menos te dice que boost está intentando crear tu tipo de retorno a partir de 3 objetos de tipos mybase
, int
e int
.
Pude resolver el problema para este caso en particular (de hecho discutimos opciones en la lista ), pero en realidad, este tipo de error “enigmático” se repite más a menudo con Boost Spirit y sería bueno tener una idea de la clase general de problemas
Su primer recurso debe ser la excelente documentación espiritual, que detalla exactamente cuál será el atributo sintetizado para una primitiva, operador o directiva del analizador determinado. Vea la sección de Referencia para Spirit Qi Docs .
En algunos casos, he decidido cambiar el foco de “intentar extraer la información de la lista de compiler errors” a “consultar activamente a Spirit por los tipos que pasa”. La técnica que uso para esto es el Tipo de Llamamiento Polimórfico (ver documentos Spirit / Fusion).
Aquí hay uno que usa las API específicas de GCC para imprimir bastante [sic] los tipos que detecta:
what_is_the_attr
#include #include #include #include template std::string nameofType(const T& v) { int status; char *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status); std::string name(realname? realname : "????"); free(realname); return name; } struct what_is_the_attr { template struct result { typedef bool type; }; template bool operator()(T& attr) const { std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl; return true; } };
Puede usarlo para detectar exactamente de qué tipo termina el tipo de atributo sintetizado de una expresión de analizador:
template void detect_attr_type(const Exp& exp) { using namespace boost::spirit::qi; const char input[] = "1 2 3 4"; auto f(std::begin(input)), l(std::end(input)-1); bool dummy = phrase_parse( f, l, exp [ what_is_the_attr() ], space); }
( Nota: esto muestra una limitación del enfoque: la técnica asume que tiene una gramática que funciona de otra manera y sabe cómo pasar una entrada que satisfaga la expresión suficiente para desencadenar la acción semántica. En la mayoría de los casos, esto será cierto cuando estás pirateando tu analizador de Espíritu, sin embargo )
Vamos a probarlo. Por ejemplo, veamos cuál es la diferencia entre y la expresión de complejidad media, y lo mismo dentro de una directiva qi::raw[]
:
int main() { detect_attr_type( -(int_ >> *int_) ); detect_attr_type( raw [ -(int_ >> *int_) ] ); }
Salida:
what_is_the_attr: boost::optional > > > what_is_the_attr: boost::iterator_range
En la parte inferior, aplicaremos esto a la pregunta en el PO.
Podríamos usar el mismo objeto de función unario ( what_is_the_attr
) para detectar estos, sin embargo, las acciones semánticas pueden tomar cualquier cantidad de argumentos, por lo que debemos generalizar. Esto sería un trabajo tedioso, si no fuera por la plantilla variadic (woot! Para c ++ 0x):
struct what_are_the_arguments { template struct result { typedef bool type; }; template bool operator()(const T&... attr) const { std::vector names { nameofType(attr)... }; std::cerr << "what_are_the_arguments:\n\t"; std::copy(names.begin(), names.end(), std::ostream_iterator (std::cerr, "\n\t")); std::cerr << '\n'; return true; } };
Repetido para los casos de prueba anteriores, revela que Spirit realmente intenta llamar a la acción semántica con tres argumentos si es posible (como se documenta ):
what_are_the_arguments: boost::optional > > > boost::spirit::unused_type bool what_are_the_arguments: boost::iterator_range boost::spirit::unused_type bool
Pero lo bueno es que ahora puedes aplicar esto a cualquier acción semántica:
template void test(const ExpWSA& exp) { const char input[] = "1 2 3 4"; auto f(std::begin(input)), l(std::end(input)-1); qi::phrase_parse(f, l, exp, qi::space); } int main() { test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]); }
Impresión, para este (lamentable) ejemplo muy artificial:
what_are_the_arguments: boost::optional std::vector > boost::fusion::vector2, std::vector > > std::ostream int
El atributo sintetizado de la regla derived
no es el mismo que para int_>>int_>>int_>>int_
:
auto base_expr = int_ >> int_; // avoids assigning to struct attribute rule base_ = base_expr; test(base_ >> int_ >> int_ [ what_is_the_attr() ] ); test(base_expr >> int_ >> int_ [ what_is_the_attr() ] );
Se imprimirá
what_is_the_attr: boost::fusion::vector3 what_is_the_attr: boost::fusion::vector4
Ahí está tu problema Discutimos algunas soluciones basadas en este diagnóstico en el hilo original (y vemos las otras respuestas aquí). Pero esta publicación debería ayudar a responder la pregunta del caso general.
En forma integrada, comstackdo con gcc 4.6.1 --std = c ++ 0x y boost 1_48:
#include #include #include #include #include #include template std::string nameofType(const T& v) { int status; char *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status); std::string name(realname? realname : "????"); free(realname); return name; } struct what_is_the_attr { template struct result { typedef bool type; }; template bool operator()(T& attr) const { std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl; return true; } }; struct what_are_the_arguments { template struct result { typedef bool type; }; template bool operator()(const T&... attr) const { std::vector names { nameofType(attr)... }; std::cerr << "what_are_the_arguments:\n\t"; std::copy(names.begin(), names.end(), std::ostream_iterator (std::cerr, "\n\t")); std::cerr << '\n'; return true; } }; #include #include #include struct mybase { int a,b; }; struct myderived : mybase { int c,d; }; BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b)); BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d)); template void test(const ExpWSA& exp) { using namespace boost::spirit::qi; const char input[] = "1 2 3 4"; auto f(std::begin(input)), l(std::end(input)-1); bool dummy = phrase_parse(f, l, exp, space); } int main() { using namespace boost::spirit::qi; // Diagnostics for the OP case auto base_expr = int_ >> int_; // avoids assigning to struct attribute rule base_ = base_expr; // Derived rule, different formulations test((base_ >> int_ >> int_) [ what_is_the_attr() ] ); test((base_expr >> int_ >> int_) [ what_is_the_attr() ] ); // Applied to attribute types test(raw [ -(int_ >> *int_) ] [ what_is_the_attr() ] ); test(-(int_ >> *int_) [ what_is_the_attr() ] ); // applied to semantic actions - contrived example namespace phx = boost::phoenix; test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]); return 0; }