2011-05-27 9 views
9

Estoy usando QI y Phoenix, y quiero escribir una pequeña gramática que devuelva 4 bools que se usarán como argumentos para una llamada de función dentro de una acción semántica.Boost :: Spirit :: Qi. ¿Cómo convertir expresiones de analizador inline en gramáticas independientes y cómo descomprimir las tuplas generadas por ellas?

que tienen varias funciones que necesitan esas cosas, y hasta ahora han utilizado este enfoque:

(qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool) 
[px::bind(&Bool4Function, spirit::_val, spirit::_1, spirit::_2, spirit::_3, spirit::_4)] 

y aunque está bien por sí mismo, utilizando por todo el lugar es simplemente feo y confuso, incluso con 'usar' las partes del espacio de nombres.

Es por eso que quería extraer esta expresión en una gramática independiente.

Así que probamos este (el crédito va a Ildjarn para el banco de pruebas):

///// grammar implementation ///// 
#include <boost/fusion/include/vector10.hpp> 
#include <boost/spirit/include/qi_bool.hpp> 
#include <boost/spirit/include/qi_char_.hpp> 
#include <boost/spirit/include/qi_grammar.hpp> 
#include <boost/spirit/include/qi_operator.hpp> 
#include <boost/spirit/include/qi_rule.hpp> 
#include <boost/spirit/include/qi_string.hpp> 

struct FourBools : boost::spirit::qi::grammar< 
    char const*, 
    boost::fusion::vector4<bool, bool, bool, bool>() 
> 
{ 
    typedef boost::fusion::vector4<bool, bool, bool, bool> attribute_type; 

    FourBools() : base_type(start_) 
    { 
     using boost::spirit::bool_; 

     start_ 
      = "4bools:" 
      >> bool_ >> ',' 
      >> bool_ >> ',' 
      >> bool_ >> ',' 
      >> bool_ >> ';' 
      ; 
    } 

private: 
    boost::spirit::qi::rule< 
     base_type::iterator_type, 
     base_type::sig_type 
    > start_; 
}; 
FourBools const fourBools; 


///// demonstration of use ///// 
#include <string> 
#include <ios> 
#include <iostream> 
#include <boost/fusion/include/at_c.hpp> 
#include <boost/spirit/include/phoenix_bind.hpp> 
#include <boost/spirit/include/phoenix_core.hpp> 
#include <boost/spirit/include/qi_action.hpp> 
#include <boost/spirit/include/qi_parse.hpp> 



void noDice(bool a, bool b, bool c, bool d) 
{ 

} 

void worksFine(boost::fusion::vector4<bool, bool, bool, bool> a) 
{ 

} 
int main() 
{ 
    namespace phx = boost::phoenix; 
    namespace spirit = boost::spirit; 

    std::string const input("4bools:true,true,true,false;"); 


    char const* first = input.c_str(); 
    char const* const last = first + input.size(); 
    bool const success = spirit::qi::parse(
     first, last, 
     fourBools[phx::bind(&noDice, spirit::_1)] 
    ); 


    if (!success) 
     std::cout << "parse() failed\n"; 
    else if (first != last) 
     std::cout << "didn't consume all input\n"; 
    std::cout.flush(); 
} 

Eso no se compila a menos fourBools[phx::bind(&noDice, spirit::_1)] se sustituye con fourBools[phx::bind(&worksFine, spirit::_1)].

Eso significa que mi problema es desempacar los argumentos para que coincidan con la firma de la función que se llamará, ya que el número de argumentos difiere en el nivel de firma (una tupla de cuatro bools, contra cuatro bools).

¿Es posible desempaquetar usando marcadores de posición de fénix directamente, en lugar de escribir envoltorios que traducen tuplas en argumentos individuales para mis funciones existentes que los necesitan por separado? Si lo es, ¿cuál sería la sintaxis para eso? Después de todo, una versión en línea como (qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool) funciona bien cuando está 'desempaquetada' por marcadores de posición spirit::_1 - spirit::_4,.

Eso me da la impresión de que esta versión también devuelve una tupla y, de alguna manera, es incompatible con el enfoque anterior, a diferencia de una gramática que devuelve una.

¿Cómo manejo esto?

Respuesta

12

Es casi imposible diagnosticar su problema si no pones un repro completa, coherente; podría ser un error de sintaxis, podría ser #include que falta, ¿quién sabe ...?

Aquí hay una demostración de trabajo; es de esperar que se puede utilizar como referencia para averiguar lo que está mal con su código:

///// grammar implementation ///// 
#include <boost/fusion/include/vector10.hpp> 
#include <boost/spirit/include/qi_bool.hpp> 
#include <boost/spirit/include/qi_char_.hpp> 
#include <boost/spirit/include/qi_grammar.hpp> 
#include <boost/spirit/include/qi_operator.hpp> 
#include <boost/spirit/include/qi_rule.hpp> 
#include <boost/spirit/include/qi_string.hpp> 

struct FourBools : boost::spirit::qi::grammar< 
    char const*, 
    boost::fusion::vector4<bool, bool, bool, bool>() 
> 
{ 
    typedef boost::fusion::vector4<bool, bool, bool, bool> attribute_type; 

    FourBools() : base_type(start_) 
    { 
     using boost::spirit::bool_; 

     start_ 
      = "4bools:" 
       >> bool_ >> ',' 
       >> bool_ >> ',' 
       >> bool_ >> ',' 
       >> bool_ >> ';' 
      ; 
    } 

private: 
    boost::spirit::qi::rule< 
     base_type::iterator_type, 
     base_type::sig_type 
    > start_; 
}; 
FourBools const fourBools; 


///// demonstration of use ///// 
#include <string> 
#include <ios> 
#include <iostream> 
#include <boost/fusion/include/at_c.hpp> 
#include <boost/spirit/include/phoenix_bind.hpp> 
#include <boost/spirit/include/phoenix_core.hpp> 
#include <boost/spirit/include/qi_action.hpp> 
#include <boost/spirit/include/qi_parse.hpp> 

typedef FourBools::attribute_type attr_t; 

struct verify_same 
{ 
    explicit verify_same(attr_t const& expected) : expected_(expected) { } 

    void verify(attr_t const& actual) const 
    { 
     using boost::fusion::at_c; 

     std::cout << std::boolalpha 
      << "same as expected: " << (actual == expected_) 
      << "\nactual values: " 
      << at_c<0>(actual) << ' ' 
      << at_c<1>(actual) << ' ' 
      << at_c<2>(actual) << ' ' 
      << at_c<3>(actual) << '\n'; 
    } 

private: 
    attr_t expected_; 
}; 

int main() 
{ 
    namespace phx = boost::phoenix; 
    namespace spirit = boost::spirit; 

    std::string const input("4bools:true,true,true,false;"); 
    verify_same const vs(attr_t(true, true, true, false)); 

    char const* first = input.c_str(); 
    char const* const last = first + input.size(); 
    bool const success = spirit::qi::parse(
     first, last, 
     fourBools[phx::bind(&verify_same::verify, phx::cref(vs), spirit::_1)] 
    ); 
    if (!success) 
     std::cout << "parse() failed\n"; 
    else if (first != last) 
     std::cout << "didn't consume all input\n"; 
    std::cout.flush(); 
} 

Como acotación al margen, creo que el uso de una tupla con los tipos puramente homogéneos es extraño; personalmente, cambiaría el atributo sintetizado de la gramática al boost::array<bool, 4>.


EDITAR (en respuesta a la edición de OP): Hay buenas y malas noticias y buenas noticias.

Aquí está la buena noticia: Boost.Fusion tiene la funcionalidad para hacer exactamente lo que quiere hacer con un código mínimo: boost::fusion::fused<>.Esto tomará un tipo invocable (incluidos punteros de función libre y punteros de función de miembro) que toma múltiples argumentos y ajusta ese tipo invocable en un funtor que toma una secuencia Fusion; cuando se invoca este funtor, toma la secuencia Fusion y la descomprime, reenviando los elementos individuales de la tupla al tipo invocable envuelto como argumentos separados.

Por lo tanto, dada la gramática ya publicado y los siguientes:

#include <string> 
#include <ios> 
#include <iostream> 
#include <boost/fusion/include/at_c.hpp> 
#include <boost/fusion/include/make_fused.hpp> 
#include <boost/spirit/include/phoenix_bind.hpp> 
#include <boost/spirit/include/phoenix_core.hpp> 
#include <boost/spirit/include/phoenix_fusion.hpp> 
#include <boost/spirit/include/qi_action.hpp> 
#include <boost/spirit/include/qi_parse.hpp> 

typedef FourBools::attribute_type attr_t; 

void free_func_taking_tuple(attr_t const& tup) 
{ 
    using boost::fusion::at_c; 

    std::cout << std::boolalpha 
     << "inside free_func_taking_tuple() :: " 
     << at_c<0>(tup) << ' ' 
     << at_c<1>(tup) << ' ' 
     << at_c<2>(tup) << ' ' 
     << at_c<3>(tup) << '\n'; 
} 

void free_func_taking_bools(
    bool const a, bool const b, 
    bool const c, bool const d 
) 
{ 
    std::cout << std::boolalpha 
     << "inside free_func_taking_bools() :: " 
     << a << ' ' 
     << b << ' ' 
     << c << ' ' 
     << d << '\n'; 
} 

boost::spirit::qi::parse() se puede llamar así:

namespace phx = boost::phoenix; 
namespace spirit = boost::spirit; 
using boost::fusion::make_fused; 

// calls free_func_taking_tuple, nothing new here 
spirit::qi::parse(
    first, last, 
    fourBools[phx::bind(free_func_taking_tuple, spirit::_1)] 
); 

// calls free_func_taking_bools, using boost::fusion::fused<> to unpack the tuple 
// into separate arguments 
spirit::qi::parse(
    first, last, 
    fourBools[phx::bind(make_fused(&free_func_taking_bools), spirit::_1)] 
); 

Aquí es la mala noticia: los envoltorios del tipo que se puede llamar de Boost.Fusion se basan en el protocolo TR1/C++ 11 result_of, mientras que Boost.Phoenix v2 implementa el protocolo Boost.Lambda result_of – estos no son compatibles. Como resultado, debe desempaquetar los elementos de tupla usted mismo:

namespace phx = boost::phoenix; 
namespace spirit = boost::spirit; 

spirit::qi::parse(
    first, last, 
    fourBools[phx::bind(
     free_func_taking_bools, 
     phx::at_c<0>(spirit::_1), 
     phx::at_c<1>(spirit::_1), 
     phx::at_c<2>(spirit::_1), 
     phx::at_c<3>(spirit::_1) 
    )] 
); 

¡Yuck! Pero, hay más buenas noticias: Boost.Phoenix v3 se lanzará en Boost 1.47 e implementa el protocolo TR1/C++ 11 result_of. En consecuencia, comenzando con Boost 1.47 podrás usar boost::fusion::fused<> y ahorrarte un tedioso texto estándar.

+0

Comentarios como este probablemente estén mal visto, pero tengo que estar contento de ver este tipo de código fuera de mi pecho. Me siento muy sucio al lado, es muy motivador, ni siquiera sabía la mitad de las cosas enumeradas y pensé que ya estaba mejorando. De todos modos, incluiré un repro mañana, si todavía no funciona después de analizar a fondo su publicación. – Erius

+0

@Erius: -] Si terminas publicando una reprografía, publica un comentario sobre esta respuesta, por lo que recibiré un mensaje en mi bandeja de entrada de SO, de lo contrario, probablemente no veré tu edición. – ildjarn

+0

Hecho, gracias de nuevo. – Erius

0

El atributo qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool es std::vector<bool> o cualquier otro contenedor stl, como se describe en la referencia: http://www.boost.org/doc/libs/1_46_0/libs/spirit/doc/html/spirit/qi/quick_reference/compound_attribute_rules.html.

La primera fila de la tabla es el caso :)

+0

Probé que justo ahora, tampoco compilaba, a menos que se suponga que no debo usar qi :: _ 1 - qi :: _ 4 para extraer los valores del vector. – Erius

+0

El atributo de 'bool_ >> bool_ >> bool_ >> bool_' es de hecho' fusion :: vector '; En realidad, Spirit permite el uso de 'something_container ' porque la tupla está compuesta únicamente por tipos homogéneos. Este comportamiento se denomina 'colapso de atributos' en los documentos de Spirit. – ildjarn

3

Como nota general, sugiero leer los artículos sobre el manejo de atributos en el sitio web de Spirit here. Constituyen una buena adición a los documentos en línea distribuidos con la biblioteca.

+0

marcado como favorito. También me aseguraré de trabajar en eso, gracias. – Erius

Cuestiones relacionadas