Detectar los tipos de parámetros en una acción semántica de Spirit

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:

  • predecir el tipo de atributos sintetizados para una regla / expresión
    • consecuentemente, predecir qué tipos de atributos se pueden definir legalmente como el atributo expuesto para la regla (confiando en construir conversiones, adaptadores de fusión o puntos de personalización del Espíritu)
  • haciendo coincidir los tipos de argumento para mi acción semántica para que
    • el comstackdor podrá comstackr la invocación de la función
    • la invocación no invocará conversiones implícitas innecesarias en el proceso

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?

Código de ejemplo:

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:

Functor 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; } }; 

Uso de la muestra: detectar tipo de atributo sintetizado

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.

Uso de muestra: detección de tipos pasados ​​a acciones semánticas

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 

Aplicado al OP

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.

Listado de código completo

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; }